538 lines
17 KiB
Rust
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(())
|
|
}
|
|
}
|