use std::{ fs::File, io::{BufRead, BufReader}, path::Path, }; pub fn solve(input: &Path) -> anyhow::Result<()> { println!("part one: {}", part_one(input)?); println!("part two: {}", part_two(input)?); Ok(()) } #[derive(Default, Clone)] struct Pos { x: usize, y: usize, visited: bool, obstacle: bool, } #[derive(Clone, Debug)] struct Guard { x: usize, y: usize, direction: Direction, } impl Guard { fn turn(&mut self) { match self.direction { Direction::North => self.direction = Direction::East, Direction::East => self.direction = Direction::South, Direction::South => self.direction = Direction::West, Direction::West => self.direction = Direction::North, } } fn move_forward(&mut self) { match self.direction { Direction::North => self.y -= 1, Direction::East => self.x += 1, Direction::South => self.y += 1, Direction::West => self.x -= 1, } } } #[derive(Clone, Debug)] enum Direction { North, East, South, West, } fn part_one(input: &Path) -> anyhow::Result { let reader = BufReader::new(File::open(input)?); let mut map: Vec> = vec![]; let mut guard: Option = None; for (y, line) in reader.lines().enumerate() { let mut row = vec![]; for (x, c) in line?.char_indices() { match c { '.' => row.push(Pos { x, y, ..Default::default() }), '#' => row.push(Pos { x, y, obstacle: true, ..Default::default() }), '^' => { row.push(Pos { x, y, visited: true, ..Default::default() }); guard = Some(Guard { x, y, direction: Direction::North, }); } _ => panic!("invalid map"), } } map.push(row); } let mut guard = guard.unwrap(); loop { //print_map(&map); if let Some(in_front) = get_in_front(&guard, &mut map) { if in_front.obstacle { guard.turn(); } else { in_front.visited = true; guard.move_forward(); } } else { break; } //std::thread::sleep(std::time::Duration::from_millis(50)); } //print_map(&map); Ok(map.iter().flatten().filter(|p| p.visited).count() as i32) } fn get_in_front<'a>(guard: &'a Guard, map: &'a mut [Vec]) -> Option<&'a mut Pos> { match guard.direction { Direction::North => match map.get_mut(if guard.y == 0 { return None; } else { guard.y - 1 }) { Some(row) => row.get_mut(guard.x), None => None, }, Direction::East => match map.get_mut(guard.y) { Some(row) => row.get_mut(guard.x + 1), None => None, }, Direction::South => match map.get_mut(guard.y + 1) { Some(row) => row.get_mut(guard.x), None => None, }, Direction::West => match map.get_mut(guard.y) { Some(row) => row.get_mut(if guard.x == 0 { return None; } else { guard.x - 1 }), None => None, }, } } fn print_map(map: &[Vec]) { for row in map { for pos in row { if pos.obstacle { print!("# "); } else if pos.visited { print!("X "); } else { print!(". "); } } println!(); } } fn part_two(input: &Path) -> anyhow::Result { let reader = BufReader::new(File::open(input)?); let mut map: Vec> = vec![]; let mut guard: Option = None; for (y, line) in reader.lines().enumerate() { let mut row = vec![]; for (x, c) in line?.char_indices() { match c { '.' => row.push(Pos { x, y, ..Default::default() }), '#' => row.push(Pos { x, y, obstacle: true, ..Default::default() }), '^' => { row.push(Pos { x, y, visited: true, ..Default::default() }); guard = Some(Guard { x, y, direction: Direction::North, }); } _ => panic!("invalid map"), } } map.push(row); } let guard = guard.unwrap(); let mut block_positions = 0; for pos in map.iter().flatten() { let mut guard = guard.clone(); let mut modified_map = map.clone(); modified_map[pos.y][pos.x].obstacle = true; let start_time = std::time::Instant::now(); loop { if let Some(in_front) = get_in_front(&guard, &mut modified_map) { if in_front.obstacle { guard.turn(); } else { in_front.visited = true; guard.move_forward(); } } else { break; } if start_time.elapsed().as_millis() >= 1 { block_positions += 1; break; } } } Ok(block_positions) }