From 40b926347bb7345c477396ab4e892e4ed65e872a Mon Sep 17 00:00:00 2001 From: Quentin Boyer Date: Fri, 6 Dec 2024 22:52:53 +0100 Subject: [PATCH] Day 6 --- src/bin/day6.rs | 196 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/bin/day6.rs diff --git a/src/bin/day6.rs b/src/bin/day6.rs new file mode 100644 index 0000000..7998136 --- /dev/null +++ b/src/bin/day6.rs @@ -0,0 +1,196 @@ +use std::{collections::HashSet, time::Instant}; + +use aoc_2024::{load, print_res}; +use bstr::{BString, ByteSlice}; +use color_eyre::eyre::ContextCompat; + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub struct Point { + line: isize, + col: isize, +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum Tile { + Blocked, + Free, +} + +type Parsed = (Point, Vec>); + +#[inline(never)] +pub fn parsing(input: &BString) -> color_eyre::Result { + let mut guard = None; + let mut lines = Vec::new(); + for (l, line) in input.lines().enumerate() { + lines.push( + line.iter() + .enumerate() + .map(|(col, &c)| match c { + b'#' => Ok(Tile::Blocked), + b'.' => Ok(Tile::Free), + b'^' => { + color_eyre::eyre::ensure!(guard.is_none(), "Found two guards"); + guard = Some(Point { + line: l as isize, + col: col as isize, + }); + Ok(Tile::Free) + } + _ => Err(color_eyre::eyre::eyre!("Invalid char: `{c}`")), + }) + .collect::>()?, + ); + } + Ok((guard.with_context(|| "No guard found")?, lines)) +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +enum Direction { + Up, + Right, + Down, + Left, +} + +impl Direction { + pub fn turn_right(self) -> Self { + match self { + Self::Up => Self::Right, + Self::Right => Self::Down, + Self::Down => Self::Left, + Self::Left => Self::Up, + } + } +} + +impl Point { + fn walk(self, direction: Direction) -> Self { + let Self { line, col } = self; + match direction { + Direction::Up => Self { + line: line - 1, + col, + }, + Direction::Right => Self { col: col + 1, line }, + Direction::Down => Self { + line: line + 1, + col, + }, + Direction::Left => Self { col: col - 1, line }, + } + } +} + +fn visited_tiles(mut guard: Point, grid: &[Vec]) -> HashSet { + let mut direction = Direction::Up; + let mut visited = HashSet::new(); + + loop { + visited.insert(guard); + + let next = guard.walk(direction); + if next.line < 0 + || next.line as usize >= grid.len() + || next.col < 0 + || next.col as usize >= grid[0].len() + { + break; + } + + let tile = grid[next.line as usize][next.col as usize]; + match tile { + Tile::Blocked => { + direction = direction.turn_right(); + } + Tile::Free => { + guard = next; + } + } + } + + visited +} + +#[inline(never)] +pub fn part1((guard, grid): Parsed) { + let visited = visited_tiles(guard, &grid); + print_res!("Positions visited: {}", visited.len()); +} + +#[inline(never)] +pub fn part2((guard, mut grid): Parsed) { + let mut possible_blocks = 0; + let visited_no_blocks = visited_tiles(guard, &grid); + + for line in 0..grid.len() { + for col in 0..grid[0].len() { + if grid[line][col] == Tile::Blocked + || !visited_no_blocks.contains(&Point { + line: line as isize, + col: col as isize, + }) + { + continue; + } + + grid[line][col] = Tile::Blocked; + + let mut guard = guard; + let mut direction = Direction::Up; + let mut visited = HashSet::new(); + + loop { + if visited.contains(&(guard, direction)) { + possible_blocks += 1; + break; + } + visited.insert((guard, direction)); + + let next = guard.walk(direction); + if next.line < 0 + || next.line as usize >= grid.len() + || next.col < 0 + || next.col as usize >= grid[0].len() + { + break; + } + + let tile = grid[next.line as usize][next.col as usize]; + match tile { + Tile::Blocked => { + direction = direction.turn_right(); + } + Tile::Free => { + guard = next; + } + } + } + + grid[line][col] = Tile::Free; + } + } + + print_res!("Possible blocks to loop: {possible_blocks}"); +} + +pub fn main() -> color_eyre::Result<()> { + let context = load()?; + + let start = Instant::now(); + let parsed = parsing(&context.input)?; + let elapsed = humantime::format_duration(start.elapsed()); + + let start = Instant::now(); + if context.part == 1 { + part1(parsed); + } else { + part2(parsed); + } + let elapsed_part = humantime::format_duration(start.elapsed()); + + println!(" Parsing: {elapsed}"); + println!(" Solving: {elapsed_part}"); + + Ok(()) +}