Fix edgecases resulting in conflict loops.

This commit is contained in:
hal8174 2024-03-23 12:29:18 +01:00
parent a145c4b70c
commit 8c29cb1e53
3 changed files with 180 additions and 138 deletions

View file

@ -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<Direction>) {
match dir {
Some(d) => {

View file

@ -23,6 +23,13 @@ struct Candidate {
max: Position,
}
struct BruteForceEntry {
path_id: usize,
start: usize,
end: usize,
path: Vec<PathField>,
}
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<Vec<BruteForceEntry>> {
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<usize> = 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(

View file

@ -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;