From 651ccb9cba08057cac8fae213aa672fe98f937ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Ludwig?= Date: Mon, 7 Aug 2023 05:50:26 +0200 Subject: [PATCH] better Pos --- src/common/area.rs | 56 ++++---- src/common/direction.rs | 61 ++++---- src/common/math.rs | 20 +-- src/common/parser.rs | 6 +- src/common/pos.rs | 302 +++++++++++++++------------------------- src/days/day09/mod.rs | 14 +- src/days/day12/mod.rs | 32 ++--- src/days/day14/mod.rs | 34 +++-- src/days/day15/mod.rs | 48 ++++--- src/days/day__/mod.rs | 11 +- src/main.rs | 2 - 11 files changed, 268 insertions(+), 318 deletions(-) diff --git a/src/common/area.rs b/src/common/area.rs index dbda7b1..af1cbe4 100644 --- a/src/common/area.rs +++ b/src/common/area.rs @@ -3,25 +3,25 @@ use std::fmt::Display; use num_traits::{Num, NumAssignOps}; -use super::pos::Pos; +use super::pos::Pos2D; #[derive(Debug, Clone, Copy, Default)] pub struct Area where T: Num, { - lower_left: Pos, - upper_right: Pos, + lower_left: Pos2D, + upper_right: Pos2D, } impl Area where T: Num + Ord + Copy, { - pub fn new(p1: Pos, p2: Pos) -> Area { + pub fn new(p1: Pos2D, p2: Pos2D) -> Area { Area { - lower_left: p1.min_components(&p2), - upper_right: p1.max_components(&p2), + lower_left: p1.min_components(p2), + upper_right: p1.max_components(p2), } } } @@ -30,25 +30,25 @@ impl Area where T: Num + Ord + Copy, { - pub fn extend(&self, pos: Pos) -> Area { + pub fn extend(&self, pos: Pos2D) -> Area { if self.contains(pos) { return *self; } Area { - lower_left: self.lower_left.min_components(&pos), - upper_right: self.upper_right.max_components(&pos), + lower_left: self.lower_left.min_components(pos), + upper_right: self.upper_right.max_components(pos), } } - pub fn get_lower_left(&self) -> Pos { + pub fn get_lower_left(&self) -> Pos2D { self.lower_left } - pub fn get_upper_right(&self) -> Pos { + pub fn get_upper_right(&self) -> Pos2D { self.upper_right } - pub fn contains(&self, pos: Pos) -> bool { + pub fn contains(&self, pos: Pos2D) -> bool { self.lower_left.x() >= pos.x() && pos.x() >= self.upper_right.x() && self.lower_left.y() >= pos.y() @@ -62,11 +62,11 @@ where { pub fn from_iterator(mut iter: I) -> Option where - I: Iterator>, + I: Iterator>, { let first = *iter.next()?; let (upper, lower) = iter.fold((first, first), |(mx, mn), p| { - (mx.max_components(p), mn.min_components(p)) + (mx.max_components(*p), mn.min_components(*p)) }); Some(Area::new(lower, upper)) @@ -214,12 +214,12 @@ impl<'a, T> Iterator for ColIterator<'a, T> where T: Num + Ord + NumAssignOps + Copy, { - type Item = Pos; + type Item = Pos2D; fn next(&mut self) -> Option { if (self.ascending && self.col <= self.area.upper_right.x()) || (!self.ascending && self.col >= self.area.lower_left.x()) { - let pos = Pos::new(self.col, self.row); + let pos = Pos2D::new(self.col, self.row); if self.ascending { self.col += T::one(); } else { @@ -266,13 +266,13 @@ impl<'a, T> Iterator for CellIterator<'a, T> where T: Num + Ord + NumAssignOps + Copy, { - type Item = Pos; + type Item = Pos2D; fn next(&mut self) -> Option { if (self.ascending && self.row <= self.area.upper_right.y()) || (!self.ascending && self.row >= self.area.lower_left.y()) { - let pos = Pos::new(self.col, self.row); + let pos = Pos2D::new(self.col, self.row); if self.ascending { self.col += T::one(); if self.col > self.area.upper_right.x() { @@ -300,18 +300,18 @@ mod test { #[test] fn test_cell_iterator() { - let area = Area::new(Pos::new(-1, -1), Pos::new(1, 1)); + let area = Area::new(Pos2D::new(-1, -1), Pos2D::new(1, 1)); let result = area.cells(true).collect::>(); let expected = vec![ - Pos::new(-1, -1), - Pos::new(0, -1), - Pos::new(1, -1), - Pos::new(-1, 0), - Pos::new(0, 0), - Pos::new(1, 0), - Pos::new(-1, 1), - Pos::new(0, 1), - Pos::new(1, 1), + Pos2D::new(-1, -1), + Pos2D::new(0, -1), + Pos2D::new(1, -1), + Pos2D::new(-1, 0), + Pos2D::new(0, 0), + Pos2D::new(1, 0), + Pos2D::new(-1, 1), + Pos2D::new(0, 1), + Pos2D::new(1, 1), ]; assert_eq!(result, expected); } diff --git a/src/common/direction.rs b/src/common/direction.rs index cd14a27..77c2e00 100644 --- a/src/common/direction.rs +++ b/src/common/direction.rs @@ -1,6 +1,6 @@ -use super::{pos::Pos, turn::Turn}; +use super::{pos::Pos2D, turn::Turn}; +use num_traits::{Num, Signed}; use std::{fmt::Display, ops::Add}; - use Direction::*; use Turn::*; @@ -13,15 +13,6 @@ pub enum Direction { } impl Direction { - pub fn as_pos(&self) -> Pos { - match *self { - East => Pos::new(1, 0), - North => Pos::new(0, 1), - West => Pos::new(-1, 0), - South => Pos::new(0, -1), - } - } - pub fn is_perpendicular(&self, other: &Direction) -> bool { match *self { East => *other != East && *other != West, @@ -91,35 +82,49 @@ impl Display for Direction { } } -impl Add> for Direction { - type Output = Pos; - - fn add(self, rhs: Pos) -> Self::Output { - Pos::add(rhs, self) +impl From for Pos2D +where + T: Num + Signed, +{ + fn from(value: Direction) -> Self { + match value { + East => Pos2D::new(T::one(), T::zero()), + North => Pos2D::new(T::zero(), T::one()), + West => Pos2D::new(-T::one(), T::zero()), + South => Pos2D::new(T::zero(), -T::one()), + } } } -impl Add> for &Direction { - type Output = Pos; +impl Add> for Direction { + type Output = Pos2D; - fn add(self, rhs: Pos) -> Self::Output { - Pos::add(rhs, *self) + fn add(self, rhs: Pos2D) -> Self::Output { + Pos2D::add(rhs, self) } } -impl Add<&Pos> for Direction { - type Output = Pos; +impl Add> for &Direction { + type Output = Pos2D; - fn add(self, rhs: &Pos) -> Self::Output { - Pos::add(*rhs, self) + fn add(self, rhs: Pos2D) -> Self::Output { + Pos2D::add(rhs, *self) } } -impl Add<&Pos> for &Direction { - type Output = Pos; +impl Add<&Pos2D> for Direction { + type Output = Pos2D; - fn add(self, rhs: &Pos) -> Self::Output { - Pos::add(*rhs, *self) + fn add(self, rhs: &Pos2D) -> Self::Output { + Pos2D::add(*rhs, self) + } +} + +impl Add<&Pos2D> for &Direction { + type Output = Pos2D; + + fn add(self, rhs: &Pos2D) -> Self::Output { + Pos2D::add(*rhs, *self) } } diff --git a/src/common/math.rs b/src/common/math.rs index 6b22811..fccb9df 100644 --- a/src/common/math.rs +++ b/src/common/math.rs @@ -27,20 +27,24 @@ where a } -pub fn gcd(a: T, b: T) -> T +pub fn gcd(a: T, b: T) -> Option where T: Num + Ord + Copy, { assert!(a >= T::zero()); assert!(b >= T::zero()); + if a.is_zero() { - b - } else if b.is_zero() { - a + if b.is_zero() { + None + } else { + Some(b) + } } else { - non_zero_gcd(a, b) + Some(non_zero_gcd(a, b)) } } + pub fn lcm(a: T, b: T) -> T where T: Num + Ord + Copy, @@ -77,9 +81,9 @@ mod tests { #[test] fn some_simple_gcd() { - assert_eq!(5, gcd(10, 15)); - assert_eq!(7, gcd(21, 49)); - assert_eq!(1, gcd(13, 17)); + assert_eq!(Some(5), gcd(10, 15)); + assert_eq!(Some(7), gcd(21, 49)); + assert_eq!(Some(1), gcd(13, 17)); } #[test] diff --git a/src/common/parser.rs b/src/common/parser.rs index fa6b022..1b66fed 100644 --- a/src/common/parser.rs +++ b/src/common/parser.rs @@ -1,7 +1,7 @@ use nom::{ branch::alt, bytes::complete::tag, - character::complete::{line_ending, space0, space1}, + character::complete::{line_ending, space0, space1, u32}, combinator::{eof, opt, value}, error::ParseError, multi::many0, @@ -34,6 +34,10 @@ where terminated(line, alt((line_ending, eof))) } +pub fn usize<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, usize, E> { + u32.map(|v| v as usize).parse(input) +} + pub fn true_false<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> { alt((value(true, tag("true")), value(false, tag("false"))))(input) } diff --git a/src/common/pos.rs b/src/common/pos.rs index 5e692a2..ddc51fe 100644 --- a/src/common/pos.rs +++ b/src/common/pos.rs @@ -1,278 +1,202 @@ -use super::direction::Direction; use super::math::gcd; -use num_traits::{Num, NumCast, Signed}; +use num_traits::{Float, Num, NumCast, Signed}; use std::fmt; -use std::ops::{Add, Mul, Sub}; +use std::ops::{Add, Div, Mul, Sub}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -pub struct Pos(T, T) -where - T: Num; +pub struct Pos2D([T; 2]); -impl Pos -where - T: Num, -{ - pub fn new(x: T, y: T) -> Pos { - Pos(x, y) +impl Pos2D { + #[inline] + pub fn new(x: T, y: T) -> Pos2D { + Pos2D([x, y]) + } + + pub fn get_x(&self) -> &T { + &self.0[0] + } + + pub fn get_y(&self) -> &T { + &self.0[1] } } -impl Pos +impl From<&[T]> for Pos2D { + fn from(value: &[T]) -> Self { + match value.len() { + 0 => Pos2D::new(T::default(), T::default()), + 1 => Pos2D::new(value[0], T::default()), + _ => Pos2D::new(value[0], value[1]), + } + } +} + +impl From<[T; 2]> for Pos2D { + fn from(value: [T; 2]) -> Self { + Pos2D(value) + } +} + +impl From<(T, T)> for Pos2D { + fn from(value: (T, T)) -> Self { + Pos2D([value.0, value.1]) + } +} + +impl Pos2D where - T: Num + Copy, + T: Copy, { - pub fn splat(v: T) -> Pos { - Pos(v, v) + pub fn splat(v: T) -> Pos2D { + Pos2D::new(v, v) } pub fn x(&self) -> T { - self.0 + self.0[0] } pub fn y(&self) -> T { - self.1 + self.0[1] } } -impl Pos +impl Pos2D where T: Num + Ord + Copy, { - pub fn normalize(&self) -> (Pos, T) { - if self.0.is_zero() && self.1.is_zero() { - (*self, T::one()) + pub fn normalize(self) -> Result<(Pos2D, T), Pos2D> { + if self.x().is_zero() && self.y().is_zero() { + Err(self) } else { - let ggt = gcd(self.0, self.1); - (Pos::new(self.0 / ggt, self.1 / ggt), ggt) + gcd(self.x(), self.y()) + .map(|ggt| (Pos2D::new(self.x() / ggt, self.y() / ggt), ggt)) + .ok_or(self) } } } -impl Pos +impl Pos2D +where + T: Float, +{ + pub fn normal(self) -> Result<(Pos2D, T), Pos2D> { + let length = self.length(); + if length == T::zero() { + Err(self) + } else { + Ok((self / length, length)) + } + } +} + +impl Pos2D where T: Num + NumCast, { - pub fn angle(&self) -> f64 { - if let (Some(x), Some(y)) = (self.0.to_f64(), self.1.to_f64()) { - y.atan2(x) + pub fn angle(&self) -> Option { + if let (Some(x), Some(y)) = (self.get_x().to_f64(), self.get_y().to_f64()) { + Some(y.atan2(x)) } else { - f64::NAN + None } } - pub fn angle2(&self) -> f64 { - if let (Some(x), Some(y)) = (self.0.to_f64(), self.1.to_f64()) { - (-x.atan2(-y) + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI) + pub fn angle2(&self) -> Option { + if let (Some(x), Some(y)) = (self.get_x().to_f64(), self.get_y().to_f64()) { + Some((-x.atan2(-y) + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI)) } else { - f64::NAN + None } } } -impl Pos +impl Pos2D where - T: Num + Ord + Copy, + T: Ord + Copy, { - pub fn max_components(&self, other: &Pos) -> Self { - Self(self.0.max(other.0), self.1.max(other.1)) + pub fn max_components(self, other: Pos2D) -> Self { + Pos2D::new(self.x().max(other.x()), self.y().max(other.y())) } - pub fn min_components(&self, other: &Pos) -> Self { - Self(self.0.min(other.0), self.1.min(other.1)) + pub fn min_components(self, other: Pos2D) -> Self { + Pos2D::new(self.x().min(other.x()), self.y().min(other.y())) } } -impl Pos +impl Pos2D where - T: Num + Signed, + T: Signed, { - pub fn abs(&self) -> T { - self.0.abs() + self.1.abs() + pub fn abs(self) -> T { + self.get_x().abs() + self.get_y().abs() } } -impl fmt::Display for Pos +impl Pos2D +where + T: Float, +{ + pub fn length(self) -> T { + (self.get_x().powi(2) + self.get_y().powi(2)).sqrt() + } +} + +impl fmt::Display for Pos2D where T: Num + fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}, {})", self.0, self.1) + write!(f, "({}, {})", self.get_x(), self.get_y()) } } -impl Add for Pos +impl<'a, T, P: Into>> Add

