Initial conflict avoidance.

This commit is contained in:
hal8174 2024-01-15 19:54:48 +01:00
parent 9751764611
commit 993fb0730c
4 changed files with 480 additions and 185 deletions

View file

@ -1,57 +1,55 @@
use factorio_blueprint::belt_finding::{common::Position, Problem}; use clap::{Parser, ValueEnum};
use factorio_blueprint::belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem};
#[derive(ValueEnum, Clone)]
enum Mode {
Solve,
ConflictAvoidance,
}
#[derive(ValueEnum, Clone)]
enum ProblemCase {
Level1,
Level2,
}
impl ProblemCase {
fn get_problem(&self) -> Problem {
match self {
ProblemCase::Level1 => problems::belt_madness_level_1(),
ProblemCase::Level2 => problems::belt_madness_level_2(),
}
}
}
#[derive(Parser)]
struct Args {
#[arg(value_enum, default_value = "level1")]
problem: ProblemCase,
#[arg(value_enum, default_value = "solve")]
mode: Mode,
}
fn main() { fn main() {
let mut p = Problem::new(17, 13); let args = Args::parse();
p.set_blocked(0, 3, true); let mut p = args.problem.get_problem();
p.set_blocked(1, 4, true); match args.mode {
p.set_blocked(2, 4, true); Mode::Solve => {
p.set_blocked(1, 5, true); println!("{}", p);
p.set_blocked(2, 5, true); p.find_path();
println!("{}", p);
p.set_blocked(1, 7, true); }
p.set_blocked(2, 7, true); Mode::ConflictAvoidance => {
p.set_blocked(1, 8, true); println!("{}", p);
p.set_blocked(2, 8, true); p.find_path();
println!("{}", p);
p.set_blocked(0, 9, true); let mut c = ConflictAvoidance::new(p);
println!("{}", c);
p.set_blocked(16, 3, true); while c.remove_conflict() {
println!("{}", c)
p.set_blocked(14, 4, true); }
p.set_blocked(15, 4, true); }
p.set_blocked(14, 5, true); }
p.set_blocked(15, 5, true);
p.set_blocked(14, 7, true);
p.set_blocked(15, 7, true);
p.set_blocked(14, 8, true);
p.set_blocked(15, 8, true);
p.set_blocked(16, 9, true);
p.add_connection(Position::new(3, 7), Position::new(13, 4));
p.add_connection(Position::new(3, 8), Position::new(13, 5));
p.add_connection(Position::new(3, 4), Position::new(13, 8));
p.add_connection(Position::new(3, 5), Position::new(13, 7));
// p.set_blocked(8, 12, true);
// p.set_blocked(8, 11, true);
// p.set_blocked(8, 10, true);
// p.set_blocked(8, 9, true);
// p.set_blocked(8, 8, true);
// p.set_blocked(8, 7, true);
// p.set_blocked(8, 6, true);
// p.set_blocked(8, 5, true);
// p.set_blocked(8, 4, true);
// p.set_blocked(8, 3, true);
// p.set_blocked(8, 2, true);
// p.set_blocked(8, 1, true);
// p.set_blocked(8, 0, true);
println!("{}", p);
p.find_path();
println!("{}", p);
} }

View file

@ -7,7 +7,7 @@ pub enum Direction {
} }
impl Direction { impl Direction {
pub fn from_neghbors(pos: &Position, neighbor: &Position) -> Self { pub fn from_neighbors(pos: &Position, neighbor: &Position) -> Self {
let x_diff = pos.x as i64 - neighbor.x as i64; let x_diff = pos.x as i64 - neighbor.x as i64;
let y_diff = pos.y as i64 - neighbor.y as i64; let y_diff = pos.y as i64 - neighbor.y as i64;
@ -143,4 +143,24 @@ impl PathField {
.map(|p| (p, *dir)), .map(|p| (p, *dir)),
} }
} }
pub fn offset(&self, offset: (i32, i32)) -> Self {
match self {
PathField::Belt { pos, dir } => PathField::Belt {
pos: Position::new(
(pos.x as i32 + offset.0) as usize,
(pos.y as i32 + offset.1) as usize,
),
dir: *dir,
},
PathField::Underground { pos, dir, len } => PathField::Underground {
pos: Position::new(
(pos.x as i32 + offset.0) as usize,
(pos.y as i32 + offset.1) as usize,
),
dir: *dir,
len: *len,
},
}
}
} }

View 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(())
}
}

View file

