use advent_of_code::{ include_aoc, strings::{convert_to_array, line_to_char}, KartesianDirection, }; use advent_of_code::{Kartesian, Table}; use log::debug; use num::Integer; include_aoc!(2024, 06); #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct DirectionalKartesian { x: T, y: T, dir: KartesianDirection, } impl From<(Kartesian, KartesianDirection)> for DirectionalKartesian { fn from(value: (Kartesian, KartesianDirection)) -> Self { DirectionalKartesian { x: value.0.x, y: value.0.y, dir: value.1, } } } impl Into<(Kartesian, KartesianDirection)> for &DirectionalKartesian { fn into(self) -> (Kartesian, KartesianDirection) { ( Kartesian { x: self.x, y: self.y, }, self.dir, ) } } impl DirectionalKartesian { fn kartesian(self) -> Kartesian { Kartesian::new(self.x, self.y) } } fn find_guard(map: Table) -> Kartesian { let mut position = Kartesian::default(); for line in map { for char in line { if char == '^' { return position; } position.y += 1; } position.y = 0; position.x += 1; } unreachable!() } fn get_path(map: Table) -> Vec> { let mut position = (find_guard(map.clone()), KartesianDirection::Top); let maximum = Kartesian::new(map.len() as i32, map[0].len() as i32); debug!("Guard is on coordinate {}:{}", position.0.x + 1, position.0.y + 1); let mut passed = Vec::new(); passed.push(position.into()); loop { position.0 = match position.0.checked_add_max(position.1.vector().into(), maximum) { None => { debug!("Guard left the space after {} steps", passed.len()); return passed; }, Some(pos) => pos, }; if map[position.0.x as usize][position.0.y as usize] != '#' { passed.push(position.into()); continue; } position.0 -= position.1.vector(); debug!("Guard turned right on {}:{}", position.0.x + 1, position.0.y + 1); position.1 = position.1.clockwise(false); } } fn solver1(input: &str) -> usize { let map = convert_to_array::<_, _, '\n'>(input, line_to_char); let mut path = get_path(map); path.sort(); path.dedup_by_key(|p| (p.x, p.y)); path.len() } fn is_looping(_path: Vec>, _start_point: DirectionalKartesian) -> bool { false } #[allow(unused_variables, unused_mut)] fn solver2(input: &str) -> usize { let map = convert_to_array::<_, _, '\n'>(input, line_to_char); let maximum = Kartesian::new(map.len() as i32, map[0].len() as i32); let mut path = get_path(map.clone()); let mut p = path.iter(); let mut looppoints = Vec::new(); let startpoint = p.next().unwrap().kartesian(); let mut currentpoint: (Kartesian, KartesianDirection); for point in p { if point.kartesian() == startpoint { continue; } currentpoint = point.into(); currentpoint.1 = currentpoint.1.clockwise(false); loop { currentpoint.0 = match currentpoint.0.checked_add_max(currentpoint.1.vector(), maximum) { None => break, Some(p) => p, }; if path.contains(¤tpoint.into()) { looppoints.push(point.kartesian()); break; } if map[currentpoint.0.x as usize][currentpoint.0.y as usize] == '#' { currentpoint.0 -= currentpoint.1.vector(); currentpoint.1 = currentpoint.1.clockwise(false) } } } looppoints.dedup(); looppoints.len() } fn main() { println!("Guard left the space after walking through {} distinct positions", solver1(DATA)); println!("There are {} positions that are good for loops", solver2(DATA)); } #[cfg(test)] mod test { use super::*; include_aoc!(EXAMPLE, 2024, 06, true); #[test] fn test_solution_1() { assert_eq!(solver1(EXAMPLE), 41); } #[test] fn test_solution_2() { assert_eq!(solver2(EXAMPLE), 6); } }