for Pos2D where - T: Num, + T: Num + Copy, { type Output = Self; - fn add(self, rhs: Self) -> Self::Output { - Pos(self.0 + rhs.0, self.1 + rhs.1) + fn add(self, rhs: P) -> Self::Output { + let rhs = rhs.into(); + Pos2D::new(self.x() + rhs.x(), self.y() + rhs.y()) } } -impl Add for &Pos +impl>> Sub

for Pos2D where T: Num + Copy, { - type Output = as Add>>::Output; - - fn add(self, rhs: Self) -> Self::Output { - Pos::add(*self, *rhs) + type Output = Pos2D; + fn sub(self, rhs: P) -> Self::Output { + let rhs = rhs.into(); + Pos2D::new(self.x() - rhs.x(), self.y() - rhs.y()) } } -impl Add<&Pos> for Pos -where - T: Num + Copy, -{ - type Output = as Add>>::Output; - fn add(self, rhs: &Self) -> Self::Output { - Pos::add(self, *rhs) - } -} - -impl Add> for &Pos -where - T: Num + Copy, -{ - type Output = as Add>>::Output; - fn add(self, rhs: Pos) -> Self::Output { - Pos::add(*self, rhs) - } -} - -impl Add<(T, T)> for Pos -where - T: Num, -{ - type Output = Self; - fn add(self, rhs: (T, T)) -> Self::Output { - Pos(self.0 + rhs.0, self.1 + rhs.1) - } -} - -impl Add for Pos { - type Output = Self; - - fn add(self, rhs: Direction) -> Self::Output { - Pos::add(self, rhs.as_pos()) - } -} - -impl Add<&Direction> for Pos { - type Output = Self; - - fn add(self, rhs: &Direction) -> Self::Output { - Pos::add(self, rhs.as_pos()) - } -} - -impl Add<&Direction> for &Pos { - type Output = Pos; - - fn add(self, rhs: &Direction) -> Self::Output { - Pos::add(*self, rhs.as_pos()) - } -} - -impl Add for &Pos { - type Output = Pos; - - fn add(self, rhs: Direction) -> Self::Output { - Pos::add(*self, rhs.as_pos()) - } -} - -impl Sub for Pos -where - T: Num, -{ - type Output = Pos; - fn sub(self, rhs: Self) -> Self::Output { - Pos(self.0 - rhs.0, self.1 - rhs.1) - } -} - -impl Sub<&Self> for Pos -where - T: Num + Copy, -{ - type Output = Pos; - fn sub(self, rhs: &Self) -> Self::Output { - Pos::sub(self, *rhs) - } -} - -impl Sub for &Pos -where - T: Num + Copy, -{ - type Output = Pos; - fn sub(self, rhs: &Pos) -> Self::Output { - Pos::sub(*self, *rhs) - } -} - -impl Sub> for &Pos -where - T: Num + Copy, -{ - type Output = Pos; - fn sub(self, rhs: Pos) -> Self::Output { - Pos::sub(*self, rhs) - } -} - -impl Mul for Pos +impl Mul for Pos2D where T: Num + Copy, { type Output = Self; fn mul(self, rhs: T) -> Self::Output { - Pos(self.0 * rhs, self.1 * rhs) + Pos2D::new(self.x() * rhs, self.y() * rhs) } } -impl Mul for &Pos +impl Div for Pos2D where T: Num + Copy, { - type Output = Pos; - fn mul(self, rhs: T) -> Self::Output { - Pos::mul(*self, rhs) + type Output = Self; + fn div(self, rhs: T) -> Self::Output { + Pos2D::new(self.x() / rhs, self.y() / rhs) } } -impl Mul<&T> for Pos -where - T: Num + Copy, -{ - type Output = Pos; - fn mul(self, rhs: &T) -> Self::Output { - Pos::mul(self, *rhs) - } -} - -impl Mul<&T> for &Pos -where - T: Num + Copy, -{ - type Output = Pos; - fn mul(self, rhs: &T) -> Self::Output { - Pos::mul(*self, *rhs) - } -} - -impl Pos +impl Pos2D where T: Num + Signed + Copy, { - pub fn taxicab_origin(&self) -> T { - self.0.abs() + self.1.abs() - } - - pub fn taxicab(&self, other: &Pos) -> T { - (self.0 - other.0).abs() + (self.1 - other.1).abs() + pub fn taxicab_between(self, other: Pos2D) -> T { + (self - other).abs() } } diff --git a/src/days/day09/mod.rs b/src/days/day09/mod.rs index 7f11710..06011d6 100644 --- a/src/days/day09/mod.rs +++ b/src/days/day09/mod.rs @@ -1,5 +1,5 @@ use super::template::{DayTrait, ResultType}; -use crate::common::{direction::Direction, file::split_lines, pos::Pos}; +use crate::common::{direction::Direction, file::split_lines, pos::Pos2D}; use itertools::Itertools; use std::{collections::HashSet, num::ParseIntError}; use thiserror::Error; @@ -63,27 +63,27 @@ fn parse_line(line: &str) -> Result<(Direction, usize), RopeError> { } } -fn get_closer(first: Pos, second: Pos) -> Option> { +fn get_closer(first: Pos2D, second: Pos2D) -> Option> { let diff = second - first; if diff.x().abs() <= 1 && diff.y().abs() <= 1 { None } else { - Some(Pos::new(diff.x().signum(), diff.y().signum())) + Some(Pos2D::new(diff.x().signum(), diff.y().signum())) } } #[derive(Debug)] struct Rope { - head: Pos, - knots: Vec>, + head: Pos2D, + knots: Vec>, } impl Rope { pub fn create(length: usize) -> Self { assert!(length >= 2); Rope { - head: Pos::new(0, 0), - knots: vec![Pos::new(0, 0); length - 1], + head: Pos2D::new(0, 0), + knots: vec![Pos2D::new(0, 0); length - 1], } } diff --git a/src/days/day12/mod.rs b/src/days/day12/mod.rs index a861e81..e38383e 100644 --- a/src/days/day12/mod.rs +++ b/src/days/day12/mod.rs @@ -1,5 +1,5 @@ use super::template::{DayTrait, ResultType}; -use crate::common::{file::split_lines, pos::Pos}; +use crate::common::{file::split_lines, pos::Pos2D}; use std::collections::{BinaryHeap, HashSet}; use thiserror::Error; @@ -43,7 +43,7 @@ enum ValleyError { struct Path { length: usize, height: char, - pos: Pos, + pos: Pos2D, } impl PartialOrd for Path { @@ -76,7 +76,7 @@ impl Ord for Path { } impl Path { - pub fn new(length: usize, height: char, pos: Pos) -> Self { + pub fn new(length: usize, height: char, pos: Pos2D) -> Self { Path { length, height, @@ -104,28 +104,28 @@ impl<'a> Neighbors<'a> { } } - fn next_pos(&mut self) -> Option> { + fn next_pos(&mut self) -> Option> { while self.state < 4 { self.state += 1; match self.state { 1 => { if self.path.pos.x() < self.valley.width() - 1 { - return Some(Pos::new(self.path.pos.x() + 1, self.path.pos.y())); + return Some(Pos2D::new(self.path.pos.x() + 1, self.path.pos.y())); } } 2 => { if self.path.pos.y() > 0 { - return Some(Pos::new(self.path.pos.x(), self.path.pos.y() - 1)); + return Some(Pos2D::new(self.path.pos.x(), self.path.pos.y() - 1)); } } 3 => { if self.path.pos.x() > 0 { - return Some(Pos::new(self.path.pos.x() - 1, self.path.pos.y())); + return Some(Pos2D::new(self.path.pos.x() - 1, self.path.pos.y())); } } 4 => { if self.path.pos.y() < self.valley.height() - 1 { - return Some(Pos::new(self.path.pos.x(), self.path.pos.y() + 1)); + return Some(Pos2D::new(self.path.pos.x(), self.path.pos.y() + 1)); } } _ => {} @@ -151,8 +151,8 @@ impl Iterator for Neighbors<'_> { struct Valley { map: Vec>, - start: Pos, - exit: Pos, + start: Pos2D, + exit: Pos2D, width: usize, } @@ -170,11 +170,11 @@ impl TryFrom<&str> for Valley { for (x, height_char) in row.chars().enumerate() { match height_char { 'S' => { - start = Some(Pos::new(x, y)); + start = Some(Pos2D::new(x, y)); height_row.push('a') } 'E' => { - exit = Some(Pos::new(x, y)); + exit = Some(Pos2D::new(x, y)); height_row.push('z') } 'a'..='z' => height_row.push(height_char), @@ -209,13 +209,13 @@ impl TryFrom<&str> for Valley { } impl Valley { - fn get_height(&self, pos: Pos) -> char { + fn get_height(&self, pos: Pos2D) -> char { self.map[pos.y()][pos.x()] } fn do_walk(&self, check: F) -> Result where - F: Fn(Pos) -> bool, + F: Fn(Pos2D) -> bool, { let mut shortest = HashSet::with_capacity(self.width * self.map.len()); let mut queue = BinaryHeap::new(); @@ -267,8 +267,8 @@ mod test { let lines = read_string(day.get_day_number(), "example01.txt")?; let valley = Valley::try_from(lines.as_str())?; assert_eq!(valley.width, 8); - assert_eq!(valley.start, Pos::new(0, 0)); - assert_eq!(valley.exit, Pos::new(5, 2)); + assert_eq!(valley.start, Pos2D::new(0, 0)); + assert_eq!(valley.exit, Pos2D::new(5, 2)); Ok(()) } diff --git a/src/days/day14/mod.rs b/src/days/day14/mod.rs index e2b8e56..3b42429 100644 --- a/src/days/day14/mod.rs +++ b/src/days/day14/mod.rs @@ -1,5 +1,5 @@ use super::template::{DayTrait, ResultType}; -use crate::common::{file::split_lines, pos::Pos}; +use crate::common::{file::split_lines, pos::Pos2D}; use itertools::Itertools; use std::{collections::HashSet, num::ParseIntError}; use thiserror::Error; @@ -54,7 +54,7 @@ impl TryFrom<&str> for Cave { fn try_from(lines: &str) -> Result { let lines = split_lines(lines); - let mut cave: HashSet> = HashSet::new(); + let mut cave: HashSet> = HashSet::new(); for line in lines { cave.extend(Cave::parse_one(line)?.iter()); } @@ -90,7 +90,7 @@ impl TryFrom<&str> for Cave { } impl Cave { - fn get(&self, map: &[Vec], pos: Pos) -> bool { + fn get(&self, map: &[Vec], pos: Pos2D) -> bool { if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x { false } else { @@ -98,28 +98,32 @@ impl Cave { } } - fn set(&self, map: &mut [Vec], pos: Pos) { + fn set(&self, map: &mut [Vec], pos: Pos2D) { map[pos.y() as usize][(pos.x() - self.min_x) as usize] = true } - fn walk(from: Pos, to: Pos) -> Result>, CaveError> { + fn walk(from: Pos2D, to: Pos2D) -> Result>, CaveError> { if from == to { return Ok(vec![from]); } if from.x() == to.x() { if from.y() < to.y() { - Ok((from.y()..to.y()).map(|y| Pos::new(from.x(), y)).collect()) + Ok((from.y()..to.y()) + .map(|y| Pos2D::new(from.x(), y)) + .collect()) } else { Ok(((to.y() + 1)..=from.y()) - .map(|y| Pos::new(from.x(), y)) + .map(|y| Pos2D::new(from.x(), y)) .collect()) } } else if from.y() == to.y() { if from.x() < to.x() { - Ok((from.x()..to.x()).map(|x| Pos::new(x, from.y())).collect()) + Ok((from.x()..to.x()) + .map(|x| Pos2D::new(x, from.y())) + .collect()) } else { Ok(((to.x() + 1)..=from.x()) - .map(|x| Pos::new(x, from.y())) + .map(|x| Pos2D::new(x, from.y())) .collect()) } } else { @@ -127,14 +131,14 @@ impl Cave { } } - fn parse_one(line: &str) -> Result>, CaveError> { + fn parse_one(line: &str) -> Result>, CaveError> { let corners = line .split("->") .map(|coord| match coord.split(',').collect::>()[..] { [first, second] => { let x: i32 = first.trim().parse()?; let y = second.trim().parse()?; - Ok(Pos::new(x, y)) + Ok(Pos2D::new(x, y)) } _ => Err(CaveError::NotAValidPos(coord.to_owned())), }) @@ -171,8 +175,8 @@ impl Cave { drops } - fn drop_one(&self, map: &[Vec]) -> Option> { - let mut drop = Pos::new(500, 0); + fn drop_one(&self, map: &[Vec]) -> Option> { + let mut drop = Pos2D::new(500, 0); loop { let next_y = drop.y() + 1; if next_y > self.height { @@ -180,7 +184,7 @@ impl Cave { } let mut stuck = true; for dx in [0, -1, 1] { - let next = Pos::new(drop.x() + dx, next_y); + let next = Pos2D::new(drop.x() + dx, next_y); if !self.get(map, next) { drop = next; stuck = false; @@ -252,7 +256,7 @@ mod test { #[test] fn test_parse() -> Result<()> { let input = "498,4 -> 498,6 -> 496,6"; - let expected = hashset! {Pos::new(498, 4), Pos::new(498, 5), Pos::new(498, 6), Pos::new(497, 6), Pos::new(496, 6)}; + let expected = hashset! {Pos2D::new(498, 4), Pos2D::new(498, 5), Pos2D::new(498, 6), Pos2D::new(497, 6), Pos2D::new(496, 6)}; let result = Cave::parse_one(input)?; assert_eq!(result, expected); diff --git a/src/days/day15/mod.rs b/src/days/day15/mod.rs index 81e69f8..0875d94 100644 --- a/src/days/day15/mod.rs +++ b/src/days/day15/mod.rs @@ -1,7 +1,7 @@ use super::template::{DayTrait, ResultType}; use crate::common::{ parser::{eol_terminated, extract_result, ignore, trim0}, - pos::Pos, + pos::Pos2D, }; use itertools::Itertools; use nom::{ @@ -44,7 +44,7 @@ impl DayTrait for Day { .iter() .tuple_combinations() .filter_map(|(first, second)| first.cross(second)) - .filter(|pos| sensors.iter().all(|sensor| !sensor.contains(pos))) + .filter(|pos| sensors.iter().all(|sensor| !sensor.contains(*pos))) .dedup() .collect::>(); @@ -58,7 +58,7 @@ impl DayTrait for Day { } impl Day { - fn parse_all(lines: &str) -> Result<(HashSet, HashSet>), SensorError> { + fn parse_all(lines: &str) -> Result<(HashSet, HashSet>), SensorError> { let data = extract_result(many0(eol_terminated(Sensor::parse)))(lines)?; let mut sensors = HashSet::new(); @@ -70,7 +70,11 @@ impl Day { Ok((sensors, beacons)) } - fn count_coverage_at(sensors: &HashSet, beacons: &HashSet>, row: i64) -> i64 { + fn count_coverage_at( + sensors: &HashSet, + beacons: &HashSet>, + row: i64, + ) -> i64 { let ranges = sensors .iter() .filter_map(|sensor| sensor.range_at(row)) @@ -117,7 +121,7 @@ impl From>> for SensorError { #[derive(Debug)] struct Line { - start: Pos, + start: Pos2D, is_up: bool, steps: i64, } @@ -139,16 +143,16 @@ impl Line { if one.pos.y() < two.pos.y() { is_up = true; if one.pos.y() + one.radius <= two.pos.y() { - start = Pos::new(one.pos.x(), one.pos.y() + one.radius + 1); + start = Pos2D::new(one.pos.x(), one.pos.y() + one.radius + 1); } else { - start = Pos::new(two.pos.x() - two.radius - 1, two.pos.y()); + start = Pos2D::new(two.pos.x() - two.radius - 1, two.pos.y()); } } else { is_up = false; if one.pos.y() - one.radius >= two.pos.y() { - start = Pos::new(one.pos.x(), one.pos.y() - one.radius - 1); + start = Pos2D::new(one.pos.x(), one.pos.y() - one.radius - 1); } else { - start = Pos::new(two.pos.x() - two.radius - 1, two.pos.y()); + start = Pos2D::new(two.pos.x() - two.radius - 1, two.pos.y()); } } let steps = two.pos.x().min(one.pos.x() + one.radius) - start.x(); @@ -160,7 +164,7 @@ impl Line { }) } - fn cross(&self, other: &Line) -> Option> { + fn cross(&self, other: &Line) -> Option> { if self.is_up == other.is_up { return None; } @@ -181,7 +185,7 @@ impl Line { return None; } - let pos = top_down.start + Pos::splat(r); + let pos = top_down.start + Pos2D::splat(r); Some(pos) } } @@ -227,7 +231,7 @@ impl Range { #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] struct Sensor { - pos: Pos, + pos: Pos2D, radius: i64, } @@ -239,21 +243,21 @@ impl Sensor { } } - fn parse_pos(input: &str) -> IResult<&str, Pos> { + fn parse_pos(input: &str) -> IResult<&str, Pos2D> { let (input, (x, y)) = separated_pair( Sensor::component("x"), trim0(char(',')), Sensor::component("y"), )(input)?; - Ok((input, Pos::new(x, y))) + Ok((input, Pos2D::new(x, y))) } - pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos)> { + pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos2D)> { let input = ignore(tag("Sensor at"))(input)?; let (input, pos) = trim0(Sensor::parse_pos)(input)?; let input = ignore(tag(": closest beacon is at"))(input)?; let (input, beacon) = trim0(Sensor::parse_pos)(input)?; - let radius = pos.taxicab(&beacon); + let radius = pos.taxicab_between(beacon); Ok((input, (Sensor { pos, radius }, beacon))) } @@ -268,12 +272,12 @@ impl Sensor { } pub fn border_distance(&self, other: &Sensor) -> i64 { - let distance = self.pos.taxicab(&other.pos); + let distance = self.pos.taxicab_between(other.pos); distance - self.radius - other.radius } - pub fn contains(&self, pos: &Pos) -> bool { - self.pos.taxicab(pos) <= self.radius + pub fn contains(&self, pos: Pos2D) -> bool { + self.pos.taxicab_between(pos) <= self.radius } } @@ -287,10 +291,10 @@ mod test { let input = "Sensor at x=2, y=18: closest beacon is at x=-2, y=15"; let expected = ( Sensor { - pos: Pos::new(2, 18), + pos: Pos2D::new(2, 18), radius: 7, }, - Pos::new(-2, 15), + Pos2D::new(-2, 15), ); let result = extract_result(Sensor::parse)(input)?; assert_eq!(result, expected); @@ -301,7 +305,7 @@ mod test { #[test] fn test_width() { let sensor = Sensor { - pos: Pos::new(8, 7), + pos: Pos2D::new(8, 7), radius: 9, }; assert_eq!(sensor.range_at(17), None); diff --git a/src/days/day__/mod.rs b/src/days/day__/mod.rs index fe2759b..06ccf1b 100644 --- a/src/days/day__/mod.rs +++ b/src/days/day__/mod.rs @@ -1,4 +1,5 @@ use super::template::{DayTrait, ResultType}; +use nom::{error::Error, Err, IResult, Parser}; use thiserror::Error; const DAY_NUMBER: usize = todo!(); @@ -21,8 +22,14 @@ impl DayTrait for Day { #[derive(Debug, Error)] enum DayError { - #[error("Dummy")] - Dummy, + #[error("Not a valid description: {0}")] + ParsingError(String), +} + +impl From>> for DayError { + fn from(error: Err>) -> Self { + DayError::ParsingError(error.to_string()) + } } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index 2e16d26..07a8ec7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -#![feature(get_many_mut)] - mod common; mod days; mod macros;