@ -10,6 +10,7 @@ use self::common::Position;
pub mod brute_force; pub mod brute_force;
pub mod common; pub mod common;
pub mod conflict_avoidance;
#[derive(Default, Clone, Copy)] #[derive(Default, Clone, Copy)]
pub struct Field { pub struct Field {
@ -216,137 +217,78 @@ impl Problem {
self.path[i] = p; self.path[i] = p;
} }
} }
}
let mut conflicts: Map<usize> = Map::new(self.map.width, self.map.height); }
for x in 0..self.map.width { pub mod problems {
for y in 0..self.map.height { use super::{common::Position, Problem};
if self.map.get(x, y).blocked {
*conflicts.get_mut(x, y) += 1; pub fn belt_madness_level_1() -> Problem {
} let mut p = Problem::new(17, 13);
}
} p.set_blocked(0, 3, true);
for path in &self.path { p.set_blocked(1, 4, true);
for pos in path { p.set_blocked(2, 4, true);
*conflicts.get_mut(pos.x, pos.y) += 1; p.set_blocked(1, 5, true);
} p.set_blocked(2, 5, true);
}
p.set_blocked(1, 7, true);
for y in 0..self.map.height { p.set_blocked(2, 7, true);
for x in 0..self.map.width { p.set_blocked(1, 8, true);
if *conflicts.get(x, y) > 1 { p.set_blocked(2, 8, true);
print!("#");
} else { p.set_blocked(0, 9, true);
print!(" ");
} p.set_blocked(16, 3, true);
}
println!(); p.set_blocked(14, 4, true);
} p.set_blocked(15, 4, true);
p.set_blocked(14, 5, true);
println!("{self}"); p.set_blocked(15, 5, true);
loop { p.set_blocked(14, 7, true);
let mut c = None; p.set_blocked(15, 7, true);
for y in 0..self.map.height { p.set_blocked(14, 8, true);
for x in 0..self.map.width { p.set_blocked(15, 8, true);
if *conflicts.get(x, y) > 1 {
c = Some((x, y)); p.set_blocked(16, 9, true);
break;
} p.add_connection(Position::new(3, 7), Position::new(13, 4));
} p.add_connection(Position::new(3, 8), Position::new(13, 5));
}
p.add_connection(Position::new(3, 4), Position::new(13, 8));
dbg!(c); p.add_connection(Position::new(3, 5), Position::new(13, 7));
let xoffset = 2; p
let yoffset = 2; }
if let Some((cx, cy)) = c { pub fn belt_madness_level_2() -> Problem {
*conflicts.get_mut(cx, cy) = 1; let mut p = Problem::new(33, 13);
let xrange = cx..=cx; p.set_blocked_range(1, 3, 2, 5, true);
let yrange = cy..=cy; p.set_blocked_range(1, 7, 2, 9, true);
let xrange = xrange.start().saturating_sub(xoffset) p.set_blocked(0, 3, true);
..=usize::min(xrange.end() + xoffset, self.map.width - 1); p.set_blocked(0, 8, true);
let yrange = yrange.start().saturating_sub(yoffset)
..=usize::min(yrange.end() + yoffset, self.map.height - 1); p.set_blocked_range(10, 0, 21, 2, true);
// dbg!(&xrange, &yrange); p.set_blocked_range(10, 5, 21, 7, true);
p.set_blocked_range(10, 10, 21, 12, true);
let xsize = xrange.end() - xrange.start() + 1;
let ysize = yrange.end() - yrange.start() + 1; p.set_blocked_range(30, 3, 31, 5, true);
p.set_blocked_range(30, 7, 31, 9, true);
// dbg!(xsize, ysize); p.set_blocked(32, 3, true);
p.set_blocked(32, 9, true);
let mut b = BruteforceBuilder::new(xsize + 2, ysize + 2);
b.set_blocked_range(0, 0, xsize + 1, 0, true); p.add_connection(Position::new(3, 3), Position::new(29, 7));
b.set_blocked_range(0, ysize + 1, xsize + 1, ysize + 1, true); p.add_connection(Position::new(3, 4), Position::new(29, 9));
b.set_blocked_range(0, 0, 0, ysize + 1, true); p.add_connection(Position::new(3, 5), Position::new(29, 8));
b.set_blocked_range(xsize + 1, 0, xsize + 1, ysize + 1, true);
p.add_connection(Position::new(3, 7), Position::new(29, 3));
for x in 0..xsize { p.add_connection(Position::new(3, 8), Position::new(29, 5));
for y in 0..ysize { p.add_connection(Position::new(3, 9), Position::new(29, 4));
b.set_blocked(
x + 1, p
y + 1,
self.map.get(xrange.start() + x, yrange.start() + y).blocked,
);
}
}
for path in &self.path {
let s = path
.iter()
.position(|p| xrange.contains(&p.x) && yrange.contains(&p.y))
.map(|i| i.saturating_sub(1));
let e = path
.iter()
.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);
let start_pos = path[start];
let start_dir = Direction::from_neghbors(&path[start], &path[start + 1]);
let end_pos = path[end];
let end_dir = Direction::from_neghbors(&path[end - 1], &path[end]);
// 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,
),
);
}
}
let mut b = b.build();
println!("{}", b);
while b.next_finish_state() {
println!("{}", b);
}
println!("{}", b.solution_count());
} else {
break;
}
}
} }
} }