Fix edgecases resulting in conflict loops.
This commit is contained in:
parent
a145c4b70c
commit
8c29cb1e53
3 changed files with 180 additions and 138 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue