From 8c29cb1e53b380e94402aa74fbea50e8363ba27b Mon Sep 17 00:00:00 2001 From: hal8174 Date: Sat, 23 Mar 2024 12:29:18 +0100 Subject: [PATCH] Fix edgecases resulting in conflict loops. --- src/belt_finding/brute_force.rs | 4 + src/belt_finding/conflict_avoidance.rs | 312 ++++++++++++++----------- src/belt_finding/mod.rs | 2 +- 3 files changed, 180 insertions(+), 138 deletions(-) diff --git a/src/belt_finding/brute_force.rs b/src/belt_finding/brute_force.rs index 7cbfc0a..91db44b 100644 --- a/src/belt_finding/brute_force.rs +++ b/src/belt_finding/brute_force.rs @@ -36,6 +36,10 @@ impl BruteforceBuilder { self.map.get_mut(x, y).blocked = blocked; } + pub fn get_blocked(&self, x: usize, y: usize) -> bool { + self.map.get(x, y).blocked + } + pub fn set_underground(&mut self, x: usize, y: usize, dir: Option) { match dir { Some(d) => { diff --git a/src/belt_finding/conflict_avoidance.rs b/src/belt_finding/conflict_avoidance.rs index ae6a169..3e6f796 100644 --- a/src/belt_finding/conflict_avoidance.rs +++ b/src/belt_finding/conflict_avoidance.rs @@ -23,6 +23,13 @@ struct Candidate { max: Position, } +struct BruteForceEntry { + path_id: usize, + start: usize, + end: usize, + path: Vec, +} + impl Candidate { fn new(min: Position, max: Position) -> Self { Self { min, max } @@ -120,6 +127,158 @@ impl ConflictAvoidance { &self.belts } + fn try_bruteforce(&self, candidate: &Candidate) -> Option> { + let xrange = candidate.min.x as usize..=candidate.max.x as usize; + let yrange = candidate.min.y as usize..=candidate.max.y as usize; + + // println!("x: {:?}, y: {:?}", xrange, yrange); + + let xsize = xrange.end() - xrange.start() + 1; + let ysize = yrange.end() - yrange.start() + 1; + + // dbg!(xsize, ysize); + + let mut b = BruteforceBuilder::new(xsize + 2, ysize + 2); + b.set_blocked_range(0, 0, xsize + 1, 0, true); + b.set_blocked_range(0, ysize + 1, xsize + 1, ysize + 1, true); + b.set_blocked_range(0, 0, 0, ysize + 1, true); + b.set_blocked_range(xsize + 1, 0, xsize + 1, ysize + 1, true); + + for x in 0..xsize { + for y in 0..ysize { + b.set_blocked( + x + 1, + y + 1, + self.map.get(xrange.start() + x, yrange.start() + y).blocked, + ); + } + } + + let mut mapping = Vec::new(); + let offset = Position::new(*xrange.start() as i32 - 1, *yrange.start() as i32 - 1); + + dbg!(&xrange, &yrange); + + for (i, path) in self.belts.iter().enumerate() { + // index of first PathField where the next position is in the area + let start_index = path + .iter() + .map(|p| { + let (pos, dir) = p.end_pos(); + pos.in_direction(&dir, 1) + }) + .position(|pos| { + xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize)) + }); + + // index of last PathField that ends inside the area + let end_index = path + .iter() + .rev() + .map(|p| p.end_pos().0) + .position(|pos| { + xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize)) + }) + .map(|rev_i| path.len() - rev_i - 1); + + dbg!(start_index, end_index); + + if let Some((start_index, end_index)) = Option::zip(start_index, end_index) { + // dbg!(start_index, end_index, path[start_index], path[end_index]); + + let (start_pos, start_dir) = path[start_index].end_pos(); + + let (end_pos, end_dir) = path[end_index].end_pos(); + + // edge cases with underground into and end + + if let Some(PathField::Underground { pos, dir, len }) = path.get(end_index + 1) { + if xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize)) { + let p = *pos - offset; + println!("Blocked {:?}", p); + if b.get_blocked(p.x as usize, p.y as usize) { + return None; + } + b.set_blocked(p.x as usize, p.y as usize, true); + } + + for l in 1..*len { + let p = pos.in_direction(dir, l as i32); + + if xrange.contains(&(p.x as usize)) && yrange.contains(&(p.y as usize)) { + let p = p - offset; + b.set_underground(p.x as usize, p.y as usize, Some(*dir)); + } + } + } + + let start_pos_offset = start_pos - offset; + b.set_blocked( + start_pos_offset.x as usize, + start_pos_offset.y as usize, + true, + ); + + if let PathField::Underground { pos, dir, len } = path[start_index] { + for l in 1..len { + let p = pos.in_direction(&dir, l as i32); + + if xrange.contains(&(p.x as usize)) && yrange.contains(&(p.y as usize)) { + let p = p - offset; + b.set_underground(p.x as usize, p.y as usize, Some(dir)); + } + } + } + + b.add_path((start_pos_offset, start_dir), (end_pos - offset, end_dir)); + + mapping.push((i, start_index, end_index)); + } else if let Some(end_index) = end_index { + if let Some(PathField::Underground { pos, dir, len }) = path.get(end_index) { + let p = pos.in_direction(dir, *len as i32); + let p = p - offset; + b.set_blocked(p.x as usize, p.y as usize, true); + } + } + } + + // println!("{:?}", mapping); + + let mut b = b.build(); + + b.print(); + self.print(); + + let mut min_cost = f64::INFINITY; + let mut solutions = Vec::new(); + + while b.next_finish_state() { + // println!("{}", b); + let c = b.cost(); + if c < min_cost { + min_cost = c; + solutions = b.get_paths(); + } + } + + if b.solution_count() == 0 { + return None; + } + + Some( + mapping + .into_iter() + .zip(solutions.into_iter()) + .map(|((path_id, start, end), path)| BruteForceEntry { + path_id, + start, + end, + path, + }) + .collect(), + ) + } + pub fn remove_conflict(&mut self) -> bool { let mut conflicts: Map = Map::new(self.map.width, self.map.height); @@ -181,159 +340,38 @@ impl ConflictAvoidance { loop { candidates.sort_by_key(|c| -c.area()); let c = candidates.pop().unwrap(); - let xrange = c.min.x as usize..=c.max.x as usize; - let yrange = c.min.y as usize..=c.max.y as usize; self.range = Some((c.min.x..=c.max.x, c.min.y..=c.max.y)); - // println!("x: {:?}, y: {:?}", xrange, yrange); - - let xsize = xrange.end() - xrange.start() + 1; - let ysize = yrange.end() - yrange.start() + 1; - - // dbg!(xsize, ysize); - - let mut b = BruteforceBuilder::new(xsize + 2, ysize + 2); - b.set_blocked_range(0, 0, xsize + 1, 0, true); - b.set_blocked_range(0, ysize + 1, xsize + 1, ysize + 1, true); - b.set_blocked_range(0, 0, 0, ysize + 1, true); - b.set_blocked_range(xsize + 1, 0, xsize + 1, ysize + 1, true); - - for x in 0..xsize { - for y in 0..ysize { - b.set_blocked( - x + 1, - y + 1, - self.map.get(xrange.start() + x, yrange.start() + y).blocked, - ); - } - } - - let mut mapping = Vec::new(); - let offset = Position::new(*xrange.start() as i32 - 1, *yrange.start() as i32 - 1); - - dbg!(&xrange, &yrange); - - for (i, path) in self.belts.iter().enumerate() { - // index of first PathField where the next position is in the area - let start_index = path - .iter() - .map(|p| { - let (pos, dir) = p.end_pos(); - pos.in_direction(&dir, 1) - }) - .position(|pos| { - xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize)) - }); - - // index of last PathField that ends inside the area - let end_index = path - .iter() - .rev() - .map(|p| p.end_pos().0) - .position(|pos| { - xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize)) - }) - .map(|rev_i| path.len() - rev_i - 1); - - if let Some((start_index, end_index)) = Option::zip(start_index, end_index) { - // dbg!(start_index, end_index, path[start_index], path[end_index]); - - let (start_pos, start_dir) = path[start_index].end_pos(); - - let (end_pos, end_dir) = path[end_index].end_pos(); - - // edge cases with underground into and end - - if let Some(PathField::Underground { pos, dir, len }) = path.get(end_index + 1) - { - if xrange.contains(&(pos.x as usize)) && yrange.contains(&(pos.y as usize)) - { - let p = *pos - offset; - println!("Blocked {:?}", p); - b.set_blocked(p.x as usize, p.y as usize, true); - } - - for l in 1..*len { - let p = pos.in_direction(dir, l as i32); - - if xrange.contains(&(p.x as usize)) && yrange.contains(&(p.y as usize)) - { - let p = p - offset; - b.set_underground(p.x as usize, p.y as usize, Some(*dir)); - } - } - } - - let start_pos_offset = start_pos - offset; - b.set_blocked( - start_pos_offset.x as usize, - start_pos_offset.y as usize, - true, - ); - - if let PathField::Underground { pos, dir, len } = path[start_index] { - for l in 1..len { - let p = pos.in_direction(&dir, l as i32); - - if xrange.contains(&(p.x as usize)) && yrange.contains(&(p.y as usize)) - { - let p = p - offset; - b.set_underground(p.x as usize, p.y as usize, Some(dir)); - } - } - } - - b.add_path((start_pos_offset, start_dir), (end_pos - offset, end_dir)); - - mapping.push((i, start_index, end_index)); - } - } - - // println!("{:?}", mapping); - - let mut b = b.build(); - - b.print(); - self.print(); - - let mut min_cost = f64::INFINITY; - let mut solutions = Vec::new(); - - while b.next_finish_state() { - // println!("{}", b); - let c = b.cost(); - if c < min_cost { - min_cost = c; - solutions = b.get_paths(); - } - } + let result = self.try_bruteforce(&c); // dbg!(&solutions); // println!("{}", b.solution_count()); - if b.solution_count() > 0 { - for (p, (index, start, end)) in solutions.iter().zip(mapping) { + let xrange = c.min.x as usize..=c.max.x as usize; + let yrange = c.min.y as usize..=c.max.y as usize; + let offset = Position::new(*xrange.start() as i32 - 1, *yrange.start() as i32 - 1); + if let Some(r) = result { + for BruteForceEntry { + path_id, + start, + end, + path, + } in r + { let mut t = Vec::new(); // println!("{:?}", p); - t.extend_from_slice(&self.belts[index][0..=start]); - t.extend(p[1..].iter().map(|p| p.offset(&offset))); - t.extend_from_slice(&self.belts[index][end + 1..]); + t.extend_from_slice(&self.belts[path_id][0..=start]); + t.extend(path[1..].iter().map(|p| p.offset(&offset))); + t.extend_from_slice(&self.belts[path_id][end + 1..]); // println!("{:?}", &t); // println!(); // println!("{:?}", &self.belts[index]); - self.belts[index] = t; + self.belts[path_id] = t; } - - for (i, path) in self.belts.iter().enumerate() { - if path.iter().any(|p| (p.pos().x == 18) && p.pos().y == 18) { - dbg!(i); - } - } - return true; } let mut candidate = Candidate::new( diff --git a/src/belt_finding/mod.rs b/src/belt_finding/mod.rs index 7965997..5bc0df6 100644 --- a/src/belt_finding/mod.rs +++ b/src/belt_finding/mod.rs @@ -70,7 +70,7 @@ impl Problem { for (i, path) in self.path.iter().enumerate() { if i != without { for p in path { - let weight = 0.5; + let weight = 1.0; let x = p.0.x as usize; let y = p.0.y as usize;