Initial conflict avoidance.
This commit is contained in:
parent
9751764611
commit
993fb0730c
4 changed files with 480 additions and 185 deletions
335
src/belt_finding/conflict_avoidance.rs
Normal file
335
src/belt_finding/conflict_avoidance.rs
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map};
|
||||
|
||||
use super::{
|
||||
common::{Dimension, Direction, PathField, Position},
|
||||
Problem, COLORS,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Field {
|
||||
blocked: bool,
|
||||
}
|
||||
|
||||
pub struct ConflictAvoidance {
|
||||
map: Map<Field>,
|
||||
belts: Vec<Vec<PathField>>,
|
||||
}
|
||||
|
||||
impl ConflictAvoidance {
|
||||
pub fn new(problem: Problem) -> Self {
|
||||
let mut map: Map<Field> = Map::new(problem.map.width, problem.map.height);
|
||||
for x in 0..problem.map.width {
|
||||
for y in 0..problem.map.height {
|
||||
map.get_mut(x, y).blocked = problem.map.get(x, y).blocked;
|
||||
}
|
||||
}
|
||||
let mut belts = Vec::new();
|
||||
for i in 0..problem.path.len() {
|
||||
let mut p = Vec::new();
|
||||
|
||||
for j in 0..problem.path[i].len() - 1 {
|
||||
p.push(PathField::Belt {
|
||||
pos: problem.path[i][j],
|
||||
dir: Direction::from_neighbors(&problem.path[i][j], &problem.path[i][j + 1]),
|
||||
});
|
||||
}
|
||||
|
||||
p.push(PathField::Belt {
|
||||
pos: *problem.path[i].last().unwrap(),
|
||||
dir: *p.last().unwrap().dir(),
|
||||
});
|
||||
|
||||
belts.push(p);
|
||||
}
|
||||
|
||||
Self { map, belts }
|
||||
}
|
||||
|
||||
pub fn remove_conflict(&mut self) -> bool {
|
||||
let mut conflicts: Map<usize> = Map::new(self.map.width, self.map.height);
|
||||
|
||||
for x in 0..self.map.width {
|
||||
for y in 0..self.map.height {
|
||||
if self.map.get(x, y).blocked {
|
||||
*conflicts.get_mut(x, y) += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for path in &self.belts {
|
||||
for p in path {
|
||||
match p {
|
||||
PathField::Belt { pos, dir: _ } => *conflicts.get_mut(pos.x, pos.y) += 1,
|
||||
PathField::Underground { pos, dir, len } => {
|
||||
*conflicts.get_mut(pos.x, pos.y) += 1;
|
||||
let end = pos
|
||||
.in_direction(
|
||||
dir,
|
||||
*len as usize,
|
||||
&Dimension {
|
||||
width: self.map.width,
|
||||
height: self.map.height,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
*conflicts.get_mut(end.x, end.y) += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for y in 0..self.map.height {
|
||||
for x in 0..self.map.width {
|
||||
if *conflicts.get(x, y) > 1 {
|
||||
print!("#");
|
||||
} else {
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
let mut c = None;
|
||||
for y in 0..self.map.height {
|
||||
for x in 0..self.map.width {
|
||||
if *conflicts.get(x, y) > 1 {
|
||||
c = Some((x, y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (cx, cy) = match c {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let mut xoffset = 0;
|
||||
let mut yoffset = 0;
|
||||
|
||||
loop {
|
||||
xoffset += 1;
|
||||
yoffset += 1;
|
||||
|
||||
let xrange = cx..=cx;
|
||||
let yrange = cy..=cy;
|
||||
|
||||
let xrange = xrange.start().saturating_sub(xoffset)
|
||||
..=usize::min(xrange.end() + xoffset, self.map.width - 1);
|
||||
let yrange = yrange.start().saturating_sub(yoffset)
|
||||
..=usize::min(yrange.end() + yoffset, self.map.height - 1);
|
||||
|
||||
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();
|
||||
|
||||
for (i, path) in self.belts.iter().enumerate() {
|
||||
let s = path
|
||||
.iter()
|
||||
.map(|p| p.pos())
|
||||
.position(|p| xrange.contains(&p.x) && yrange.contains(&p.y))
|
||||
.map(|i| i.saturating_sub(1));
|
||||
let e = path
|
||||
.iter()
|
||||
.map(|p| p.pos())
|
||||
.rev()
|
||||
.position(|p| xrange.contains(&p.x) && yrange.contains(&p.y))
|
||||
.map(|i| usize::min(path.len() - 1, path.len() - i));
|
||||
|
||||
if let Some((start, end)) = s.zip(e) {
|
||||
// dbg!(start, end);
|
||||
mapping.push((i, start, end));
|
||||
|
||||
// dbg!(path[start], path[end]);
|
||||
|
||||
let (start_pos, start_dir) = path[start]
|
||||
.end_pos(&Dimension {
|
||||
width: self.map.width,
|
||||
height: self.map.height,
|
||||
})
|
||||
.unwrap();
|
||||
let end_pos = path[end].pos();
|
||||
let end_dir = path[end].dir();
|
||||
|
||||
// dbg!(start_pos, end_pos);
|
||||
|
||||
b.add_path(
|
||||
(
|
||||
Position::new(
|
||||
start_pos.x - (xrange.start() - 1),
|
||||
start_pos.y - (yrange.start() - 1),
|
||||
),
|
||||
start_dir,
|
||||
),
|
||||
(
|
||||
Position::new(
|
||||
end_pos.x - (xrange.start() - 1),
|
||||
end_pos.y - (yrange.start() - 1),
|
||||
),
|
||||
*end_dir,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!("{:?}", mapping);
|
||||
|
||||
let mut b = b.build();
|
||||
|
||||
println!("{}", b);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
println!("{}", b.solution_count());
|
||||
|
||||
if b.solution_count() > 0 {
|
||||
for (p, (index, start, end)) in solutions.iter().zip(mapping) {
|
||||
let mut t = Vec::new();
|
||||
|
||||
t.extend_from_slice(&self.belts[index][0..=start]);
|
||||
t.extend(p[1..].iter().map(|p| {
|
||||
p.offset(((*xrange.start() as i32) - 1, (*yrange.start() as i32) - 1))
|
||||
}));
|
||||
t.extend_from_slice(&self.belts[index][end..]);
|
||||
// println!("{:?}", &t);
|
||||
// println!("{:?}", &self.belts[index]);
|
||||
|
||||
self.belts[index] = t;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_all_conflicts(&mut self) {
|
||||
while self.remove_conflict() {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ConflictAvoidance {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let width_digits = self.map.width.ilog10() + 1;
|
||||
let height_digits = self.map.height.ilog10() + 1;
|
||||
|
||||
// print header
|
||||
for i in 0..width_digits {
|
||||
let d = width_digits - i - 1;
|
||||
// padding
|
||||
for _ in 0..height_digits {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
|
||||
for x in 0..self.map.width {
|
||||
let digits = x / (usize::pow(10, d));
|
||||
if digits == 0 && d > 0 {
|
||||
write!(f, " ")?;
|
||||
} else {
|
||||
write!(f, "{}", char::from_u32((digits % 10) as u32 + 48).unwrap())?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
let mut m: Map<Option<(usize, &str)>> = Map::new(self.map.width, self.map.height);
|
||||
|
||||
for (i, problem) in self.belts.iter().enumerate() {
|
||||
for p in problem {
|
||||
match p {
|
||||
PathField::Belt { pos, dir } => match dir {
|
||||
Direction::Up => m.set(pos.x, pos.y, Some((i, "↑"))),
|
||||
Direction::Right => m.set(pos.x, pos.y, Some((i, "→"))),
|
||||
Direction::Down => m.set(pos.x, pos.y, Some((i, "↓"))),
|
||||
Direction::Left => m.set(pos.x, pos.y, Some((i, "←"))),
|
||||
},
|
||||
PathField::Underground { pos, dir, len } => {
|
||||
match dir {
|
||||
Direction::Up => m.set(pos.x, pos.y, Some((i, "↟"))),
|
||||
Direction::Right => m.set(pos.x, pos.y, Some((i, "↠"))),
|
||||
Direction::Down => m.set(pos.x, pos.y, Some((i, "↡"))),
|
||||
Direction::Left => m.set(pos.x, pos.y, Some((i, "↞"))),
|
||||
};
|
||||
let end_pos = pos
|
||||
.in_direction(
|
||||
dir,
|
||||
*len as usize,
|
||||
&Dimension {
|
||||
width: self.map.width,
|
||||
height: self.map.height,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
match dir {
|
||||
Direction::Up => m.set(end_pos.x, end_pos.y, Some((i, "↥"))),
|
||||
Direction::Right => m.set(end_pos.x, end_pos.y, Some((i, "↦"))),
|
||||
Direction::Down => m.set(end_pos.x, end_pos.y, Some((i, "↧"))),
|
||||
Direction::Left => m.set(end_pos.x, end_pos.y, Some((i, "↤"))),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
let last_pos = problem.last().unwrap().pos();
|
||||
m.set(last_pos.x, last_pos.y, Some((i, "T")));
|
||||
}
|
||||
|
||||
// Print body
|
||||
|
||||
for y in 0..self.map.height {
|
||||
write!(f, "{:1$}", y, height_digits as usize)?;
|
||||
|
||||
for x in 0..self.map.width {
|
||||
if let Some((i, c)) = m.get(x, y) {
|
||||
write!(f, "{}", c.color(COLORS[*i]))?;
|
||||
} else if self.map.get(x, y).blocked {
|
||||
write!(f, "#")?;
|
||||
} else if x % 8 == 0 || y % 8 == 0 {
|
||||
write!(f, "∙")?;
|
||||
} else {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue