use std::{collections::HashSet, time::Instant}; use nom::{ branch::alt, character::complete::{char, multispace1}, multi::{many1, separated_list1}, IResult, Parser, }; #[derive(Debug, PartialEq, Eq)] enum Tile { Start, Empty, Wall, Passed, } fn parse(input: &str) -> IResult<&str, Vec>> { let (input, result) = separated_list1( multispace1, many1(alt((char('.'), char('#'), char('^'))).map(|c| match c { '.' => Tile::Empty, '#' => Tile::Wall, '^' => Tile::Start, _ => unreachable!(), })), )(input)?; Ok((input, result)) } fn proc( height: usize, width: usize, dir: &(i32, i32), pos: &(usize, usize), ) -> Option<(usize, usize)> { let x = pos.0 as i32 + dir.0; let y = pos.1 as i32 + dir.1; if x < 0 || y < 0 || x >= width as i32 || y >= height as i32 { None } else { Some((x as usize, y as usize)) } } fn turn(dir: (i32, i32)) -> (i32, i32) { match dir { (0, -1) => (1, 0), (1, 0) => (0, 1), (0, 1) => (-1, 0), (-1, 0) => (0, -1), _ => unreachable!(), } } fn solve_1(input: &str) -> usize { use Tile::*; let (_, mut tiles) = parse(input).unwrap(); let mut pos = (0, 0); for (y, row_of_tiles) in tiles.iter().enumerate() { for (x, tile) in row_of_tiles.iter().enumerate() { if let Start = tile { pos = (x, y); break; } } } let h = tiles.len(); let w = tiles[0].len(); let mut dir = (0, -1); loop { tiles[pos.1][pos.0] = Passed; match proc(h, w, &dir, &pos) { None => break, Some(mut p) => { while tiles[p.1][p.0] == Wall { dir = turn(dir); p = proc(h, w, &dir, &pos).unwrap(); } pos = p; } } } tiles.into_iter().fold(0, |sum, row_of_tiles| { sum + row_of_tiles .into_iter() .filter(|tile| *tile == Passed) .count() }) } fn solve_2(input: &str) -> usize { use Tile::*; let (_, mut tiles) = parse(input).unwrap(); let mut pos = (0, 0); for (y, row_of_tiles) in tiles.iter().enumerate() { for (x, tile) in row_of_tiles.iter().enumerate() { if let Start = tile { pos = (x, y); break; } } } let start = pos; let h = tiles.len(); let w = tiles[0].len(); let mut result = 0; for y in 0..h { for x in 0..w { match tiles[y][x] { Wall => continue, Start => continue, _ => tiles[y][x] = Wall, } pos = start; let mut visited = HashSet::new(); let mut dir = (0, -1); loop { if visited.contains(&(pos, dir)) { result += 1; break; } visited.insert((pos, dir)); match proc(h, w, &dir, &pos) { None => break, Some(mut p) => { while tiles[p.1][p.0] == Wall { dir = turn(dir); p = proc(h, w, &dir, &pos).unwrap(); } pos = p; } } } tiles[y][x] = Empty; } } result } fn main() { println!("Hello, this is Patrick!"); let now = Instant::now(); let input = include_str!("../input.txt"); let result_1 = solve_1(input); println!("The number of walked tiles is {}", result_1); let result_2 = solve_2(input); println!("The number of possible obstructions is {}", result_2); println!("Time passed: {:?}", Instant::now() - now); } #[cfg(test)] mod tests { use super::*; #[test] fn test_1() { let test_input = include_str!("../test.txt"); assert_eq!(solve_1(test_input), 41); } #[test] fn test_2() { let test_input = include_str!("../test.txt"); assert_eq!(solve_2(test_input), 6); } }