diff --git a/src/bin/2024/06.rs b/src/bin/2024/06.rs new file mode 100644 index 0000000..359d968 --- /dev/null +++ b/src/bin/2024/06.rs @@ -0,0 +1,173 @@ +#[allow(unused_imports)] +use advent_of_code::{ + include_data, include_example, + strings::{convert_to_array, line_to_char}, + KartesianDirection, +}; +use advent_of_code::{Kartesian, Table}; +use log::debug; +use num::Integer; + +include_data!(DATA 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(), 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(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(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_example!(EXAMPLE 2024 06); + + #[test] + fn test_solution_1() { + assert_eq!(solver1(EXAMPLE), 41); + } + + #[test] + fn test_solution_2() { + assert_eq!(solver2(EXAMPLE), 6); + } +} diff --git a/src/lib.rs b/src/lib.rs index e961e27..8071e64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,8 @@ use num::*; pub mod strings; pub use advent_of_code_macros::*; +pub type Table = Vec>; + pub fn matrix(zero: bool, diag: bool, non_diag: bool) -> Vec<(i32, i32)> { const MIN: i32 = -1; const MAX: i32 = 1; @@ -47,7 +49,7 @@ impl Add for Kartesian { type Output = Kartesian; } -impl AddAssign for Kartesian { +impl AddAssign for Kartesian { fn add_assign(&mut self, rhs: Self) { self.x += rhs.x; self.y += rhs.y; @@ -65,7 +67,7 @@ impl Sub for Kartesian { type Output = Kartesian; } -impl SubAssign for Kartesian { +impl SubAssign for Kartesian { fn sub_assign(&mut self, rhs: Self) { self.x -= rhs.x; self.y -= rhs.y; @@ -78,6 +80,31 @@ impl Kartesian { } } +impl Kartesian { + pub fn checked_add_max(self, rhs: Kartesian, max: Kartesian) -> Option> { + let mut new = Kartesian::default(); + new.x = match self.x.checked_add(&rhs.x) { + None => return None, + Some(x) => { + if x < T::default() || x >= max.x { + return None; + } + x + } + }; + new.y = match self.y.checked_add(&rhs.y) { + None => return None, + Some(y) => { + if y < T::default() || y >= max.y { + return None; + } + y + } + }; + Some(new) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Euclidian where @@ -100,7 +127,7 @@ impl Add for Euclidian { type Output = Euclidian; } -impl AddAssign for Euclidian { +impl AddAssign for Euclidian { fn add_assign(&mut self, rhs: Self) { self.x += rhs.x; self.y += rhs.y; @@ -120,7 +147,7 @@ impl Sub for Euclidian { type Output = Euclidian; } -impl SubAssign for Euclidian { +impl SubAssign for Euclidian { fn sub_assign(&mut self, rhs: Self) { self.x -= rhs.x; self.y -= rhs.y; @@ -129,13 +156,11 @@ impl SubAssign for Euclidian { } impl Euclidian { - pub const fn new(x: T, y: T) -> Self { + pub const fn new(x: T, y: T, z: T) -> Self { Self { x: x, y: y, z: z } } } -pub type Table = Vec>; - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum KartesianDirection { TopLeft, @@ -167,4 +192,77 @@ impl KartesianDirection { }, } } + + pub fn clockwise(self, diagonal: bool) -> KartesianDirection { + match (self, diagonal) { + (KartesianDirection::TopLeft, _) => Self::Top, + (KartesianDirection::Top, true) => Self::TopRight, + (KartesianDirection::Top, false) => Self::Right, + (KartesianDirection::TopRight, _) => Self::Right, + (KartesianDirection::Left, true) => Self::TopLeft, + (KartesianDirection::Left, false) => Self::Top, + (KartesianDirection::None, _) => Self::None, + (KartesianDirection::Right, true) => Self::BottomRight, + (KartesianDirection::Right, false) => Self::Bottom, + (KartesianDirection::BottomLeft, _) => Self::Left, + (KartesianDirection::Bottom, true) => Self::BottomLeft, + (KartesianDirection::Bottom, false) => Self::Left, + (KartesianDirection::BottomRight, _) => Self::Bottom, + } + } + + pub fn anticlockwise(self, diagonal: bool) -> KartesianDirection { + match (self, diagonal) { + (KartesianDirection::TopLeft, _) => Self::Left, + (KartesianDirection::Top, true) => Self::TopLeft, + (KartesianDirection::Top, false) => Self::Left, + (KartesianDirection::TopRight, _) => Self::Top, + (KartesianDirection::Left, true) => Self::BottomLeft, + (KartesianDirection::Left, false) => Self::Bottom, + (KartesianDirection::None, _) => Self::None, + (KartesianDirection::Right, true) => Self::TopRight, + (KartesianDirection::Right, false) => Self::Top, + (KartesianDirection::BottomLeft, _) => Self::Bottom, + (KartesianDirection::Bottom, true) => Self::BottomRight, + (KartesianDirection::Bottom, false) => Self::Right, + (KartesianDirection::BottomRight, _) => Self::Right, + } + } +} + +#[test] +fn test_clockwise() { + let mut current = KartesianDirection::Top; + let clock = [ + KartesianDirection::Top, + KartesianDirection::TopRight, + KartesianDirection::Right, + KartesianDirection::BottomRight, + KartesianDirection::Bottom, + KartesianDirection::BottomLeft, + KartesianDirection::Left, + KartesianDirection::TopLeft, + ]; + for hand in clock { + assert_eq!(current, hand); + current = current.clockwise(true); + } +} +#[test] +fn test_anticlockwise() { + let mut current = KartesianDirection::Top; + let clock = [ + KartesianDirection::Top, + KartesianDirection::TopLeft, + KartesianDirection::Left, + KartesianDirection::BottomLeft, + KartesianDirection::Bottom, + KartesianDirection::BottomRight, + KartesianDirection::Right, + KartesianDirection::TopRight, + ]; + for hand in clock { + assert_eq!(current, hand); + current = current.anticlockwise(true); + } }