diff --git a/day6/Cargo.lock b/day6/Cargo.lock new file mode 100644 index 0000000..61d4c64 --- /dev/null +++ b/day6/Cargo.lock @@ -0,0 +1,61 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "day6" +version = "0.1.0" +dependencies = [ + "rayon", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] diff --git a/day6/Cargo.toml b/day6/Cargo.toml new file mode 100644 index 0000000..7ef1ea8 --- /dev/null +++ b/day6/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "day6" +version = "0.1.0" +edition = "2021" + +[dependencies] +rayon = "1.10.0" diff --git a/day6/src/main.rs b/day6/src/main.rs new file mode 100644 index 0000000..dc4a2a3 --- /dev/null +++ b/day6/src/main.rs @@ -0,0 +1,191 @@ +use rayon::prelude::*; +use std::fmt::Display; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Direction { + Up, + Down, + Left, + Right, +} +use Direction::*; + +impl Direction { + fn to_pos(self) -> (isize, isize) { + match self { + Up => (-1, 0), + Down => (1, 0), + Left => (0, -1), + Right => (0, 1), + } + } + fn turn(&mut self) { + match self { + Up => *self = Right, + Down => *self = Left, + Left => *self = Up, + Right => *self = Down, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum PositionType { + Empty, + Visited(Vec), + Obstructed, +} +use PositionType::*; + +impl Display for PositionType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Empty => write!(f, "."), + Visited(_) => write!(f, "X"), + Obstructed => write!(f, "#"), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Guard { + position: (usize, usize), + direction: Direction, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct State { + map: Vec>, + guard: Guard, +} + +impl Display for State { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.map + .iter() + .map(|l| l.iter().map(|c| c.to_string()).collect::() + "\n") + .collect::() + ) + } +} + +fn parse(input: &str) -> State { + let mut guard_pos = (0, 0); + State { + map: input + .lines() + .enumerate() + .map(|(line_idx, line)| { + line.chars() + .enumerate() + .map(|(c_idx, c)| match c { + '#' => Obstructed, + '^' => { + guard_pos = (line_idx, c_idx); + Empty + } + _ => Empty, + }) + .collect() + }) + .collect(), + guard: Guard { + position: guard_pos, + direction: Up, + }, + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum MoveResult { + EndedOutside, + EndedLoop, + Running, +} +use MoveResult::*; + +fn move_guard(state: &mut State) -> MoveResult { + if let Visited(ref mut directions) = + state.map[state.guard.position.0][state.guard.position.1] + { + if directions.contains(&state.guard.direction) { + return EndedLoop; + } + directions.push(state.guard.direction); + } else { + state.map[state.guard.position.0][state.guard.position.1] = + Visited(vec![]); + } + let direction_pos = state.guard.direction.to_pos(); + let pos_in_front = ( + ((state.guard.position.0 as isize) + direction_pos.0) as usize, + ((state.guard.position.1 as isize) + direction_pos.1) as usize, + ); + let Some(line) = state.map.get(pos_in_front.0) else { + return EndedOutside; + }; + let Some(pos_type) = line.get(pos_in_front.1) else { + return EndedOutside; + }; + match pos_type { + Obstructed => { + state.guard.direction.turn(); + } + _ => { + state.guard.position = pos_in_front; + } + } + Running +} + +fn part1(state: &mut State) -> usize { + while move_guard(state) == Running {} + state + .map + .iter() + .map(|l| l.iter().filter(|c| matches!(c, Visited(_))).count()) + .sum() +} + +fn part2(state: &State) -> usize { + let mut original_path = state.clone(); + while move_guard(&mut original_path) == Running {} + let original_path_positions: Vec<(usize, usize)> = original_path + .map + .iter() + .enumerate() + .flat_map(|(l_idx, l)| { + l.iter() + .enumerate() + .filter(|(_, c)| matches!(c, Visited(_))) + .map(move |(idx, _)| (l_idx, idx)) + }) + .collect(); + original_path_positions + .par_iter() + .filter(|pos| { + let mut new_start_state = state.clone(); + new_start_state.map[pos.0][pos.1] = Obstructed; + loop { + match move_guard(&mut new_start_state) { + EndedOutside => { + return false; + } + EndedLoop => { + return true; + } + Running => (), + } + } + }) + .count() +} + +fn main() { + let input = parse(include_str!("../input.txt")); + println!("{}", part1(&mut input.clone())); + println!("{}", part2(&input)); +}