diff --git a/y2024/src/days/d16.rs b/y2024/src/days/d16.rs index 50a0fb0..67671ad 100644 --- a/y2024/src/days/d16.rs +++ b/y2024/src/days/d16.rs @@ -1,9 +1,258 @@ -pub fn process_part1(input: &str) -> i32 { +use std::{collections::HashMap, error::Error}; + +use itertools::Itertools; + +pub fn process_part1(input: &str) -> usize { + let mut start = (0, 0); + let mut end = (0, 0); + let grid = input + .lines() + .enumerate() + .map(|(yidx, row)| { + row.chars() + .enumerate() + .map(|(xidx, chara)| { + let tile = GridTile::try_from(chara).unwrap(); + match tile { + GridTile::Wall | GridTile::Path => tile, + GridTile::Start => { + start = (xidx, yidx); + tile + } + GridTile::End => { + end = (xidx, yidx); + tile + } + } + }) + .collect_vec() + }) + .collect_vec(); + let mut next_paths = vec![Reindeer { + coords: start, + ..Default::default() + }]; + let mut arrived: Vec = Vec::new(); + let mut visited = HashMap::new(); + while !next_paths.is_empty() { + next_paths = next_paths + .iter() + .map(|reindeer| reindeer.get_all_next_paths(&grid)) + .collect_vec() + .concat(); + for (idx, reindeer) in next_paths.clone().iter().enumerate().rev() { + if let Some(score) = visited.get_mut(&reindeer.coords) { + if *score <= reindeer.score { + next_paths.remove(idx); + continue; + } else { + *score = reindeer.score; + } + } else { + visited.insert(reindeer.coords, reindeer.score); + } + if reindeer.state == ReindeerState::Arrived { + let arrived_reindeer = next_paths.remove(idx); + arrived.push(arrived_reindeer); + } + } + } + arrived.iter().map(|reindeer| reindeer.score).min().unwrap() +} + +pub fn process_part2(input: &str) -> usize { 0 } -pub fn process_part2(input: &str) -> i32 { - 0 +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] +enum Orientation { + #[default] + East, + West, + North, + South, +} + +impl Orientation { + fn turn_clockwise(&self) -> Self { + match self { + Orientation::East => Self::South, + Orientation::West => Self::North, + Orientation::North => Self::East, + Orientation::South => Self::West, + } + } + + fn turn_counterclockwise(&self) -> Self { + match self { + Orientation::East => Self::North, + Orientation::West => Self::South, + Orientation::North => Self::West, + Orientation::South => Self::East, + } + } +} + +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] +enum ReindeerState { + #[default] + Going, + Arrived, + Stuck, +} + +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] +struct Reindeer { + orientation: Orientation, + coords: (usize, usize), + score: usize, + state: ReindeerState, +} + +impl Reindeer { + fn get_next(&self, grid: &[Vec]) -> Reindeer { + let tile = match self.orientation { + Orientation::East => grid[self.coords.1][self.coords.0 + 1], + Orientation::West => grid[self.coords.1][self.coords.0 - 1], + Orientation::North => grid[self.coords.1 - 1][self.coords.0], + Orientation::South => grid[self.coords.1 + 1][self.coords.0], + }; + let coords = match self.orientation { + Orientation::East => (self.coords.0 + 1, self.coords.1), + Orientation::West => (self.coords.0 - 1, self.coords.1), + Orientation::South => (self.coords.0, self.coords.1 + 1), + Orientation::North => (self.coords.0, self.coords.1 - 1), + }; + if tile == GridTile::Wall { + Reindeer { + state: ReindeerState::Stuck, + ..*self + } + } else if tile == GridTile::End { + Reindeer { + score: self.score + 1, + state: ReindeerState::Arrived, + coords, + ..*self + } + } else { + Reindeer { + score: self.score + 1, + coords, + ..*self + } + } + } + + fn get_right(&self, grid: &[Vec]) -> Reindeer { + let tile = match self.orientation { + Orientation::East => grid[self.coords.1 + 1][self.coords.0], + Orientation::West => grid[self.coords.1 - 1][self.coords.0], + Orientation::North => grid[self.coords.1][self.coords.0 + 1], + Orientation::South => grid[self.coords.1][self.coords.0 - 1], + }; + let coords = match self.orientation { + Orientation::East => (self.coords.0, self.coords.1 + 1), + Orientation::West => (self.coords.0, self.coords.1 - 1), + Orientation::South => (self.coords.0 - 1, self.coords.1), + Orientation::North => (self.coords.0 + 1, self.coords.1), + }; + if tile == GridTile::Wall { + Reindeer { + state: ReindeerState::Stuck, + ..*self + } + } else if tile == GridTile::End { + Reindeer { + score: self.score + 1001, + state: ReindeerState::Arrived, + coords, + orientation: self.orientation.turn_clockwise(), + } + } else { + Reindeer { + score: self.score + 1001, + orientation: self.orientation.turn_clockwise(), + coords, + ..*self + } + } + } + + fn get_left(&self, grid: &[Vec]) -> Reindeer { + let tile = match self.orientation { + Orientation::East => grid[self.coords.1 - 1][self.coords.0], + Orientation::West => grid[self.coords.1 + 1][self.coords.0], + Orientation::North => grid[self.coords.1][self.coords.0 - 1], + Orientation::South => grid[self.coords.1][self.coords.0 + 1], + }; + let coords = match self.orientation { + Orientation::East => (self.coords.0, self.coords.1 - 1), + Orientation::West => (self.coords.0, self.coords.1 + 1), + Orientation::South => (self.coords.0 + 1, self.coords.1), + Orientation::North => (self.coords.0 - 1, self.coords.1), + }; + if tile == GridTile::Wall { + Reindeer { + state: ReindeerState::Stuck, + ..*self + } + } else if tile == GridTile::End { + Reindeer { + score: self.score + 1001, + state: ReindeerState::Arrived, + coords, + orientation: self.orientation.turn_counterclockwise(), + } + } else { + Reindeer { + score: self.score + 1001, + orientation: self.orientation.turn_counterclockwise(), + coords, + ..*self + } + } + } + + fn get_all_next_paths(&self, grid: &[Vec]) -> Vec { + let next = self.get_next(grid); + let right = self.get_right(grid); + let left = self.get_left(grid); + let mut paths = Vec::new(); + if next.state != ReindeerState::Stuck { + paths.push(next); + } + if right.state != ReindeerState::Stuck { + paths.push(right); + } + if left.state != ReindeerState::Stuck { + paths.push(left); + } + paths + } +} + +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] +enum GridTile { + Wall, + #[default] + Path, + Start, + End, +} + +impl TryFrom for GridTile { + type Error = Box; + + fn try_from(value: char) -> std::result::Result> { + match value { + '#' => Ok(Self::Wall), + '.' => Ok(Self::Path), + 'S' => Ok(Self::Start), + 'E' => Ok(Self::End), + _ => Err(Box::from(format!("{value} is not a valid tile"))), + } + } } #[cfg(test)]