diff --git a/day24/Cargo.lock b/day24/Cargo.lock new file mode 100644 index 0000000..6d2ef2f --- /dev/null +++ b/day24/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "day24" +version = "0.1.0" diff --git a/day24/Cargo.toml b/day24/Cargo.toml new file mode 100644 index 0000000..456f6d7 --- /dev/null +++ b/day24/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day24" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/day24/example.txt b/day24/example.txt new file mode 100644 index 0000000..628e51b --- /dev/null +++ b/day24/example.txt @@ -0,0 +1,5 @@ +########### +#0.1.....2# +#.#######.# +#4.......3# +########### diff --git a/day24/input.txt b/day24/input.txt new file mode 100644 index 0000000..c9439b3 --- /dev/null +++ b/day24/input.txt @@ -0,0 +1,37 @@ +################################################################################################################################################################################### +#.....#.#.....#...#....4#.....#.#...#.........#...#...............#...................#...#.#...........#.#...........#.#.#.#.........#.#.......#...#...........#.....#...#7..#.#.# +###.#.#.###.#.#.###.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.###.#.###.#.#.#.###.###.#.#####.###.#.#.###.#.#.#.#.#.#.#.#.#.#.#.#.###.#####.#.#.#.#.#####.#.#.#.###.#.#.#.#.#####.#.#.#.# +#.#.....#.#.#...#.........#.....#.....#.......#.#.#.............#.#.#.#.....#.#.......#.....#.........#...#.......#.....#.#.#.............#...........#.#.....#.#.....#.......#.#.# +#.#.#.#.#.#.#.#.#.#.#####.#####.###.###.#.###.#.###.###.#.#####.#.#.#.#.#.###.#.#.###.#.#.#.#.###.#########.###########.#.#.###.#.#.###.###.#.###.###.#.#.#####.#.###.#.#####.#.### +#...........#...#...#.....#.....#...#.#...#.#.....#.........#...#...#.....#.....#.#.#...#...#...#...#.....#.......#...#...#...............#...#...#.............#.....#.#.....#...# +###.#.#.###.#.#.#.#.###.#.###.#####.#.#.#.#.#.###.###.###.#.#.#.###.#.#.#.#.###.#.#.#.###.#####.#########.#.#.#.#.#.###.#.#.#.#.#####.#.#.#.#.###.#.#.#.#.#.#.#.#####.#.###.#.#.#.# +#3#...#.#.#.#.........#...............#...#.#.....#...#.....#...#.......#...#.....#.#.#...#.....#...#.....#.#.#.....#.....#...........#.#.#.#.....#.#.........#.#...#.#.#.#...#...# +#.###.###.#######.###.#.###.#.#.#.###.###.#######.###.#.#####.#####.#.#.#.#.#######.###.###.###.###.###.#.#########.#.#.#.#.#.#####.###.#.###.#.###.#.#####.###.###.###.#.#.#.###.# +#.#...#.....#.#.............#.....#.#.....#.#.....#.#.#.....#.....#.......#.....#.................#...........#...#.#.....#...#.....#...#.......#.#.....#...#...#.#.#...#...#...#.# +#.###.###.###.#.#.#.#####.#.###.#.#.###.#.#.#.#.#.#.#.#.#.#####.#####.#.#.#.#.#.#.###########.#.#.#.#.#.###.#.#.#.#.#.#.#.#.#.###.#.#.#####.#####.#.###.#.#.#.#.#.#.#####.#.###.#.# +#.....#.......#.#.#.#.#...............#...#.#.#.#...#...........#.....#.#...#.................#...#.#.#...#.............#...#.........#...............#...#.#.#.....#.....#.....#.# +#####.#.#######.#.###.#.#.#.#.###.#.#.#.###.###.###.#.#.#.#.#.#.###.#.#.#.#.#######.###.#.###.#.#.#.###.#.#.###.###.#.#.#.#.#####.#####.#.###.#####.###.#.#.#####.#.#.#####.#.#.#.# +#.#...#.........#...#.#...#.......#...#.#.......#...#.#.........#.#.#...#.#.#.#.........#.#.#.......#...#...#...#.#...#.......#...#.....#...#...#.#...#...#...#...........#...#.#.# +#.#####.#.###.#.#.#######.#.###.#.#.#.#########.#.#.#.#.#####.#.#.#######.#.#.###########.#.#########.###.#.#.#.#.###.#.#.###.#########.#.#.#.###.#.#.###.#.#.###.#####.#.###.#.#.# +#.......#.......#...#.#.#...#...#.....#.#...#...#.#.#.#.#...#.....#.#...#...#.............#.......#.......#...#.#.............#.......#.....#...#...#.#.....#.............#...#.#.# +#.#####.###.#####.#.#.#.#.#.#.#.#.#.#.#.#.###.###.#.###.###.#.#.###.#.#.#.#.###.#.###.#.#.#.#.#.#.#.#######.#.#.###.#.#.#.#.###.#.###.###.#####.#.#.#.#.#####.###.#.###.#####.###.# +#..6#...#...#...#...#.#.....#...#.#.#...#...........#.#.#...#.#.#.....#.....#.#.#.....#.......#.................#.#.....#.#.........#...#...#...........#.#2....#.#.......#.#.#.#.# +#.###.###.#.###.#####.#####.#.###.###.#.###.#.#####.#.#.#.#.#.#.###.#.#.#.#.#.#.#.#.#.#.###.#######.#.#.#.#.#####.#.#.#######.###.#####.###.#####.#####.#.#####.###.#######.###.### +#.#.....#...#...#...........#.#.......#.#...#.#.............#...#...#.....#...#.....#.......#.......#.......#...#...#.......#...#.......#.#...#...#.........#...#...#...#.......#.# +#.#.###.#.#.#.#.###.#######.#.#.###.###.#####.###.#.###.#######.#####.#####.#.#####.#.###.#.#.#.#.#####.###.#.#.#.#.#.#.#.#.#############.###.#.#.#.###.#.#.###.#.#.#.#.#####.#.#.# +#...#.........#.....#...#.#...#.....#...#...#.......#.....#...#...#...#...#.............#.#...#.............#.....#...#.#.#.......#.....#.....#.....#...........#...#...#.....#...# +#.#######.#.#.###.#.#.#.#.#.###.#.#.#.###.#.###.#.#.#.#####.#.#.#.#.#.#.#.#.#####.#####.#####.#.#######.###.#.#.###.#.###.#.#.#.#.#.###.#.#.###.#.#.#######.###.#.###.#.#.#.#.###.# +#.....#.......#...#.#...#.....#...#.#...........#.....#.....#.#.#...#.....#.................#.........#.#.......#...........#...#...#.......#0#...#.....#.......#.#...........#...# +#.#.#.#.#.###.#.#.#.###.###.#.#.###.#.#.#####.#######.#.#.#.#.#.###.###.###.#.#####.###.#####.#.#.###.###.###.###.#####.###.#.#.#.#.#.###.#.#.#.#.#.###.#.###.#.#.#.#.#.#.#####.### +#.#.#...#...#.#.......#.............#...........................#.......#...........#.#...#...#.#...#.....#...#.#.#.#.#.#.......#.#...#...#...#...............#.......#.....#.....# +#.#.###.#.#.#.#.#.#####.#.#####.#.#.###.#.#.#.#.#############.#.###.#.#.#.#.#####.#.#.###.#.###.#.#.#######.###.#.#.#.#.#.###.#.#####.#.###.###.#######.#.###.#####.#.#.#.#######.# +#...#.......#.....#...#...#...#.....#5....#...#.......#.#.#...#...........#.#.......#.#...#.#.......#.#.#...#...#.....#.............#...#...#.....#.................#.....#.#...#.# +#######.#.#.#######.#####.###.#.#.#######.#.#.#.#.#.#.#.#.#.###.#.###.#.#.#.###.###.#.#.#.###.#.###.#.#.###.#.###.#####.###.#######.#.#.#.#.#.#.#.#########.###.#.#.#.#.#.#.#.#.### +#.#.........#...........#.........#.........#.#.#...........#...#.....#...................#...........#...#...#...#.#.......#...#.....#.#.#.....#.#.............#.........#.#...#.# +#.#.#.###.#.###.#.###.#.###.#.#######.#.###.#.#.#.#########.#.###.#.#####.###.#.#.###.#.#.#.###.#.#####.###.#.###.#.#.###.#.#.#.#.#.#.#.#.###.#.#.###.#.#####.#.#.#######.#.#####.# +#.........#.#.....#.....#...#...#.......#.....#.................#...#...#.....#...#...#.#.#.#...#...........#.#.....#.#.....#...#.#...#.......#.........#.....#.....#.......#...#.# +#.#####.#.#.#.#.#.#.#####.###.###.#.#####.###.#####.###.#.#.#.#.#.###.#.#.#.#.#.#####.###.###.#.#.#.#.#.###.#.#.#.#.#.#.#.#####.#.#.#.#.#.#########.#.#.#.###.#.###.#.#.#.#.#.#.### +#.......#...#...#.....#.#...#...#...#.#.............#.....#.............#.#.......#.......#...#...#...#.....#.......#...#...........#.#...#.#.......#...........#.#.....#.....#...# +#.#.#.#.#.###.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#####.#.###.#.#.#####.#.#.#.#####.#.#.###.###.#.#.#.#.#.#.#.#####.#.#.#####.###.###.###.###.#.#.#.#.#.#.#########.#####.#.#.#.#.#.# +#.#.#.#.............#...#...#.#.....#...........#.........#...#.#.#...#.#.........#.........#.........#.....#.........#...#...#...#..1#.....#.#.#...#.#.....#...#...........#.....# +################################################################################################################################################################################### diff --git a/day24/src/main.rs b/day24/src/main.rs new file mode 100644 index 0000000..fe9051d --- /dev/null +++ b/day24/src/main.rs @@ -0,0 +1,177 @@ +use std::collections::{BTreeSet, HashSet, VecDeque}; + +#[cfg(debug_assertions)] +const TO_VISIT: usize = 4; +#[cfg(debug_assertions)] +const TO_VISIT_CHAR: char = '4'; +#[cfg(debug_assertions)] +const FILENAME: &str = "example.txt"; + +#[cfg(not(debug_assertions))] +const TO_VISIT: usize = 7; +#[cfg(not(debug_assertions))] +const TO_VISIT_CHAR: char = '7'; +#[cfg(not(debug_assertions))] +const FILENAME: &str = "input.txt"; + +fn main() { + let mut position = (0, 0); + let mut targets = HashSet::new(); + + let map: Vec> = std::fs::read_to_string(FILENAME) + .unwrap() + .lines() + .enumerate() + .map(|(y, l)| { + l.chars() + .enumerate() + .map({ + |(x, c)| match c { + '#' => Item::Wall, + '0' => { + position = (x, y); + Item::Passage + } + '1'..=TO_VISIT_CHAR => { + targets.insert((x, y)); + Item::Passage + } + _ => Item::Passage, + } + }) + .collect() + }) + .collect(); + + let grid = Grid::new(map, targets); + + let state = State::new(position); + + let result = bfs(state.clone(), &grid) - 1; // 460 + println!("{result}"); + + let result = bfs_2(state, &grid); // 668 + println!("{result}"); +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct State { + position: (usize, usize), + visited: BTreeSet<(usize, usize)>, +} + +impl State { + fn is_goal_state(&self) -> bool { + self.visited.len() == TO_VISIT + } + + fn is_goal_state_2(&self, start_position: (usize, usize)) -> bool { + self.visited.len() == TO_VISIT && self.position == start_position + } + + fn get_neighbor_states(&self, grid: &Grid) -> impl Iterator { + [ + (self.position.0 - 1, self.position.1), + (self.position.0, self.position.1 - 1), + (self.position.0, self.position.1 + 1), + (self.position.0 + 1, self.position.1), + ] + .into_iter() + .filter(|(x, y)| grid.is_passage(*x, *y)) + .map(|(x, y)| { + let mut state = self.clone(); + state.position = (x, y); + state + }) + } + + fn new(position: (usize, usize)) -> Self { + let visited = BTreeSet::new(); + Self { position, visited } + } +} + +#[derive(Debug)] +struct Grid { + map: Vec>, + targets: HashSet<(usize, usize)>, +} + +impl Grid { + fn is_target_position(&self, position: (usize, usize)) -> bool { + self.targets.contains(&position) + } + + fn is_passage(&self, x: usize, y: usize) -> bool { + match self.map[y][x] { + Item::Passage => true, + Item::Wall => false, + } + } + + fn new(map: Vec>, targets: HashSet<(usize, usize)>) -> Self { + Self { map, targets } + } +} + +#[derive(Debug)] +enum Item { + Passage, + Wall, +} + +fn bfs(start: State, grid: &Grid) -> usize { + let mut queue = VecDeque::new(); + let mut visited = HashSet::new(); + + visited.insert(start.clone()); + + queue.push_back((start, 0)); + + while let Some((mut state, distance)) = queue.pop_front() { + if state.is_goal_state() { + return distance; + } + + if grid.is_target_position(state.position) { + state.visited.insert(state.position); + } + + for neighbor in state.get_neighbor_states(grid) { + if !visited.contains(&neighbor) { + visited.insert(neighbor.clone()); + queue.push_back((neighbor, distance + 1)); + } + } + } + panic!("goal unreachable"); +} + +fn bfs_2(start: State, grid: &Grid) -> usize { + let mut queue = VecDeque::new(); + let mut visited = HashSet::new(); + + visited.insert(start.clone()); + + let start_position = start.position; + + queue.push_back((start, 0)); + + while let Some((mut state, distance)) = queue.pop_front() { + if state.is_goal_state_2(start_position) { + return distance; + } + + if grid.is_target_position(state.position) { + state.visited.insert(state.position); + } + + for neighbor in state.get_neighbor_states(grid) { + if !visited.contains(&neighbor) { + visited.insert(neighbor.clone()); + queue.push_back((neighbor, distance + 1)); + } + } + } + panic!("goal unreachable"); +}