better Pos

This commit is contained in:
Rüdiger Ludwig 2023-08-07 05:50:26 +02:00
parent bcefb1b68f
commit 651ccb9cba
11 changed files with 268 additions and 318 deletions

View file

@ -3,25 +3,25 @@ use std::fmt::Display;
use num_traits::{Num, NumAssignOps}; use num_traits::{Num, NumAssignOps};
use super::pos::Pos; use super::pos::Pos2D;
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct Area<T> pub struct Area<T>
where where
T: Num, T: Num,
{ {
lower_left: Pos<T>, lower_left: Pos2D<T>,
upper_right: Pos<T>, upper_right: Pos2D<T>,
} }
impl<T> Area<T> impl<T> Area<T>
where where
T: Num + Ord + Copy, T: Num + Ord + Copy,
{ {
pub fn new(p1: Pos<T>, p2: Pos<T>) -> Area<T> { pub fn new(p1: Pos2D<T>, p2: Pos2D<T>) -> Area<T> {
Area { Area {
lower_left: p1.min_components(&p2), lower_left: p1.min_components(p2),
upper_right: p1.max_components(&p2), upper_right: p1.max_components(p2),
} }
} }
} }
@ -30,25 +30,25 @@ impl<T> Area<T>
where where
T: Num + Ord + Copy, T: Num + Ord + Copy,
{ {
pub fn extend(&self, pos: Pos<T>) -> Area<T> { pub fn extend(&self, pos: Pos2D<T>) -> Area<T> {
if self.contains(pos) { if self.contains(pos) {
return *self; return *self;
} }
Area { Area {
lower_left: self.lower_left.min_components(&pos), lower_left: self.lower_left.min_components(pos),
upper_right: self.upper_right.max_components(&pos), upper_right: self.upper_right.max_components(pos),
} }
} }
pub fn get_lower_left(&self) -> Pos<T> { pub fn get_lower_left(&self) -> Pos2D<T> {
self.lower_left self.lower_left
} }
pub fn get_upper_right(&self) -> Pos<T> { pub fn get_upper_right(&self) -> Pos2D<T> {
self.upper_right self.upper_right
} }
pub fn contains(&self, pos: Pos<T>) -> bool { pub fn contains(&self, pos: Pos2D<T>) -> bool {
self.lower_left.x() >= pos.x() self.lower_left.x() >= pos.x()
&& pos.x() >= self.upper_right.x() && pos.x() >= self.upper_right.x()
&& self.lower_left.y() >= pos.y() && self.lower_left.y() >= pos.y()
@ -62,11 +62,11 @@ where
{ {
pub fn from_iterator<I>(mut iter: I) -> Option<Self> pub fn from_iterator<I>(mut iter: I) -> Option<Self>
where where
I: Iterator<Item = &'a Pos<T>>, I: Iterator<Item = &'a Pos2D<T>>,
{ {
let first = *iter.next()?; let first = *iter.next()?;
let (upper, lower) = iter.fold((first, first), |(mx, mn), p| { 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)) Some(Area::new(lower, upper))
@ -214,12 +214,12 @@ impl<'a, T> Iterator for ColIterator<'a, T>
where where
T: Num + Ord + NumAssignOps + Copy, T: Num + Ord + NumAssignOps + Copy,
{ {
type Item = Pos<T>; type Item = Pos2D<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if (self.ascending && self.col <= self.area.upper_right.x()) if (self.ascending && self.col <= self.area.upper_right.x())
|| (!self.ascending && self.col >= self.area.lower_left.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 { if self.ascending {
self.col += T::one(); self.col += T::one();
} else { } else {
@ -266,13 +266,13 @@ impl<'a, T> Iterator for CellIterator<'a, T>
where where
T: Num + Ord + NumAssignOps + Copy, T: Num + Ord + NumAssignOps + Copy,
{ {
type Item = Pos<T>; type Item = Pos2D<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if (self.ascending && self.row <= self.area.upper_right.y()) if (self.ascending && self.row <= self.area.upper_right.y())
|| (!self.ascending && self.row >= self.area.lower_left.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 { if self.ascending {
self.col += T::one(); self.col += T::one();
if self.col > self.area.upper_right.x() { if self.col > self.area.upper_right.x() {
@ -300,18 +300,18 @@ mod test {
#[test] #[test]
fn test_cell_iterator() { 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::<Vec<_>>(); let result = area.cells(true).collect::<Vec<_>>();
let expected = vec![ let expected = vec![
Pos::new(-1, -1), Pos2D::new(-1, -1),
Pos::new(0, -1), Pos2D::new(0, -1),
Pos::new(1, -1), Pos2D::new(1, -1),
Pos::new(-1, 0), Pos2D::new(-1, 0),
Pos::new(0, 0), Pos2D::new(0, 0),
Pos::new(1, 0), Pos2D::new(1, 0),
Pos::new(-1, 1), Pos2D::new(-1, 1),
Pos::new(0, 1), Pos2D::new(0, 1),
Pos::new(1, 1), Pos2D::new(1, 1),
]; ];
assert_eq!(result, expected); assert_eq!(result, expected);
} }

View file

@ -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 std::{fmt::Display, ops::Add};
use Direction::*; use Direction::*;
use Turn::*; use Turn::*;
@ -13,15 +13,6 @@ pub enum Direction {
} }
impl Direction { impl Direction {
pub fn as_pos(&self) -> Pos<i32> {
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 { pub fn is_perpendicular(&self, other: &Direction) -> bool {
match *self { match *self {
East => *other != East && *other != West, East => *other != East && *other != West,
@ -91,35 +82,49 @@ impl Display for Direction {
} }
} }
impl Add<Pos<i32>> for Direction { impl<T> From<Direction> for Pos2D<T>
type Output = Pos<i32>; where
T: Num + Signed,
fn add(self, rhs: Pos<i32>) -> Self::Output { {
Pos::add(rhs, self) 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<Pos<i32>> for &Direction { impl Add<Pos2D<i32>> for Direction {
type Output = Pos<i32>; type Output = Pos2D<i32>;
fn add(self, rhs: Pos<i32>) -> Self::Output { fn add(self, rhs: Pos2D<i32>) -> Self::Output {
Pos::add(rhs, *self) Pos2D::add(rhs, self)
} }
} }
impl Add<&Pos<i32>> for Direction { impl Add<Pos2D<i32>> for &Direction {
type Output = Pos<i32>; type Output = Pos2D<i32>;
fn add(self, rhs: &Pos<i32>) -> Self::Output { fn add(self, rhs: Pos2D<i32>) -> Self::Output {
Pos::add(*rhs, self) Pos2D::add(rhs, *self)
} }
} }
impl Add<&Pos<i32>> for &Direction { impl Add<&Pos2D<i32>> for Direction {
type Output = Pos<i32>; type Output = Pos2D<i32>;
fn add(self, rhs: &Pos<i32>) -> Self::Output { fn add(self, rhs: &Pos2D<i32>) -> Self::Output {
Pos::add(*rhs, *self) Pos2D::add(*rhs, self)
}
}
impl Add<&Pos2D<i32>> for &Direction {
type Output = Pos2D<i32>;
fn add(self, rhs: &Pos2D<i32>) -> Self::Output {
Pos2D::add(*rhs, *self)
} }
} }

View file

@ -27,20 +27,24 @@ where
a a
} }
pub fn gcd<T>(a: T, b: T) -> T pub fn gcd<T>(a: T, b: T) -> Option<T>
where where
T: Num + Ord + Copy, T: Num + Ord + Copy,
{ {
assert!(a >= T::zero()); assert!(a >= T::zero());
assert!(b >= T::zero()); assert!(b >= T::zero());
if a.is_zero() { if a.is_zero() {
b if b.is_zero() {
} else if b.is_zero() { None
a } else {
Some(b)
}
} else { } else {
non_zero_gcd(a, b) Some(non_zero_gcd(a, b))
} }
} }
pub fn lcm<T>(a: T, b: T) -> T pub fn lcm<T>(a: T, b: T) -> T
where where
T: Num + Ord + Copy, T: Num + Ord + Copy,
@ -77,9 +81,9 @@ mod tests {
#[test] #[test]
fn some_simple_gcd() { fn some_simple_gcd() {
assert_eq!(5, gcd(10, 15)); assert_eq!(Some(5), gcd(10, 15));
assert_eq!(7, gcd(21, 49)); assert_eq!(Some(7), gcd(21, 49));
assert_eq!(1, gcd(13, 17)); assert_eq!(Some(1), gcd(13, 17));
} }
#[test] #[test]

View file

@ -1,7 +1,7 @@
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::complete::tag, bytes::complete::tag,
character::complete::{line_ending, space0, space1}, character::complete::{line_ending, space0, space1, u32},
combinator::{eof, opt, value}, combinator::{eof, opt, value},
error::ParseError, error::ParseError,
multi::many0, multi::many0,
@ -34,6 +34,10 @@ where
terminated(line, alt((line_ending, eof))) 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> { 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) alt((value(true, tag("true")), value(false, tag("false"))))(input)
} }

View file

@ -1,278 +1,202 @@
use super::direction::Direction;
use super::math::gcd; use super::math::gcd;
use num_traits::{Num, NumCast, Signed}; use num_traits::{Float, Num, NumCast, Signed};
use std::fmt; use std::fmt;
use std::ops::{Add, Mul, Sub}; use std::ops::{Add, Div, Mul, Sub};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Pos<T>(T, T) pub struct Pos2D<T>([T; 2]);
where
T: Num;
impl<T> Pos<T> impl<T> Pos2D<T> {
where #[inline]
T: Num, pub fn new(x: T, y: T) -> Pos2D<T> {
{ Pos2D([x, y])
pub fn new(x: T, y: T) -> Pos<T> { }
Pos(x, y)
pub fn get_x(&self) -> &T {
&self.0[0]
}
pub fn get_y(&self) -> &T {
&self.0[1]
} }
} }
impl<T> Pos<T> impl<T: Copy + Default> From<&[T]> for Pos2D<T> {
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<T> From<[T; 2]> for Pos2D<T> {
fn from(value: [T; 2]) -> Self {
Pos2D(value)
}
}
impl<T> From<(T, T)> for Pos2D<T> {
fn from(value: (T, T)) -> Self {
Pos2D([value.0, value.1])
}
}
impl<T> Pos2D<T>
where where
T: Num + Copy, T: Copy,
{ {
pub fn splat(v: T) -> Pos<T> { pub fn splat(v: T) -> Pos2D<T> {
Pos(v, v) Pos2D::new(v, v)
} }
pub fn x(&self) -> T { pub fn x(&self) -> T {
self.0 self.0[0]
} }
pub fn y(&self) -> T { pub fn y(&self) -> T {
self.1 self.0[1]
} }
} }
impl<T> Pos<T> impl<T> Pos2D<T>
where where
T: Num + Ord + Copy, T: Num + Ord + Copy,
{ {
pub fn normalize(&self) -> (Pos<T>, T) { pub fn normalize(self) -> Result<(Pos2D<T>, T), Pos2D<T>> {
if self.0.is_zero() && self.1.is_zero() { if self.x().is_zero() && self.y().is_zero() {
(*self, T::one()) Err(self)
} else { } else {
let ggt = gcd(self.0, self.1); gcd(self.x(), self.y())
(Pos::new(self.0 / ggt, self.1 / ggt), ggt) .map(|ggt| (Pos2D::new(self.x() / ggt, self.y() / ggt), ggt))
.ok_or(self)
} }
} }
} }
impl<T> Pos<T> impl<T> Pos2D<T>
where
T: Float,
{
pub fn normal(self) -> Result<(Pos2D<T>, T), Pos2D<T>> {
let length = self.length();
if length == T::zero() {
Err(self)
} else {
Ok((self / length, length))
}
}
}
impl<T> Pos2D<T>
where where
T: Num + NumCast, T: Num + NumCast,
{ {
pub fn angle(&self) -> f64 { pub fn angle(&self) -> Option<f64> {
if let (Some(x), Some(y)) = (self.0.to_f64(), self.1.to_f64()) { if let (Some(x), Some(y)) = (self.get_x().to_f64(), self.get_y().to_f64()) {
y.atan2(x) Some(y.atan2(x))
} else { } else {
f64::NAN None
} }
} }
pub fn angle2(&self) -> f64 { pub fn angle2(&self) -> Option<f64> {
if let (Some(x), Some(y)) = (self.0.to_f64(), self.1.to_f64()) { if let (Some(x), Some(y)) = (self.get_x().to_f64(), self.get_y().to_f64()) {
(-x.atan2(-y) + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI) Some((-x.atan2(-y) + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI))
} else { } else {
f64::NAN None
} }
} }
} }
impl<T> Pos<T> impl<T> Pos2D<T>
where where
T: Num + Ord + Copy, T: Ord + Copy,
{ {
pub fn max_components(&self, other: &Pos<T>) -> Self { pub fn max_components(self, other: Pos2D<T>) -> Self {
Self(self.0.max(other.0), self.1.max(other.1)) Pos2D::new(self.x().max(other.x()), self.y().max(other.y()))
} }
pub fn min_components(&self, other: &Pos<T>) -> Self { pub fn min_components(self, other: Pos2D<T>) -> Self {
Self(self.0.min(other.0), self.1.min(other.1)) Pos2D::new(self.x().min(other.x()), self.y().min(other.y()))
} }
} }
impl<T> Pos<T> impl<T> Pos2D<T>
where where
T: Num + Signed, T: Signed,
{ {
pub fn abs(&self) -> T { pub fn abs(self) -> T {
self.0.abs() + self.1.abs() self.get_x().abs() + self.get_y().abs()
} }
} }
impl<T> fmt::Display for Pos<T> impl<T> Pos2D<T>
where
T: Float,
{
pub fn length(self) -> T {
(self.get_x().powi(2) + self.get_y().powi(2)).sqrt()
}
}
impl<T> fmt::Display for Pos2D<T>
where where
T: Num + fmt::Display, T: Num + fmt::Display,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.0, self.1) write!(f, "({}, {})", self.get_x(), self.get_y())
} }
} }
impl<T> Add for Pos<T> impl<'a, T, P: Into<Pos2D<T>>> Add<P> for Pos2D<T>
where where
T: Num, T: Num + Copy,
{ {
type Output = Self; type Output = Self;
fn add(self, rhs: Self) -> Self::Output { fn add(self, rhs: P) -> Self::Output {
Pos(self.0 + rhs.0, self.1 + rhs.1) let rhs = rhs.into();
Pos2D::new(self.x() + rhs.x(), self.y() + rhs.y())
} }
} }
impl<T> Add for &Pos<T> impl<T, P: Into<Pos2D<T>>> Sub<P> for Pos2D<T>
where where
T: Num + Copy, T: Num + Copy,
{ {
type Output = <Pos<T> as Add<Pos<T>>>::Output; type Output = Pos2D<T>;
fn sub(self, rhs: P) -> Self::Output {
fn add(self, rhs: Self) -> Self::Output { let rhs = rhs.into();
Pos::add(*self, *rhs) Pos2D::new(self.x() - rhs.x(), self.y() - rhs.y())
} }
} }
impl<T> Add<&Pos<T>> for Pos<T> impl<T> Mul<T> for Pos2D<T>
where
T: Num + Copy,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: &Self) -> Self::Output {
Pos::add(self, *rhs)
}
}
impl<T> Add<Pos<T>> for &Pos<T>
where
T: Num + Copy,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: Pos<T>) -> Self::Output {
Pos::add(*self, rhs)
}
}
impl<T> Add<(T, T)> for Pos<T>
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<Direction> for Pos<i32> {
type Output = Self;
fn add(self, rhs: Direction) -> Self::Output {
Pos::add(self, rhs.as_pos())
}
}
impl Add<&Direction> for Pos<i32> {
type Output = Self;
fn add(self, rhs: &Direction) -> Self::Output {
Pos::add(self, rhs.as_pos())
}
}
impl Add<&Direction> for &Pos<i32> {
type Output = Pos<i32>;
fn add(self, rhs: &Direction) -> Self::Output {
Pos::add(*self, rhs.as_pos())
}
}
impl Add<Direction> for &Pos<i32> {
type Output = Pos<i32>;
fn add(self, rhs: Direction) -> Self::Output {
Pos::add(*self, rhs.as_pos())
}
}
impl<T> Sub for Pos<T>
where
T: Num,
{
type Output = Pos<T>;
fn sub(self, rhs: Self) -> Self::Output {
Pos(self.0 - rhs.0, self.1 - rhs.1)
}
}
impl<T> Sub<&Self> for Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn sub(self, rhs: &Self) -> Self::Output {
Pos::sub(self, *rhs)
}
}
impl<T> Sub for &Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn sub(self, rhs: &Pos<T>) -> Self::Output {
Pos::sub(*self, *rhs)
}
}
impl<T> Sub<Pos<T>> for &Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn sub(self, rhs: Pos<T>) -> Self::Output {
Pos::sub(*self, rhs)
}
}
impl<T> Mul<T> for Pos<T>
where where
T: Num + Copy, T: Num + Copy,
{ {
type Output = Self; type Output = Self;
fn mul(self, rhs: T) -> Self::Output { fn mul(self, rhs: T) -> Self::Output {
Pos(self.0 * rhs, self.1 * rhs) Pos2D::new(self.x() * rhs, self.y() * rhs)
} }
} }
impl<T> Mul<T> for &Pos<T> impl<T> Div<T> for Pos2D<T>
where where
T: Num + Copy, T: Num + Copy,
{ {
type Output = Pos<T>; type Output = Self;
fn mul(self, rhs: T) -> Self::Output { fn div(self, rhs: T) -> Self::Output {
Pos::mul(*self, rhs) Pos2D::new(self.x() / rhs, self.y() / rhs)
} }
} }
impl<T> Mul<&T> for Pos<T> impl<T> Pos2D<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn mul(self, rhs: &T) -> Self::Output {
Pos::mul(self, *rhs)
}
}
impl<T> Mul<&T> for &Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn mul(self, rhs: &T) -> Self::Output {
Pos::mul(*self, *rhs)
}
}
impl<T> Pos<T>
where where
T: Num + Signed + Copy, T: Num + Signed + Copy,
{ {
pub fn taxicab_origin(&self) -> T { pub fn taxicab_between(self, other: Pos2D<T>) -> T {
self.0.abs() + self.1.abs() (self - other).abs()
}
pub fn taxicab(&self, other: &Pos<T>) -> T {
(self.0 - other.0).abs() + (self.1 - other.1).abs()
} }
} }

View file

@ -1,5 +1,5 @@
use super::template::{DayTrait, ResultType}; 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 itertools::Itertools;
use std::{collections::HashSet, num::ParseIntError}; use std::{collections::HashSet, num::ParseIntError};
use thiserror::Error; use thiserror::Error;
@ -63,27 +63,27 @@ fn parse_line(line: &str) -> Result<(Direction, usize), RopeError> {
} }
} }
fn get_closer(first: Pos<i32>, second: Pos<i32>) -> Option<Pos<i32>> { fn get_closer(first: Pos2D<i32>, second: Pos2D<i32>) -> Option<Pos2D<i32>> {
let diff = second - first; let diff = second - first;
if diff.x().abs() <= 1 && diff.y().abs() <= 1 { if diff.x().abs() <= 1 && diff.y().abs() <= 1 {
None None
} else { } else {
Some(Pos::new(diff.x().signum(), diff.y().signum())) Some(Pos2D::new(diff.x().signum(), diff.y().signum()))
} }
} }
#[derive(Debug)] #[derive(Debug)]
struct Rope { struct Rope {
head: Pos<i32>, head: Pos2D<i32>,
knots: Vec<Pos<i32>>, knots: Vec<Pos2D<i32>>,
} }
impl Rope { impl Rope {
pub fn create(length: usize) -> Self { pub fn create(length: usize) -> Self {
assert!(length >= 2); assert!(length >= 2);
Rope { Rope {
head: Pos::new(0, 0), head: Pos2D::new(0, 0),
knots: vec![Pos::new(0, 0); length - 1], knots: vec![Pos2D::new(0, 0); length - 1],
} }
} }

View file

@ -1,5 +1,5 @@
use super::template::{DayTrait, ResultType}; 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 std::collections::{BinaryHeap, HashSet};
use thiserror::Error; use thiserror::Error;
@ -43,7 +43,7 @@ enum ValleyError {
struct Path { struct Path {
length: usize, length: usize,
height: char, height: char,
pos: Pos<usize>, pos: Pos2D<usize>,
} }
impl PartialOrd for Path { impl PartialOrd for Path {
@ -76,7 +76,7 @@ impl Ord for Path {
} }
impl Path { impl Path {
pub fn new(length: usize, height: char, pos: Pos<usize>) -> Self { pub fn new(length: usize, height: char, pos: Pos2D<usize>) -> Self {
Path { Path {
length, length,
height, height,
@ -104,28 +104,28 @@ impl<'a> Neighbors<'a> {
} }
} }
fn next_pos(&mut self) -> Option<Pos<usize>> { fn next_pos(&mut self) -> Option<Pos2D<usize>> {
while self.state < 4 { while self.state < 4 {
self.state += 1; self.state += 1;
match self.state { match self.state {
1 => { 1 => {
if self.path.pos.x() < self.valley.width() - 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 => { 2 => {
if self.path.pos.y() > 0 { 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 => { 3 => {
if self.path.pos.x() > 0 { 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 => { 4 => {
if self.path.pos.y() < self.valley.height() - 1 { 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 { struct Valley {
map: Vec<Vec<char>>, map: Vec<Vec<char>>,
start: Pos<usize>, start: Pos2D<usize>,
exit: Pos<usize>, exit: Pos2D<usize>,
width: usize, width: usize,
} }
@ -170,11 +170,11 @@ impl TryFrom<&str> for Valley {
for (x, height_char) in row.chars().enumerate() { for (x, height_char) in row.chars().enumerate() {
match height_char { match height_char {
'S' => { 'S' => {
start = Some(Pos::new(x, y)); start = Some(Pos2D::new(x, y));
height_row.push('a') height_row.push('a')
} }
'E' => { 'E' => {
exit = Some(Pos::new(x, y)); exit = Some(Pos2D::new(x, y));
height_row.push('z') height_row.push('z')
} }
'a'..='z' => height_row.push(height_char), 'a'..='z' => height_row.push(height_char),
@ -209,13 +209,13 @@ impl TryFrom<&str> for Valley {
} }
impl Valley { impl Valley {
fn get_height(&self, pos: Pos<usize>) -> char { fn get_height(&self, pos: Pos2D<usize>) -> char {
self.map[pos.y()][pos.x()] self.map[pos.y()][pos.x()]
} }
fn do_walk<F>(&self, check: F) -> Result<usize, ValleyError> fn do_walk<F>(&self, check: F) -> Result<usize, ValleyError>
where where
F: Fn(Pos<usize>) -> bool, F: Fn(Pos2D<usize>) -> bool,
{ {
let mut shortest = HashSet::with_capacity(self.width * self.map.len()); let mut shortest = HashSet::with_capacity(self.width * self.map.len());
let mut queue = BinaryHeap::new(); let mut queue = BinaryHeap::new();
@ -267,8 +267,8 @@ mod test {
let lines = read_string(day.get_day_number(), "example01.txt")?; let lines = read_string(day.get_day_number(), "example01.txt")?;
let valley = Valley::try_from(lines.as_str())?; let valley = Valley::try_from(lines.as_str())?;
assert_eq!(valley.width, 8); assert_eq!(valley.width, 8);
assert_eq!(valley.start, Pos::new(0, 0)); assert_eq!(valley.start, Pos2D::new(0, 0));
assert_eq!(valley.exit, Pos::new(5, 2)); assert_eq!(valley.exit, Pos2D::new(5, 2));
Ok(()) Ok(())
} }

View file

@ -1,5 +1,5 @@
use super::template::{DayTrait, ResultType}; 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 itertools::Itertools;
use std::{collections::HashSet, num::ParseIntError}; use std::{collections::HashSet, num::ParseIntError};
use thiserror::Error; use thiserror::Error;
@ -54,7 +54,7 @@ impl TryFrom<&str> for Cave {
fn try_from(lines: &str) -> Result<Self, Self::Error> { fn try_from(lines: &str) -> Result<Self, Self::Error> {
let lines = split_lines(lines); let lines = split_lines(lines);
let mut cave: HashSet<Pos<i32>> = HashSet::new(); let mut cave: HashSet<Pos2D<i32>> = HashSet::new();
for line in lines { for line in lines {
cave.extend(Cave::parse_one(line)?.iter()); cave.extend(Cave::parse_one(line)?.iter());
} }
@ -90,7 +90,7 @@ impl TryFrom<&str> for Cave {
} }
impl Cave { impl Cave {
fn get(&self, map: &[Vec<bool>], pos: Pos<i32>) -> bool { fn get(&self, map: &[Vec<bool>], pos: Pos2D<i32>) -> bool {
if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x { if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x {
false false
} else { } else {
@ -98,28 +98,32 @@ impl Cave {
} }
} }
fn set(&self, map: &mut [Vec<bool>], pos: Pos<i32>) { fn set(&self, map: &mut [Vec<bool>], pos: Pos2D<i32>) {
map[pos.y() as usize][(pos.x() - self.min_x) as usize] = true map[pos.y() as usize][(pos.x() - self.min_x) as usize] = true
} }
fn walk(from: Pos<i32>, to: Pos<i32>) -> Result<Vec<Pos<i32>>, CaveError> { fn walk(from: Pos2D<i32>, to: Pos2D<i32>) -> Result<Vec<Pos2D<i32>>, CaveError> {
if from == to { if from == to {
return Ok(vec![from]); return Ok(vec![from]);
} }
if from.x() == to.x() { if from.x() == to.x() {
if from.y() < to.y() { 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 { } else {
Ok(((to.y() + 1)..=from.y()) Ok(((to.y() + 1)..=from.y())
.map(|y| Pos::new(from.x(), y)) .map(|y| Pos2D::new(from.x(), y))
.collect()) .collect())
} }
} else if from.y() == to.y() { } else if from.y() == to.y() {
if from.x() < to.x() { 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 { } else {
Ok(((to.x() + 1)..=from.x()) Ok(((to.x() + 1)..=from.x())
.map(|x| Pos::new(x, from.y())) .map(|x| Pos2D::new(x, from.y()))
.collect()) .collect())
} }
} else { } else {
@ -127,14 +131,14 @@ impl Cave {
} }
} }
fn parse_one(line: &str) -> Result<HashSet<Pos<i32>>, CaveError> { fn parse_one(line: &str) -> Result<HashSet<Pos2D<i32>>, CaveError> {
let corners = line let corners = line
.split("->") .split("->")
.map(|coord| match coord.split(',').collect::<Vec<_>>()[..] { .map(|coord| match coord.split(',').collect::<Vec<_>>()[..] {
[first, second] => { [first, second] => {
let x: i32 = first.trim().parse()?; let x: i32 = first.trim().parse()?;
let y = second.trim().parse()?; let y = second.trim().parse()?;
Ok(Pos::new(x, y)) Ok(Pos2D::new(x, y))
} }
_ => Err(CaveError::NotAValidPos(coord.to_owned())), _ => Err(CaveError::NotAValidPos(coord.to_owned())),
}) })
@ -171,8 +175,8 @@ impl Cave {
drops drops
} }
fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos<i32>> { fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos2D<i32>> {
let mut drop = Pos::new(500, 0); let mut drop = Pos2D::new(500, 0);
loop { loop {
let next_y = drop.y() + 1; let next_y = drop.y() + 1;
if next_y > self.height { if next_y > self.height {
@ -180,7 +184,7 @@ impl Cave {
} }
let mut stuck = true; let mut stuck = true;
for dx in [0, -1, 1] { 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) { if !self.get(map, next) {
drop = next; drop = next;
stuck = false; stuck = false;
@ -252,7 +256,7 @@ mod test {
#[test] #[test]
fn test_parse() -> Result<()> { fn test_parse() -> Result<()> {
let input = "498,4 -> 498,6 -> 496,6"; 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)?; let result = Cave::parse_one(input)?;
assert_eq!(result, expected); assert_eq!(result, expected);

View file

@ -1,7 +1,7 @@
use super::template::{DayTrait, ResultType}; use super::template::{DayTrait, ResultType};
use crate::common::{ use crate::common::{
parser::{eol_terminated, extract_result, ignore, trim0}, parser::{eol_terminated, extract_result, ignore, trim0},
pos::Pos, pos::Pos2D,
}; };
use itertools::Itertools; use itertools::Itertools;
use nom::{ use nom::{
@ -44,7 +44,7 @@ impl DayTrait for Day {
.iter() .iter()
.tuple_combinations() .tuple_combinations()
.filter_map(|(first, second)| first.cross(second)) .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() .dedup()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -58,7 +58,7 @@ impl DayTrait for Day {
} }
impl Day { impl Day {
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> { fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos2D<i64>>), SensorError> {
let data = extract_result(many0(eol_terminated(Sensor::parse)))(lines)?; let data = extract_result(many0(eol_terminated(Sensor::parse)))(lines)?;
let mut sensors = HashSet::new(); let mut sensors = HashSet::new();
@ -70,7 +70,11 @@ impl Day {
Ok((sensors, beacons)) Ok((sensors, beacons))
} }
fn count_coverage_at(sensors: &HashSet<Sensor>, beacons: &HashSet<Pos<i64>>, row: i64) -> i64 { fn count_coverage_at(
sensors: &HashSet<Sensor>,
beacons: &HashSet<Pos2D<i64>>,
row: i64,
) -> i64 {
let ranges = sensors let ranges = sensors
.iter() .iter()
.filter_map(|sensor| sensor.range_at(row)) .filter_map(|sensor| sensor.range_at(row))
@ -117,7 +121,7 @@ impl From<Err<Error<&str>>> for SensorError {
#[derive(Debug)] #[derive(Debug)]
struct Line { struct Line {
start: Pos<i64>, start: Pos2D<i64>,
is_up: bool, is_up: bool,
steps: i64, steps: i64,
} }
@ -139,16 +143,16 @@ impl Line {
if one.pos.y() < two.pos.y() { if one.pos.y() < two.pos.y() {
is_up = true; is_up = true;
if one.pos.y() + one.radius <= two.pos.y() { 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 { } 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 { } else {
is_up = false; is_up = false;
if one.pos.y() - one.radius >= two.pos.y() { 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 { } 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(); 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<Pos<i64>> { fn cross(&self, other: &Line) -> Option<Pos2D<i64>> {
if self.is_up == other.is_up { if self.is_up == other.is_up {
return None; return None;
} }
@ -181,7 +185,7 @@ impl Line {
return None; return None;
} }
let pos = top_down.start + Pos::splat(r); let pos = top_down.start + Pos2D::splat(r);
Some(pos) Some(pos)
} }
} }
@ -227,7 +231,7 @@ impl Range {
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
struct Sensor { struct Sensor {
pos: Pos<i64>, pos: Pos2D<i64>,
radius: i64, radius: i64,
} }
@ -239,21 +243,21 @@ impl Sensor {
} }
} }
fn parse_pos(input: &str) -> IResult<&str, Pos<i64>> { fn parse_pos(input: &str) -> IResult<&str, Pos2D<i64>> {
let (input, (x, y)) = separated_pair( let (input, (x, y)) = separated_pair(
Sensor::component("x"), Sensor::component("x"),
trim0(char(',')), trim0(char(',')),
Sensor::component("y"), Sensor::component("y"),
)(input)?; )(input)?;
Ok((input, Pos::new(x, y))) Ok((input, Pos2D::new(x, y)))
} }
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos<i64>)> { pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos2D<i64>)> {
let input = ignore(tag("Sensor at"))(input)?; let input = ignore(tag("Sensor at"))(input)?;
let (input, pos) = trim0(Sensor::parse_pos)(input)?; let (input, pos) = trim0(Sensor::parse_pos)(input)?;
let input = ignore(tag(": closest beacon is at"))(input)?; let input = ignore(tag(": closest beacon is at"))(input)?;
let (input, beacon) = trim0(Sensor::parse_pos)(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))) Ok((input, (Sensor { pos, radius }, beacon)))
} }
@ -268,12 +272,12 @@ impl Sensor {
} }
pub fn border_distance(&self, other: &Sensor) -> i64 { 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 distance - self.radius - other.radius
} }
pub fn contains(&self, pos: &Pos<i64>) -> bool { pub fn contains(&self, pos: Pos2D<i64>) -> bool {
self.pos.taxicab(pos) <= self.radius 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 input = "Sensor at x=2, y=18: closest beacon is at x=-2, y=15";
let expected = ( let expected = (
Sensor { Sensor {
pos: Pos::new(2, 18), pos: Pos2D::new(2, 18),
radius: 7, radius: 7,
}, },
Pos::new(-2, 15), Pos2D::new(-2, 15),
); );
let result = extract_result(Sensor::parse)(input)?; let result = extract_result(Sensor::parse)(input)?;
assert_eq!(result, expected); assert_eq!(result, expected);
@ -301,7 +305,7 @@ mod test {
#[test] #[test]
fn test_width() { fn test_width() {
let sensor = Sensor { let sensor = Sensor {
pos: Pos::new(8, 7), pos: Pos2D::new(8, 7),
radius: 9, radius: 9,
}; };
assert_eq!(sensor.range_at(17), None); assert_eq!(sensor.range_at(17), None);

View file

@ -1,4 +1,5 @@
use super::template::{DayTrait, ResultType}; use super::template::{DayTrait, ResultType};
use nom::{error::Error, Err, IResult, Parser};
use thiserror::Error; use thiserror::Error;
const DAY_NUMBER: usize = todo!(); const DAY_NUMBER: usize = todo!();
@ -21,8 +22,14 @@ impl DayTrait for Day {
#[derive(Debug, Error)] #[derive(Debug, Error)]
enum DayError { enum DayError {
#[error("Dummy")] #[error("Not a valid description: {0}")]
Dummy, ParsingError(String),
}
impl From<Err<Error<&str>>> for DayError {
fn from(error: Err<Error<&str>>) -> Self {
DayError::ParsingError(error.to_string())
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1,5 +1,3 @@
#![feature(get_many_mut)]
mod common; mod common;
mod days; mod days;
mod macros; mod macros;