Single path brute force

This commit is contained in:
hal8174 2023-12-11 17:28:58 +01:00
parent c10843ad2f
commit 2bc323aa58
4 changed files with 413 additions and 2 deletions

View file

@ -0,0 +1,326 @@
use std::fmt::Display;
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)]
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 = 5;
pub struct Bruteforce {
pub map: Map<BruteforceField>,
pub path: Vec<PathField>,
pub end_pos: Position,
pub end_dir: Direction,
pub solution_count: u128,
pub count: u128,
}
impl Bruteforce {
pub fn apply_path_field(&mut self, 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 1..*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.path.push(path_field);
}
fn apply_path_field_remove(&mut self) {
match self.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 1..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 check_finish(&self) -> bool {
match self.path.last().unwrap() {
PathField::Belt { pos, dir } => self
.pos_in_direction(pos, dir, 1)
.filter(|p| p == &self.end_pos && dir != &self.end_dir.reverse())
.is_some(),
PathField::Underground { pos, dir, len } => self
.pos_in_direction(pos, dir, *len as usize + 1)
.filter(|p| p == &self.end_pos && dir != &self.end_dir.reverse())
.is_some(),
}
}
fn next_state_internal(&mut self, pos: Position, dir: Direction) -> bool {
if let Some(next_pos) = self
.pos_in_direction(&pos, &dir, 1)
.filter(|p| !self.map.get(p.x, p.y).blocked)
{
self.apply_path_field(PathField::Belt { pos: next_pos, dir });
true
} else {
false
}
}
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 {
for l in len..MAX_UNDERGROUND_LENGTH {
if let Some(p) = self.pos_in_direction(pos, dir, l as usize) {
if !self.map.get(p.x, p.y).blocked {
self.apply_path_field_remove();
self.apply_path_field(PathField::Underground {
pos: *pos,
dir: *dir,
len: l,
});
return true;
}
}
}
false
}
fn modify_remove(&mut self) -> bool {
if let Some([second_last, last]) = self.path.last_chunk().cloned() {
match last {
PathField::Belt { pos, dir } => {
if second_last.dir() == &dir {
self.apply_path_field_remove();
self.apply_path_field(PathField::Belt {
pos,
dir: second_last.dir().clockwise(),
});
return true;
}
if second_last.dir().clockwise() == dir {
self.apply_path_field_remove();
self.apply_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(), 1)
{
return true;
}
}
PathField::Underground { pos, dir, len } => {
if self.modify_underground(&pos, &dir, len + 1) {
return true;
}
}
}
self.apply_path_field_remove();
self.modify_remove()
} else {
false
}
}
pub fn next_state(&mut self) -> bool {
self.count += 1;
let (pos, dir) = self.path_field_end(self.path.last().unwrap());
// try add
if let Some(next_pos) = self
.pos_in_direction(&pos, &dir, 1)
.filter(|p| !self.map.get(p.x, p.y).blocked)
{
self.apply_path_field(PathField::Belt { pos: next_pos, dir });
return true;
}
// modify and remove
self.modify_remove()
}
pub fn next_finish_state(&mut self) -> bool {
self.solution_count += 1;
while self.next_state() {
if self.check_finish() {
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)),
}
}
}
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 p in &self.path {
match p {
PathField::Belt { pos, dir } => match dir {
Direction::Up => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Right => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Down => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Left => m.set(pos.x, pos.y, Some((0, ""))),
},
PathField::Underground { pos, dir, len } => {
match dir {
Direction::Up => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Right => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Down => m.set(pos.x, pos.y, Some((0, ""))),
Direction::Left => m.set(pos.x, pos.y, Some((0, ""))),
};
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((0, ""))),
Direction::Right => m.set(end_pos.x, end_pos.y, Some((0, ""))),
Direction::Down => m.set(end_pos.x, end_pos.y, Some((0, ""))),
Direction::Left => m.set(end_pos.x, end_pos.y, Some((0, ""))),
};
}
}
}
// 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(())
}
}