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, 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, end_pos: Position, end_dir: Direction, finished: bool, } #[derive(Clone)] pub struct Bruteforce { map: Map, dimension: Dimension, problems: Vec, pointer_stack: Vec, 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 { &self.problems[self.modify_pointer()].path } fn add_path(&self) -> &Vec { &self.problems[self.add_pointer()].path } // fn modify_path_mut(&mut self) -> &mut Vec { // 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> { 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> = 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() } }