diff --git a/Cargo.lock b/Cargo.lock index 0b394f7..32e1562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,13 @@ dependencies = [ "winnow", ] +[[package]] +name = "day06" +version = "0.1.0" +dependencies = [ + "winnow", +] + [[package]] name = "memchr" version = "2.7.4" diff --git a/Cargo.toml b/Cargo.toml index 766f8ff..8db5cad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["day01", "day02", "day03", "day04", "day05"] +members = ["day01", "day02", "day03", "day04", "day05", "day06"] [workspace.dependencies] winnow = "0.6" diff --git a/day06/Cargo.toml b/day06/Cargo.toml new file mode 100644 index 0000000..6840b30 --- /dev/null +++ b/day06/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "day06" +version = "0.1.0" +edition = "2024" + +[dependencies] +winnow.workspace = true diff --git a/day06/src/main.rs b/day06/src/main.rs new file mode 100644 index 0000000..e25a8e9 --- /dev/null +++ b/day06/src/main.rs @@ -0,0 +1,238 @@ +use std::{ + collections::HashSet, + fmt::{Display, Write}, +}; + +use winnow::{ + PResult, Parser, + ascii::{newline, till_line_ending}, + combinator::{alt, repeat, separated}, +}; + +fn main() { + let input = std::fs::read_to_string("input").unwrap(); + println!("{}", pt1(&input)); + println!("{}", pt2(&input)); +} + +fn pt1(input: &str) -> usize { + let mut board = Board::new(input); + + board.run() +} + +fn pt2(input: &str) -> usize { + 0 +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +enum Dir { + #[default] + N, + S, + E, + W, +} + +impl Dir { + fn next(&self, loc: Loc) -> Loc { + let Loc(r, c) = loc; + let (nr, nc) = match self { + Dir::N => (r.wrapping_sub(1), c), + Dir::S => (r + 1, c), + Dir::E => (r, c + 1), + Dir::W => (r, c.wrapping_sub(1)), + }; + Loc(nr, nc) + } + + fn rot(&self) -> Self { + use Dir::*; + match self { + N => E, + E => S, + S => W, + W => N, + } + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +struct Loc(usize, usize); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Cell { + Obstacle, + Empty, + Guard(Dir), + Visited, +} + +impl Display for Cell { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Cell::*; + use Dir::*; + let c = match self { + Obstacle => '#', + Empty => '.', + Visited => 'X', + Guard(d) => match d { + N => '^', + E => '>', + S => 'v', + W => '<', + }, + }; + f.write_char(c) + } +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct Board { + cells: Vec>, + guard: (Loc, Dir), +} + +impl Display for Board { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str( + &self + .cells + .iter() + //.map(|row| row.iter().map(|c| format!("{c}")).collect::()) + .map(|row| { + row.iter().fold(String::new(), |mut out, c| { + let _ = write!(out, "{c}"); + out + }) + }) + .collect::>() + .join("\n"), + ) + } +} + +impl Board { + fn new(input: &str) -> Self { + parse(input).unwrap() + } + + fn step(&mut self) -> Option { + let (loc, dir) = &mut self.guard; + *self + .cells + .get_mut(loc.0) + .and_then(|r| r.get_mut(loc.1)) + .unwrap() = Cell::Visited; + let nloc = dir.next(*loc); + if let Some(next_cell) = self + .cells + .get_mut(nloc.0) + .and_then(|row| row.get_mut(nloc.1)) + { + match next_cell { + Cell::Obstacle => { + *dir = dir.rot(); + Some(*loc) + } + Cell::Empty | Cell::Visited => { + *next_cell = Cell::Guard(*dir); + *loc = nloc; + Some(nloc) + } + _ => unreachable!(), + } + } else { + None + } + } + + fn run(&mut self) -> usize { + let mut locs = HashSet::new(); + locs.insert(self.guard.0); + while let Some(loc) = self.step() { + locs.insert(loc); + } + locs.len() + } +} + +fn parse_cell(input: &mut &str) -> PResult { + alt((parse_guard, parse_floor)).parse_next(input) +} + +fn parse_guard(input: &mut &str) -> PResult { + let g = alt(('^', '>', 'v', '<')).parse_next(input)?; + use Cell::Guard; + use Dir::*; + match g { + '^' => Ok(Guard(N)), + '>' => Ok(Guard(E)), + 'v' | 'V' => Ok(Guard(S)), + '<' => Ok(Guard(W)), + _ => unreachable!(), + } +} + +fn parse_floor(input: &mut &str) -> PResult { + let g = alt(('.', '#')).parse_next(input)?; + match g { + '.' => Ok(Cell::Empty), + '#' => Ok(Cell::Obstacle), + 'X' => Ok(Cell::Visited), + _ => unreachable!(), + } +} + +fn parse_line(input: &mut &str) -> PResult> { + let mut line = till_line_ending.parse_next(input)?; + repeat(1.., parse_cell).parse_next(&mut line) +} + +fn parse(input: &str) -> PResult { + let mut gdir = Dir::N; + let mut loc = Loc(0, 0); + + let mut input = input; + let cells: Vec> = separated(1.., parse_line, newline) + .parse_next(&mut input) + .unwrap(); + + for (row, line) in cells.iter().enumerate() { + for (col, cell) in line.iter().enumerate() { + if let Cell::Guard(d) = cell { + gdir = *d; + loc = Loc(row, col); + } + } + } + + Ok(Board { + cells, + guard: (loc, gdir), + }) +} + +#[cfg(test)] +mod test { + use super::*; + static INPUT: &str = "....#..... +.........# +.......... +..#....... +.......#.. +.......... +.#..^..... +........#. +#......... +......#..."; + + #[test] + fn p1() { + let v = pt1(INPUT); + assert_eq!(v, 41) + } + + #[test] + fn p2() {} +}