factorio_blueprint/src/belt_finding/brute_force.rs

745 lines
23 KiB
Rust

use std::fmt::Display;
use colored::Colorize;
use crate::misc::Map;
use super::{
common::{Dimension, Direction, PathField},
Position, COLORS,
};
#[derive(Default, Clone)]
pub struct BruteforceField {
pub blocked: bool,
underground_vertical: bool,
underground_horizontal: bool,
}
static MAX_UNDERGROUND_LENGTH: u8 = 6;
#[derive(Clone)]
pub struct BruteforceBuilder {
map: Map<BruteforceField>,
path: Vec<[(Position, Direction); 2]>,
}
impl BruteforceBuilder {
pub fn new(width: usize, height: usize) -> Self {
Self {
map: Map::new(width, height),
path: Vec::new(),
}
}
pub fn add_path(&mut self, start: (Position, Direction), end: (Position, Direction)) {
self.path.push([start, end]);
}
pub fn set_blocked(&mut self, x: usize, y: usize, blocked: bool) {
self.map.get_mut(x, y).blocked = blocked;
}
pub fn set_blocked_range(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, blocked: bool) {
for x in x1..=x2 {
for y in y1..=y2 {
self.set_blocked(x, y, blocked);
}
}
}
pub fn build(self) -> Bruteforce {
let dimension = Dimension {
width: self.map.width,
height: self.map.height,
};
let mut b = Bruteforce {
map: self.map,
problems: Vec::new(),
pointer_stack: vec![0],
solution_count: 0,
count: 0,
dimension,
};
for [start, end] in self.path {
b.map.get_mut(start.0.x, start.0.y).blocked = true;
b.map.get_mut(end.0.x, end.0.y).blocked = true;
b.problems.push(Problem {
path: vec![PathField::Belt {
pos: start.0,
dir: start.1,
}],
end_pos: end.0,
end_dir: end.1,
finished: false,
})
}
b
}
}
#[derive(Clone)]
struct Problem {
path: Vec<PathField>,
end_pos: Position,
end_dir: Direction,
finished: bool,
}
#[derive(Clone)]
pub struct Bruteforce {
map: Map<BruteforceField>,
dimension: Dimension,
problems: Vec<Problem>,
pointer_stack: Vec<usize>,
solution_count: u128,
count: u128,
}
impl Bruteforce {
pub fn modify_pointer(&self) -> usize {
self.pointer_stack
.get(self.pointer_stack.len().saturating_sub(2))
.copied()
.unwrap_or(0)
}
fn add_pointer(&self) -> usize {
self.pointer_stack.last().copied().unwrap_or(0)
}
fn internal_apply_path_field(&mut self, i: usize, path_field: PathField) {
match &path_field {
PathField::Belt { pos, dir: _ } => self.map.get_mut(pos.x, pos.y).blocked = true,
PathField::Underground { pos, dir, len } => {
self.map.get_mut(pos.x, pos.y).blocked = true;
for i in 0..=*len {
let mid_pos = pos.in_direction(dir, i.into(), &self.dimension).unwrap();
if dir.vertical() {
self.map.get_mut(mid_pos.x, mid_pos.y).underground_vertical = true;
} else {
self.map
.get_mut(mid_pos.x, mid_pos.y)
.underground_horizontal = true;
}
}
let end_pos = { pos.in_direction(dir, *len as usize, &self.dimension) }.unwrap();
self.map.get_mut(end_pos.x, end_pos.y).blocked = true;
}
}
self.problems[i].path.push(path_field);
}
fn internal_remove_path_field(&mut self, i: usize) {
match self.problems[i].path.pop().unwrap() {
PathField::Belt { pos, dir: _ } => self.map.get_mut(pos.x, pos.y).blocked = false,
PathField::Underground { pos, dir, len } => {
self.map.get_mut(pos.x, pos.y).blocked = false;
for i in 0..=len {
let mid_pos = pos.in_direction(&dir, i.into(), &self.dimension).unwrap();
if dir.vertical() {
self.map.get_mut(mid_pos.x, mid_pos.y).underground_vertical = false;
} else {
self.map
.get_mut(mid_pos.x, mid_pos.y)
.underground_horizontal = false;
}
}
let end_pos = pos.in_direction(&dir, len.into(), &self.dimension).unwrap();
self.map.get_mut(end_pos.x, end_pos.y).blocked = false;
}
}
}
fn apply_path_field(&mut self, path_field: PathField) {
self.internal_apply_path_field(self.add_pointer(), path_field);
// set finished
let i = self.add_pointer();
// dbg!(i, self.check_finish(i));
self.problems[i].finished = self.check_finish(i);
// advance pointer
let current_pointer = self.add_pointer();
let mut pointer = current_pointer;
for i in 1..self.problems.len() {
if !self.problems[(current_pointer + i) % self.problems.len()].finished {
pointer = (current_pointer + i) % self.problems.len();
break;
}
}
self.pointer_stack.push(pointer);
}
fn remove_path_field(&mut self) {
self.internal_remove_path_field(self.modify_pointer());
// remove finish
let i = self.modify_pointer();
self.problems[i].finished = false;
// restore pointer
self.pointer_stack.pop();
}
fn modify_path_field(&mut self, path_field: PathField) {
let i = self.modify_pointer();
let last = *self.problems[i].path.last().unwrap();
match (last, &path_field) {
(PathField::Belt { pos: _, dir: _ }, PathField::Belt { pos: _, dir: _ }) => {}
(PathField::Belt { pos: _, dir: _ }, PathField::Underground { pos, dir, len }) => {
let end_pos = pos
.in_direction(dir, *len as usize, &self.dimension)
.unwrap();
self.map.get_mut(end_pos.x, end_pos.y).blocked = true;
for l in 0..=*len {
let p = pos.in_direction(dir, l as usize, &self.dimension).unwrap();
match dir.vertical() {
true => self.map.get_mut(p.x, p.y).underground_vertical = true,
false => self.map.get_mut(p.x, p.y).underground_horizontal = true,
}
}
}
(
PathField::Underground {
pos: _,
dir: _,
len: _,
},
PathField::Belt { pos: _, dir: _ },
) => {
unreachable!()
}
(
PathField::Underground {
pos,
dir,
len: last_len,
},
PathField::Underground {
pos: _,
dir: _,
len: new_len,
},
) => {
let last_end_pos = pos
.in_direction(&dir, last_len as usize, &self.dimension)
.unwrap();
let new_end_pos = pos
.in_direction(&dir, *new_len as usize, &self.dimension)
.unwrap();
self.map.get_mut(last_end_pos.x, last_end_pos.y).blocked = false;
self.map.get_mut(new_end_pos.x, new_end_pos.y).blocked = true;
match last_len < *new_len {
true => {
for l in last_len + 1..=*new_len {
let p = pos.in_direction(&dir, l as usize, &self.dimension).unwrap();
match dir.vertical() {
true => self.map.get_mut(p.x, p.y).underground_vertical = true,
false => self.map.get_mut(p.x, p.y).underground_horizontal = true,
}
}
}
false => unreachable!(),
}
}
}
*self.problems[i].path.last_mut().unwrap() = path_field;
// set finished
self.problems[i].finished = self.check_finish(i);
}
fn check_finish(&self, i: usize) -> bool {
match self.problems[i].path.last().unwrap() {
PathField::Belt { pos, dir } => pos
.in_direction(dir, 1, &self.dimension)
.filter(|p| {
p == &self.problems[i].end_pos && dir != &self.problems[i].end_dir.reverse()
})
.is_some(),
PathField::Underground { pos, dir, len } => pos
.in_direction(dir, *len as usize + 1, &self.dimension)
.filter(|p| {
p == &self.problems[i].end_pos && dir != &self.problems[i].end_dir.reverse()
})
.is_some(),
}
}
fn modify_underground(&mut self, pos: &Position, dir: &Direction, len: u8) -> bool {
if len >= MAX_UNDERGROUND_LENGTH {
return false;
}
if len == 2
&& match dir.vertical() {
true => self.map.get(pos.x, pos.y).underground_vertical,
false => self.map.get(pos.x, pos.y).underground_horizontal,
}
{
return false;
}
let end_pos = match pos.in_direction(dir, len as usize, &self.dimension) {
Some(s) => s,
None => {
return false;
}
};
if match dir.vertical() {
true => self.map.get(end_pos.x, end_pos.y).underground_vertical,
false => self.map.get(end_pos.x, end_pos.y).underground_horizontal,
} {
return false;
}
if !self.map.get(end_pos.x, end_pos.y).blocked {
self.modify_path_field(PathField::Underground {
pos: *pos,
dir: *dir,
len,
});
return true;
}
self.modify_underground(pos, dir, len + 1)
}
// fn modify_underground(&mut self, pos: &Position, dir: &Direction, len: u8) -> bool {
// dbg!(pos, dir, len);
// if match dir.vertical() {
// true => self.map.get(pos.x, pos.y).underground_vertical,
// false => self.map.get(pos.x, pos.y).underground_horizontal,
// } {
// return false;
// }
// for l in len..MAX_UNDERGROUND_LENGTH {
// if let Some(p) = self.pos_in_direction(pos, dir, l as usize) {
// dbg!(l, &p);
// if !self.map.get(p.x, p.y).blocked {
// if !(1..l)
// .filter_map(|i| self.pos_in_direction(pos, dir, i as usize))
// .any(|local_pos| !match dir.vertical() {
// true => self.map.get(local_pos.x, local_pos.y).underground_vertical,
// false => {
// self.map
// .get(local_pos.x, local_pos.y)
// .underground_horizontal
// }
// })
// {
// dbg!(true);
// self.modify_path_field(PathField::Underground {
// pos: *pos,
// dir: *dir,
// len: l,
// });
// return true;
// } else {
// return false;
// }
// }
// }
// }
// false
// }
fn modify_remove(&mut self) -> bool {
if let Some([second_last, last]) = self.modify_path().last_chunk().copied() {
match last {
PathField::Belt { pos, dir } => {
if second_last.dir() == &dir {
self.modify_path_field(PathField::Belt {
pos,
dir: second_last.dir().clockwise(),
});
if self.is_next_free(&pos, &second_last.dir().clockwise()) {
return true;
} else {
return self.modify_remove();
}
}
if second_last.dir().clockwise() == dir {
self.modify_path_field(PathField::Belt {
pos,
dir: second_last.dir().counter_clockwise(),
});
if self.is_next_free(&pos, &second_last.dir().counter_clockwise()) {
return true;
} else {
return self.modify_remove();
}
}
if second_last.dir().counter_clockwise() == dir
&& self.modify_underground(&pos, second_last.dir(), 2)
{
let (p, d) = self
.modify_path()
.last()
.unwrap()
.end_pos(&self.dimension)
.unwrap();
if self.is_next_free(&p, &d) {
return true;
} else {
return self.modify_remove();
}
}
}
PathField::Underground { pos, dir, len } => {
if self.modify_underground(&pos, &dir, len + 1) {
let (p, d) = self
.modify_path()
.last()
.unwrap()
.end_pos(&self.dimension)
.unwrap();
if self.is_next_free(&p, &d) {
return true;
} else {
return self.modify_remove();
}
}
}
}
self.remove_path_field();
self.modify_remove()
} else {
false
}
}
fn modify_path(&self) -> &Vec<PathField> {
&self.problems[self.modify_pointer()].path
}
fn add_path(&self) -> &Vec<PathField> {
&self.problems[self.add_pointer()].path
}
// fn modify_path_mut(&mut self) -> &mut Vec<PathField> {
// let i = self.modify_pointer();
// &mut self.problems[i].path
// }
fn is_next_free(&self, pos: &Position, dir: &Direction) -> bool {
let i = self.modify_pointer();
{ pos.in_direction(dir, 1, &self.dimension) }
.filter(|p| !self.map.get(p.x, p.y).blocked || self.problems[i].end_pos == *p)
.is_some()
}
// Add an Path elemeent
fn add(&mut self) -> bool {
let (pos, dir) = self
.add_path()
.last()
.unwrap()
.end_pos(&self.dimension)
.unwrap();
if let Some(p) = pos.in_direction(&dir, 1, &self.dimension) {
if !self.map.get(p.x, p.y).blocked {
self.apply_path_field(PathField::Belt { pos: p, dir });
return self.is_next_free(&p, &dir);
}
}
false
}
pub fn next_state(&mut self) -> bool {
self.count += 1;
if self.add() {
return true;
}
self.modify_remove()
}
pub fn next_finish_state(&mut self) -> bool {
while self.next_state() {
// if self.count % 1000000 == 0 {
// println!("{}\n{}", self.count, self);
// }
if self.problems.iter().all(|p| p.finished) {
self.solution_count += 1;
return true;
}
}
false
}
pub fn get_paths(&self) -> Vec<Vec<PathField>> {
self.problems.iter().map(|p| p.path.clone()).collect()
}
pub fn cost(&self) -> f64 {
self.problems
.iter()
.flat_map(|p| {
p.path.iter().map(|f| match f {
PathField::Belt { pos: _, dir: _ } => 1.5,
PathField::Underground {
pos: _,
dir: _,
len: _,
} => 17.5,
})
})
.sum()
}
pub fn count(&self) -> u128 {
self.count
}
pub fn solution_count(&self) -> u128 {
self.solution_count
}
}
impl Display for Bruteforce {
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.problems.iter().enumerate() {
if problem.finished {
m.set(problem.end_pos.x, problem.end_pos.y, Some((i, "T")));
} else {
m.set(problem.end_pos.x, problem.end_pos.y, Some((i, "t")));
}
for p in &problem.path {
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, &self.dimension)
.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, ""))),
};
}
}
}
}
// 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 self.map.get(x, y).underground_horizontal {
write!(f, "_")?;
} else if self.map.get(x, y).underground_vertical {
write!(f, "|")?;
} else if x % 8 == 0 || y % 8 == 0 {
write!(f, "")?;
} else {
write!(f, " ")?;
}
}
writeln!(f)?;
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::problems;
macro_rules! test_bruteforce {
($i:ident $x:expr) => {
#[test]
fn $i() {
let mut b = problems::$i();
while b.next_finish_state() {}
assert_eq!(b.solution_count(), $x);
}
};
($i:ident $x:expr; $($is:ident $xs:expr);+) => {
test_bruteforce!($i $x);
test_bruteforce!($($is $xs);+);
};
}
test_bruteforce!(simple 9; mid 238240; snake 5);
}
pub mod problems {
use super::*;
pub fn simple() -> Bruteforce {
let mut b = BruteforceBuilder::new(6, 8);
b.add_path(
(Position::new(1, 0), Direction::Down),
(Position::new(0, 0), Direction::Up),
);
b.add_path(
(Position::new(4, 0), Direction::Down),
(Position::new(1, 7), Direction::Up),
);
b.set_blocked_range(0, 2, 5, 5, true);
b.build()
}
pub fn mid() -> Bruteforce {
let mut b = BruteforceBuilder::new(6, 6);
b.add_path(
(Position::new(0, 0), Direction::Down),
(Position::new(5, 5), Direction::Down),
);
b.add_path(
(Position::new(0, 5), Direction::Up),
(Position::new(5, 0), Direction::Up),
);
b.build()
}
pub fn snake() -> Bruteforce {
let mut p = BruteforceBuilder::new(14, 3);
p.add_path(
(Position::new(0, 0), Direction::Right),
(Position::new(13, 0), Direction::Right),
);
p.add_path(
(Position::new(0, 1), Direction::Right),
(Position::new(13, 1), Direction::Right),
);
p.add_path(
(Position::new(0, 2), Direction::Right),
(Position::new(13, 2), Direction::Right),
);
p.set_blocked_range(3, 2, 10, 2, true);
p.build()
}
pub fn weaving() -> Bruteforce {
let mut b = BruteforceBuilder::new(14, 6);
b.add_path(
(Position::new(0, 0), Direction::Right),
(Position::new(13, 0), Direction::Right),
);
b.add_path(
(Position::new(0, 1), Direction::Right),
(Position::new(13, 1), Direction::Right),
);
b.add_path(
(Position::new(0, 2), Direction::Right),
(Position::new(13, 2), Direction::Right),
);
b.add_path(
(Position::new(0, 3), Direction::Right),
(Position::new(13, 3), Direction::Right),
);
b.add_path(
(Position::new(0, 4), Direction::Right),
(Position::new(13, 4), Direction::Right),
);
b.add_path(
(Position::new(0, 5), Direction::Right),
(Position::new(13, 5), Direction::Right),
);
// b.set_blocked_range(7, 2, 10, 2, true);
// b.set_blocked_range(7, 3, 10, 4, true);
b.set_blocked_range(3, 2, 10, 3, true);
b.set_blocked(2, 0, true);
b.set_blocked(11, 0, true);
b.set_blocked(2, 5, true);
b.set_blocked(11, 5, true);
b.build()
}
}