use std::fmt::Display; use termcolor::ColorSpec; use crate::misc::Map; use super::{ common::{print_map, Dimension, Direction, PathField, PositionType}, Position, COLORS, }; #[derive(Default, Debug, Clone)] pub struct BruteforceField { pub blocked: bool, underground_vertical: bool, underground_horizontal: bool, } static MAX_UNDERGROUND_LENGTH: u8 = 6; #[derive(Clone, Debug)] 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_underground(&mut self, x: usize, y: usize, dir: Option) { match dir { Some(d) => { if d.vertical() { self.map.get_mut(x, y).underground_vertical = true; } else { self.map.get_mut(x, y).underground_horizontal = true; } } None => { self.map.get_mut(x, y).underground_vertical = false; self.map.get_mut(x, y).underground_horizontal = false; } } } 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 { // dbg!(&self); 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 as usize, start.0.y as usize) // .blocked = true; // b.map.get_mut(end.0.x as usize, end.0.y as usize).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) } /// Path field must be valid and inside bounds. 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 as usize, pos.y as usize).blocked = true } PathField::Underground { pos, dir, len } => { self.map.get_mut(pos.x as usize, pos.y as usize).blocked = true; for i in 0..=*len { let mid_pos = pos.in_direction(dir, i as PositionType); if dir.vertical() { self.map .get_mut(mid_pos.x as usize, mid_pos.y as usize) .underground_vertical = true; } else { self.map .get_mut(mid_pos.x as usize, mid_pos.y as usize) .underground_horizontal = true; } } let end_pos = pos.in_direction(dir, *len as PositionType); self.map .get_mut(end_pos.x as usize, end_pos.y as usize) .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 as usize, pos.y as usize).blocked = false } PathField::Underground { pos, dir, len } => { self.map.get_mut(pos.x as usize, pos.y as usize).blocked = false; for i in 0..=len { let mid_pos = pos.in_direction(&dir, i as PositionType); if dir.vertical() { self.map .get_mut(mid_pos.x as usize, mid_pos.y as usize) .underground_vertical = false; } else { self.map .get_mut(mid_pos.x as usize, mid_pos.y as usize) .underground_horizontal = false; } } let end_pos = pos.in_direction(&dir, len.into()); self.map .get_mut(end_pos.x as usize, end_pos.y as usize) .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 PositionType); self.map .get_mut(end_pos.x as usize, end_pos.y as usize) .blocked = true; for l in 0..=*len { let p = pos.in_direction(dir, l as PositionType); match dir.vertical() { true => { self.map .get_mut(p.x as usize, p.y as usize) .underground_vertical = true } false => { self.map .get_mut(p.x as usize, p.y as usize) .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 PositionType); let new_end_pos = pos.in_direction(&dir, *new_len as PositionType); self.map .get_mut(last_end_pos.x as usize, last_end_pos.y as usize) .blocked = false; self.map .get_mut(new_end_pos.x as usize, new_end_pos.y as usize) .blocked = true; match last_len < *new_len { true => { for l in last_len + 1..=*new_len { let p = pos.in_direction(&dir, l as PositionType); match dir.vertical() { true => { self.map .get_mut(p.x as usize, p.y as usize) .underground_vertical = true } false => { self.map .get_mut(p.x as usize, p.y as usize) .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 { self.problems[i].path.last().unwrap().end_pos() == (self.problems[i].end_pos, self.problems[i].end_dir) // match self.problems[i].path.last().unwrap() { // PathField::Belt { pos, dir } => Some(pos.in_direction(dir, 1)) // .filter(|p| { // p == &self.problems[i].end_pos && dir != &self.problems[i].end_dir.reverse() // }) // .is_some(), // PathField::Underground { pos, dir, len } => { // Some(pos.in_direction(dir, *len as PositionType + 1)) // .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 as usize, pos.y as usize) .underground_vertical } false => { self.map .get(pos.x as usize, pos.y as usize) .underground_horizontal } } { return false; } let binding = pos.in_direction(dir, len as PositionType); let end_pos = match binding.in_range( &Position::new(0, 0), &Position::new( self.map.width as PositionType, self.map.height as PositionType, ), ) { Some(t) => t, None => return false, }; if match dir.vertical() { true => { self.map .get(end_pos.x as usize, end_pos.y as usize) .underground_vertical } false => { self.map .get(end_pos.x as usize, end_pos.y as usize) .underground_horizontal } } { return false; } if !self.map.get(end_pos.x as usize, end_pos.y as usize).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(); 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(); 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(); self.check_finish(i) || pos .in_direction(dir, 1) .in_range( &Position::new(0, 0), &Position::new( self.map.width as PositionType, self.map.height as PositionType, ), ) .filter(|&p| { !self.map.get(p.x as usize, p.y as usize).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(); if let Some(&p) = pos.in_direction(&dir, 1).in_range( &Position::new(0, 0), &Position::new( self.map.width as PositionType, self.map.height as PositionType, ), ) { if !self.map.get(p.x as usize, p.y as usize).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; 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 as usize, problem.end_pos.y as usize, Some((i, "T")), ); } else { m.set( problem.end_pos.x as usize, problem.end_pos.y as usize, Some((i, "t")), ); } for p in &problem.path { match p { PathField::Belt { pos, dir } => match dir { Direction::Up => m.set(pos.x as usize, pos.y as usize, Some((i, "↑"))), Direction::Right => m.set(pos.x as usize, pos.y as usize, Some((i, "→"))), Direction::Down => m.set(pos.x as usize, pos.y as usize, Some((i, "↓"))), Direction::Left => m.set(pos.x as usize, pos.y as usize, Some((i, "←"))), }, PathField::Underground { pos, dir, len } => { match dir { Direction::Up => m.set(pos.x as usize, pos.y as usize, Some((i, "↟"))), Direction::Right => { m.set(pos.x as usize, pos.y as usize, Some((i, "↠"))) } Direction::Down => { m.set(pos.x as usize, pos.y as usize, Some((i, "↡"))) } Direction::Left => { m.set(pos.x as usize, pos.y as usize, Some((i, "↞"))) } }; let end_pos = pos.in_direction(dir, *len as PositionType); match dir { Direction::Up => { m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↥"))) } Direction::Right => { m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↦"))) } Direction::Down => { m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↧"))) } Direction::Left => { m.set(end_pos.x as usize, end_pos.y as usize, Some((i, "↤"))) } }; } } } } // Print body print_map(self.map.width as i32, self.map.height as i32, |x, y| { if let Some((i, c)) = m.get(x as usize, y as usize) { let mut color = ColorSpec::new(); color.set_fg(Some(COLORS[*i])); (color, *c) } else if self.map.get(x as usize, y as usize).blocked { (ColorSpec::new(), "#") } else if self.map.get(x as usize, y as usize).underground_horizontal { (ColorSpec::new(), "_") } else if self.map.get(x as usize, y as usize).underground_vertical { (ColorSpec::new(), "|") } else if x % 8 == 0 || y % 8 == 0 { (ColorSpec::new(), "∙") } else { (ColorSpec::new(), " ") } }); 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 308986; 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.set_blocked(1, 0, true); b.add_path( (Position::new(4, 0), Direction::Down), (Position::new(1, 7), Direction::Up), ); b.set_blocked(4, 0, true); 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.set_blocked(0, 0, true); b.add_path( (Position::new(0, 5), Direction::Up), (Position::new(5, 0), Direction::Up), ); b.set_blocked(0, 0, true); b.build() } pub fn snake() -> Bruteforce { let mut p = BruteforceBuilder::new(13, 3); p.add_path( (Position::new(0, 0), Direction::Right), (Position::new(12, 0), Direction::Right), ); p.add_path( (Position::new(0, 1), Direction::Right), (Position::new(12, 1), Direction::Right), ); p.add_path( (Position::new(0, 2), Direction::Right), (Position::new(12, 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() } }