factorio_blueprint/src/belt_finding/brute_force.rs
2023-12-31 15:54:54 +01:00

538 lines
17 KiB
Rust

use std::fmt::Display;
use clap::builder::PathBufValueParser;
use colored::Colorize;
use crate::misc::Map;
use super::{Direction, Position, COLORS};
#[derive(Default)]
pub struct BruteforceField {
pub blocked: bool,
underground_vertical: bool,
underground_horizontal: bool,
}
#[derive(Clone, Debug)]
pub enum PathField {
Belt {
pos: Position,
dir: Direction,
},
Underground {
pos: Position,
dir: Direction,
len: u8,
},
}
impl PathField {
fn pos(&self) -> &Position {
match self {
PathField::Belt { pos, dir: _ } => pos,
PathField::Underground {
pos,
dir: _,
len: _,
} => pos,
}
}
fn dir(&self) -> &Direction {
match self {
PathField::Belt { pos: _, dir } => dir,
PathField::Underground {
pos: _,
dir,
len: _,
} => dir,
}
}
}
static MAX_UNDERGROUND_LENGTH: u8 = 6;
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 mut b = Bruteforce {
map: self.map,
problems: Vec::new(),
pointer_stack: vec![0],
solution_count: 0,
count: 0,
depth: 1,
};
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
}
}
struct Problem {
path: Vec<PathField>,
end_pos: Position,
end_dir: Direction,
finished: bool,
}
pub struct Bruteforce {
map: Map<BruteforceField>,
problems: Vec<Problem>,
pointer_stack: Vec<usize>,
depth: usize,
solution_count: u128,
count: u128,
}
impl Bruteforce {
pub fn modify_pointer(&self) -> usize {
self.pointer_stack
.get(self.pointer_stack.len() - 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 = self.pos_in_direction(pos, dir, i.into()).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 = self.pos_in_direction(pos, dir, *len as usize).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 = self.pos_in_direction(&pos, &dir, i.into()).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 = self.pos_in_direction(&pos, &dir, len.into()).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) {
self.internal_remove_path_field(self.modify_pointer());
self.internal_apply_path_field(self.modify_pointer(), path_field);
// set finished
let i = self.modify_pointer();
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 } => self
.pos_in_direction(pos, dir, 1)
.filter(|p| {
p == &self.problems[i].end_pos && dir != &self.problems[i].end_dir.reverse()
})
.is_some(),
PathField::Underground { pos, dir, len } => self
.pos_in_direction(pos, dir, *len as usize + 1)
.filter(|p| {
p == &self.problems[i].end_pos && dir != &self.problems[i].end_dir.reverse()
})
.is_some(),
}
}
fn path_field_end(&self, path_field: &PathField) -> (Position, Direction) {
match path_field {
PathField::Belt { pos, dir } => (*pos, *dir),
PathField::Underground { pos, dir, len } => (
self.pos_in_direction(pos, dir, *len as usize).unwrap(),
*dir,
),
}
}
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 self.pos_in_direction(pos, dir, len as usize) {
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().cloned() {
match last {
PathField::Belt { pos, dir } => {
if second_last.dir() == &dir {
self.modify_path_field(PathField::Belt {
pos,
dir: second_last.dir().clockwise(),
});
return true;
}
if second_last.dir().clockwise() == dir {
self.modify_path_field(PathField::Belt {
pos,
dir: second_last.dir().counter_clockwise(),
});
return true;
}
if second_last.dir().counter_clockwise() == dir
&& self.modify_underground(&pos, second_last.dir(), 2)
{
return true;
}
}
PathField::Underground { pos, dir, len } => {
if self.modify_underground(&pos, &dir, len + 1) {
return true;
}
}
}
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
// }
// Add an Path elemeent
fn add(&mut self) -> bool {
let (pos, dir) = self.path_field_end(self.add_path().last().unwrap());
if let Some(p) = self.pos_in_direction(&pos, &dir, 1) {
if !self.map.get(p.x, p.y).blocked {
self.apply_path_field(PathField::Belt { pos: p, dir });
return true;
}
}
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
}
fn pos_in_direction(&self, pos: &Position, dir: &Direction, len: usize) -> Option<Position> {
match dir {
Direction::Up => pos.y.checked_sub(len).map(|y| Position::new(pos.x, y)),
Direction::Right => Some(pos.x + len)
.filter(|&x| x < self.map.width)
.map(|x| Position::new(x, pos.y)),
Direction::Down => Some(pos.y + len)
.filter(|&y| y < self.map.height)
.map(|y| Position::new(pos.x, y)),
Direction::Left => pos.x.checked_sub(len).map(|x| Position::new(x, pos.y)),
}
}
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 = self.pos_in_direction(pos, dir, *len as usize).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(())
}
}