use rayon::prelude::*; pub fn day6() { let (world, start) = World::from_string(&crate::input(6)); let (mut path, _) = traverse(start, &world); path.sort_by_key(|t| (t.x, t.y)); path.dedup_by_key(|t| (t.x, t.y)); let total_traveled = path.len(); let loops = path .par_iter() .filter(|p| { let mut new_world = world.clone(); new_world.map[p.y][p.x] = Terrain::Blocked; let (_, is_loop) = traverse(start, &new_world); is_loop }) .count(); println!("Total Travelled: {}", total_traveled); println!("Total Loopable Positions: {}", loops); } fn traverse(start: Transform, world: &World) -> (Vec, bool) { let mut positions = vec![start]; let mut current = start; while let Some(next) = current.travel(world) { if positions.contains(&next) { return (positions, true); } positions.push(next); current = next; } (positions, false) } #[derive(PartialEq, Eq, Copy, Clone)] struct Transform { x: usize, y: usize, direction: Direction, } impl Transform { fn new(x: usize, y: usize, direction: Direction) -> Self { Self { x, y, direction } } fn turn(&self) -> Self { let direction = match self.direction { Direction::North => Direction::East, Direction::East => Direction::South, Direction::South => Direction::West, Direction::West => Direction::North, }; Self::new(self.x, self.y, direction) } fn travel(&self, world: &World) -> Option { let (dx, dy) = match self.direction { Direction::North => (0, -1), Direction::South => (0, 1), Direction::East => (1, 0), Direction::West => (-1, 0), }; let new_x = (self.x as i32 + dx).try_into().ok()?; let new_y = (self.y as i32 + dy).try_into().ok()?; let next = Self::new(new_x, new_y, self.direction); match world.terrain_at(&next) { Some(Terrain::Blocked) => Some(self.turn()), Some(Terrain::Free) => Some(next), None => None, } } } #[derive(Clone)] struct World { map: Vec>, } impl World { fn from_string(input: &str) -> (Self, Transform) { let mut start = Transform::new(0, 0, Direction::North); let map = input .lines() .enumerate() .map(|(y, line)| { if let Some(x) = line.find('^') { start = Transform::new(x, y, Direction::North); } line.chars() .map(|c| { if c == '#' { Terrain::Blocked } else { Terrain::Free } }) .collect() }) .collect(); (Self { map }, start) } fn terrain_at(&self, at: &Transform) -> Option { self.map.get(at.y)?.get(at.x).copied() } } #[derive(PartialEq, Eq, Copy, Clone)] enum Terrain { Free, Blocked, } #[derive(PartialEq, Eq, Copy, Clone)] enum Direction { North, South, East, West, }