diff --git a/src/day6.rs b/src/day6.rs new file mode 100644 index 0000000..beefd73 --- /dev/null +++ b/src/day6.rs @@ -0,0 +1,182 @@ +pub fn day6() { + let input = crate::input(6); + + let (world, cursor_location) = World::from_string(&input); + let start = Transform::new( + cursor_location.0, + cursor_location.1, + Direction::North, + &world, + ); + + let mut path = traverse(start).unwrap(); + + path.sort_by(|a, b| { + if a.x == b.x { + a.y.cmp(&b.y) + } else { + a.x.cmp(&b.x) + } + }); + path.dedup_by(|a, b| a.x == b.x && a.y == b.y); + let total_traveled = path.len(); + + let loops = path + .iter() + .filter(|p| { + let mut new_world = world.clone(); + new_world.map[p.y][p.x] = Terrain::Blocked; + let start = Transform::new(start.x, start.y, start.direction, &new_world); + traverse(start).is_none() + }) + .count(); + + println!("Total Travelled: {}", total_traveled); + println!("Total Loopable Positions: {}", loops); +} + +#[derive(Copy, Clone)] +struct Transform<'a> { + x: usize, + y: usize, + direction: Direction, + world: &'a World, +} + +impl<'a> Transform<'a> { + fn new(x: usize, y: usize, direction: Direction, world: &'a World) -> Self { + Self { + x, + y, + direction, + world, + } + } + + fn turn(&self) -> Self { + Self { + x: self.x, + y: self.y, + direction: self.direction.turn(), + world: self.world, + } + } + + fn travel(&self) -> Option { + let t = match self.direction { + Direction::North => Some(Self { + x: self.x, + y: self.y.checked_sub(1)?, + direction: self.direction, + world: self.world, + }), + Direction::South => Some(Self { + x: self.x, + y: self.y.checked_add(1)?, + direction: self.direction, + world: self.world, + }), + Direction::East => Some(Self { + x: self.x.checked_add(1)?, + y: self.y, + direction: self.direction, + world: self.world, + }), + Direction::West => Some(Self { + x: self.x.checked_sub(1)?, + y: self.y, + direction: self.direction, + world: self.world, + }), + }?; + + if t.y >= t.world.map.len() || t.x >= t.world.map[0].len() { + return None; + }; + + if self.world.terrain_at(&t) == Terrain::Blocked { + Some(self.turn()) + } else { + Some(t) + } + } +} + +#[derive(Clone)] +struct World { + map: Vec>, +} + +impl World { + fn from_string(input: &str) -> (Self, (usize, usize)) { + let mut map: Vec> = Vec::new(); + let mut cursor: (usize, usize) = (0, 0); + for (y, line) in input.lines().enumerate() { + let row: Vec = line + .chars() + .map(|c| match c { + '#' => Terrain::Blocked, + _ => Terrain::Free, + }) + .collect(); + map.push(row); + if let Some(x) = line.find('^') { + cursor = (x, y); + }; + } + (Self { map }, cursor) + } + + fn terrain_at(&self, at: &Transform) -> Terrain { + self.map[at.y][at.x] + } +} + +fn traverse<'a>(start: Transform<'a>) -> Option>> { + let mut cursor = start; + let mut traversed: Vec = vec![cursor]; + while let Some(new_cursor) = cursor.travel() { + if traversed.contains(&new_cursor) { + return None; + } + cursor = new_cursor; + traversed.push(cursor); + } + Some(traversed) +} + +#[derive(PartialEq, Eq, Copy, Clone)] +enum Terrain { + Free, + Blocked, +} + +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +enum Direction { + North, + South, + East, + West, +} + +impl Direction { + fn turn(&self) -> Self { + match self { + Self::North => Self::East, + Self::East => Self::South, + Self::South => Self::West, + Self::West => Self::North, + } + } +} + +impl<'a> PartialEq for Transform<'a> { + fn eq(&self, other: &Self) -> bool { + self.x == other.x + && self.y == other.y + && self.direction == other.direction + && std::ptr::eq(self.world, other.world) + } +} + +impl<'a> Eq for Transform<'a> {} diff --git a/src/main.rs b/src/main.rs index 434db46..28a2dbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,10 @@ mod day2; mod day3; mod day4; mod day5; +mod day6; fn main() { - day5::day5(); + day6::day6(); } pub fn input(day: u8) -> String {