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() {
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);
p.set_blocked(2, 4, true);
p.set_blocked(1, 5, true);
p.set_blocked(2, 5, true);
p.set_blocked(1, 7, true);
p.set_blocked(2, 7, true);
p.set_blocked(1, 8, true);
p.set_blocked(2, 8, true);
p.set_blocked(0, 9, true);
p.set_blocked(16, 3, true);
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);
match args.mode {
Mode::Solve => {
println!("{}", p);
p.find_path();
println!("{}", p);
}
Mode::ConflictAvoidance => {
println!("{}", p);
p.find_path();
println!("{}", p);
let mut c = ConflictAvoidance::new(p);
println!("{}", c);
while c.remove_conflict() {
println!("{}", c)
}
}
}
}

View file

@ -7,7 +7,7 @@ pub enum 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 y_diff = pos.y as i64 - neighbor.y as i64;
@ -143,4 +143,24 @@ impl PathField {
.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 common;
pub mod conflict_avoidance;
#[derive(Default, Clone, Copy)]
pub struct Field {
@ -216,137 +217,78 @@ impl Problem {
self.path[i] = p;
}
}
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.path {
for pos in path {
*conflicts.get_mut(pos.x, pos.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!();
}
println!("{self}");
loop {
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;
}
}
}
dbg!(c);
let xoffset = 2;
let yoffset = 2;
if let Some((cx, cy)) = c {
*conflicts.get_mut(cx, cy) = 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);
// dbg!(&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,
);
}
}
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;
}
}
}
}
pub mod problems {
use super::{common::Position, Problem};
pub fn belt_madness_level_1() -> Problem {
let mut p = Problem::new(17, 13);
p.set_blocked(0, 3, true);
p.set_blocked(1, 4, true);
p.set_blocked(2, 4, true);
p.set_blocked(1, 5, true);
p.set_blocked(2, 5, true);
p.set_blocked(1, 7, true);
p.set_blocked(2, 7, true);
p.set_blocked(1, 8, true);
p.set_blocked(2, 8, true);
p.set_blocked(0, 9, true);
p.set_blocked(16, 3, true);
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
}
pub fn belt_madness_level_2() -> Problem {
let mut p = Problem::new(33, 13);
p.set_blocked_range(1, 3, 2, 5, true);
p.set_blocked_range(1, 7, 2, 9, true);
p.set_blocked(0, 3, true);
p.set_blocked(0, 8, true);
p.set_blocked_range(10, 0, 21, 2, true);
p.set_blocked_range(10, 5, 21, 7, true);
p.set_blocked_range(10, 10, 21, 12, true);
p.set_blocked_range(30, 3, 31, 5, true);
p.set_blocked_range(30, 7, 31, 9, true);
p.set_blocked(32, 3, true);
p.set_blocked(32, 9, true);
p.add_connection(Position::new(3, 3), Position::new(29, 7));
p.add_connection(Position::new(3, 4), Position::new(29, 9));
p.add_connection(Position::new(3, 5), Position::new(29, 8));
p.add_connection(Position::new(3, 7), Position::new(29, 3));
p.add_connection(Position::new(3, 8), Position::new(29, 5));
p.add_connection(Position::new(3, 9), Position::new(29, 4));
p
}
}