day22 finished
This commit is contained in:
parent
88be9a39a0
commit
80d724a6f6
11 changed files with 927 additions and 305 deletions
|
|
@ -3,22 +3,22 @@ use std::fmt::Display;
|
||||||
|
|
||||||
use num_traits::{Num, NumAssignOps};
|
use num_traits::{Num, NumAssignOps};
|
||||||
|
|
||||||
use super::pos::Pos2D;
|
use super::pos2::Pos2;
|
||||||
|
|
||||||
#[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: Pos2D<T>,
|
lower_left: Pos2<T>,
|
||||||
upper_right: Pos2D<T>,
|
upper_right: Pos2<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Area<T>
|
impl<T> Area<T>
|
||||||
where
|
where
|
||||||
T: Num + Ord + Copy,
|
T: Num + Ord + Copy,
|
||||||
{
|
{
|
||||||
pub fn new(p1: Pos2D<T>, p2: Pos2D<T>) -> Area<T> {
|
pub fn new(p1: Pos2<T>, p2: Pos2<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,7 +30,7 @@ impl<T> Area<T>
|
||||||
where
|
where
|
||||||
T: Num + Ord + Copy,
|
T: Num + Ord + Copy,
|
||||||
{
|
{
|
||||||
pub fn extend(&self, pos: Pos2D<T>) -> Area<T> {
|
pub fn extend(&self, pos: Pos2<T>) -> Area<T> {
|
||||||
if self.contains(pos) {
|
if self.contains(pos) {
|
||||||
return *self;
|
return *self;
|
||||||
}
|
}
|
||||||
|
|
@ -40,15 +40,15 @@ where
|
||||||
upper_right: self.upper_right.max_components(pos),
|
upper_right: self.upper_right.max_components(pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_lower_left(&self) -> Pos2D<T> {
|
pub fn get_lower_left(&self) -> Pos2<T> {
|
||||||
self.lower_left
|
self.lower_left
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_upper_right(&self) -> Pos2D<T> {
|
pub fn get_upper_right(&self) -> Pos2<T> {
|
||||||
self.upper_right
|
self.upper_right
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, pos: Pos2D<T>) -> bool {
|
pub fn contains(&self, pos: Pos2<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,7 +62,7 @@ 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 Pos2D<T>>,
|
I: Iterator<Item = &'a Pos2<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| {
|
||||||
|
|
@ -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 = Pos2D<T>;
|
type Item = Pos2<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 = Pos2D::new(self.col, self.row);
|
let pos = Pos2::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 = Pos2D<T>;
|
type Item = Pos2<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 = Pos2D::new(self.col, self.row);
|
let pos = Pos2::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(Pos2D::new(-1, -1), Pos2D::new(1, 1));
|
let area = Area::new(Pos2::new(-1, -1), Pos2::new(1, 1));
|
||||||
let result = area.cells(true).collect::<Vec<_>>();
|
let result = area.cells(true).collect::<Vec<_>>();
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
Pos2D::new(-1, -1),
|
Pos2::new(-1, -1),
|
||||||
Pos2D::new(0, -1),
|
Pos2::new(0, -1),
|
||||||
Pos2D::new(1, -1),
|
Pos2::new(1, -1),
|
||||||
Pos2D::new(-1, 0),
|
Pos2::new(-1, 0),
|
||||||
Pos2D::new(0, 0),
|
Pos2::new(0, 0),
|
||||||
Pos2D::new(1, 0),
|
Pos2::new(1, 0),
|
||||||
Pos2D::new(-1, 1),
|
Pos2::new(-1, 1),
|
||||||
Pos2D::new(0, 1),
|
Pos2::new(0, 1),
|
||||||
Pos2D::new(1, 1),
|
Pos2::new(1, 1),
|
||||||
];
|
];
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{pos::Pos2D, turn::Turn};
|
use super::{pos2::Pos2, turn::Turn};
|
||||||
use num_traits::{Num, Signed};
|
use num_traits::{Num, Signed};
|
||||||
use std::{fmt::Display, ops::Add};
|
use std::{fmt::Display, ops::Add};
|
||||||
use Direction::*;
|
use Direction::*;
|
||||||
|
|
@ -82,49 +82,49 @@ impl Display for Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<Direction> for Pos2D<T>
|
impl<T> From<Direction> for Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + Signed,
|
T: Num + Signed,
|
||||||
{
|
{
|
||||||
fn from(value: Direction) -> Self {
|
fn from(value: Direction) -> Self {
|
||||||
match value {
|
match value {
|
||||||
East => Pos2D::new(T::one(), T::zero()),
|
East => Pos2::new(T::one(), T::zero()),
|
||||||
North => Pos2D::new(T::zero(), -T::one()),
|
North => Pos2::new(T::zero(), -T::one()),
|
||||||
West => Pos2D::new(-T::one(), T::zero()),
|
West => Pos2::new(-T::one(), T::zero()),
|
||||||
South => Pos2D::new(T::zero(), T::one()),
|
South => Pos2::new(T::zero(), T::one()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<Pos2D<i32>> for Direction {
|
impl Add<Pos2<i32>> for Direction {
|
||||||
type Output = Pos2D<i32>;
|
type Output = Pos2<i32>;
|
||||||
|
|
||||||
fn add(self, rhs: Pos2D<i32>) -> Self::Output {
|
fn add(self, rhs: Pos2<i32>) -> Self::Output {
|
||||||
Pos2D::add(rhs, self)
|
Pos2::add(rhs, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<Pos2D<i32>> for &Direction {
|
impl Add<Pos2<i32>> for &Direction {
|
||||||
type Output = Pos2D<i32>;
|
type Output = Pos2<i32>;
|
||||||
|
|
||||||
fn add(self, rhs: Pos2D<i32>) -> Self::Output {
|
fn add(self, rhs: Pos2<i32>) -> Self::Output {
|
||||||
Pos2D::add(rhs, *self)
|
Pos2::add(rhs, *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<&Pos2D<i32>> for Direction {
|
impl Add<&Pos2<i32>> for Direction {
|
||||||
type Output = Pos2D<i32>;
|
type Output = Pos2<i32>;
|
||||||
|
|
||||||
fn add(self, rhs: &Pos2D<i32>) -> Self::Output {
|
fn add(self, rhs: &Pos2<i32>) -> Self::Output {
|
||||||
Pos2D::add(*rhs, self)
|
Pos2::add(*rhs, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<&Pos2D<i32>> for &Direction {
|
impl Add<&Pos2<i32>> for &Direction {
|
||||||
type Output = Pos2D<i32>;
|
type Output = Pos2<i32>;
|
||||||
|
|
||||||
fn add(self, rhs: &Pos2D<i32>) -> Self::Output {
|
fn add(self, rhs: &Pos2<i32>) -> Self::Output {
|
||||||
Pos2D::add(*rhs, *self)
|
Pos2::add(*rhs, *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,7 @@ pub mod helper;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod pos;
|
pub mod pos2;
|
||||||
|
pub mod pos3;
|
||||||
pub mod turn;
|
pub mod turn;
|
||||||
|
pub mod unit_vector;
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,48 @@
|
||||||
use super::direction::Direction;
|
use super::direction::Direction;
|
||||||
use super::math::gcd;
|
use super::math::gcd;
|
||||||
use num_traits::{CheckedAdd, CheckedSub, Float, Num, NumCast, Signed};
|
use num_traits::{CheckedAdd, CheckedSub, Float, Num, NumCast, Signed, Zero};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Add, Div, Mul, Sub};
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||||
|
|
||||||
pub type Pos2D<T> = Pos<T>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct Pos<T>([T; 2]);
|
pub struct Pos2<T>([T; 2]);
|
||||||
|
|
||||||
impl<T> Pos<T> {
|
impl<T> Pos2<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(x: T, y: T) -> Pos<T> {
|
pub fn new(x: T, y: T) -> Pos2<T> {
|
||||||
Pos([x, y])
|
Pos2([x, y])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn get_x(&self) -> &T {
|
pub fn get_x(&self) -> &T {
|
||||||
&self.0[0]
|
&self.0[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn get_y(&self) -> &T {
|
pub fn get_y(&self) -> &T {
|
||||||
&self.0[1]
|
&self.0[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + Default> From<&[T]> for Pos<T> {
|
impl<T> From<[T; 2]> for Pos2<T> {
|
||||||
fn from(value: &[T]) -> Self {
|
|
||||||
match value.len() {
|
|
||||||
0 => Pos::new(T::default(), T::default()),
|
|
||||||
1 => Pos::new(value[0], T::default()),
|
|
||||||
_ => Pos::new(value[0], value[1]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<[T; 2]> for Pos<T> {
|
|
||||||
fn from(value: [T; 2]) -> Self {
|
fn from(value: [T; 2]) -> Self {
|
||||||
Pos(value)
|
Pos2(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<(T, T)> for Pos<T> {
|
impl<T> From<(T, T)> for Pos2<T> {
|
||||||
fn from(value: (T, T)) -> Self {
|
fn from(value: (T, T)) -> Self {
|
||||||
Pos([value.0, value.1])
|
Pos2([value.0, value.1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Copy,
|
T: Copy,
|
||||||
{
|
{
|
||||||
pub fn splat(v: T) -> Pos<T> {
|
#[inline]
|
||||||
Pos::new(v, v)
|
pub fn splat(v: T) -> Pos2<T> {
|
||||||
|
Pos2::new(v, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x(&self) -> T {
|
pub fn x(&self) -> T {
|
||||||
|
|
@ -63,26 +54,26 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + Ord + Copy,
|
T: Num + Ord + Copy,
|
||||||
{
|
{
|
||||||
pub fn normalize(self) -> Result<(Pos<T>, T), Pos<T>> {
|
pub fn normalize(self) -> Result<(Pos2<T>, T), Pos2<T>> {
|
||||||
if self.x().is_zero() && self.y().is_zero() {
|
if self.x().is_zero() && self.y().is_zero() {
|
||||||
Err(self)
|
Err(self)
|
||||||
} else {
|
} else {
|
||||||
gcd(self.x(), self.y())
|
gcd(self.x(), self.y())
|
||||||
.map(|ggt| (Pos::new(self.x() / ggt, self.y() / ggt), ggt))
|
.map(|ggt| (Pos2::new(self.x() / ggt, self.y() / ggt), ggt))
|
||||||
.ok_or(self)
|
.ok_or(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Float,
|
T: Float,
|
||||||
{
|
{
|
||||||
pub fn normal(self) -> Result<(Pos<T>, T), Pos<T>> {
|
pub fn normal(self) -> Result<(Pos2<T>, T), Pos2<T>> {
|
||||||
let length = self.length();
|
let length = self.length();
|
||||||
if length == T::zero() {
|
if length == T::zero() {
|
||||||
Err(self)
|
Err(self)
|
||||||
|
|
@ -92,7 +83,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + NumCast,
|
T: Num + NumCast,
|
||||||
{
|
{
|
||||||
|
|
@ -113,20 +104,20 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Ord + Copy,
|
T: Ord + Copy,
|
||||||
{
|
{
|
||||||
pub fn max_components(self, other: Pos<T>) -> Self {
|
pub fn max_components(self, other: Pos2<T>) -> Self {
|
||||||
Pos::new(self.x().max(other.x()), self.y().max(other.y()))
|
Pos2::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: Pos2<T>) -> Self {
|
||||||
Pos::new(self.x().min(other.x()), self.y().min(other.y()))
|
Pos2::new(self.x().min(other.x()), self.y().min(other.y()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Signed,
|
T: Signed,
|
||||||
{
|
{
|
||||||
|
|
@ -135,7 +126,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Float,
|
T: Float,
|
||||||
{
|
{
|
||||||
|
|
@ -144,7 +135,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> fmt::Display for Pos<T>
|
impl<T> fmt::Display for Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + fmt::Display,
|
T: Num + fmt::Display,
|
||||||
{
|
{
|
||||||
|
|
@ -153,58 +144,82 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, P: Into<Pos<T>>> Add<P> for Pos<T>
|
impl<T> Zero for Pos2<T>
|
||||||
|
where
|
||||||
|
T: Num + Zero + Copy,
|
||||||
|
{
|
||||||
|
fn zero() -> Self {
|
||||||
|
Pos2::splat(T::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.x().is_zero() && self.y().is_zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, P: Into<Pos2<T>>> Add<P> for Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + Copy,
|
T: Num + Copy,
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
fn add(self, rhs: P) -> Self::Output {
|
fn add(self, rhs: P) -> Self::Output {
|
||||||
let rhs = rhs.into();
|
let rhs = rhs.into();
|
||||||
Pos::new(self.x() + rhs.x(), self.y() + rhs.y())
|
Pos2::new(self.x() + rhs.x(), self.y() + rhs.y())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P: Into<Pos<T>>> Sub<P> for Pos<T>
|
impl<T, P: Into<Pos2<T>>> Sub<P> for Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + Copy,
|
T: Num + Copy,
|
||||||
{
|
{
|
||||||
type Output = Pos<T>;
|
type Output = Pos2<T>;
|
||||||
fn sub(self, rhs: P) -> Self::Output {
|
fn sub(self, rhs: P) -> Self::Output {
|
||||||
let rhs = rhs.into();
|
let rhs = rhs.into();
|
||||||
Pos::new(self.x() - rhs.x(), self.y() - rhs.y())
|
Pos2::new(self.x() - rhs.x(), self.y() - rhs.y())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Mul<T> for Pos<T>
|
impl<T> Mul<T> for Pos2<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::new(self.x() * rhs, self.y() * rhs)
|
Pos2::new(self.x() * rhs, self.y() * rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Div<T> for Pos<T>
|
impl<T> Div<T> for Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + Copy,
|
T: Num + Copy,
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
fn div(self, rhs: T) -> Self::Output {
|
fn div(self, rhs: T) -> Self::Output {
|
||||||
Pos::new(self.x() / rhs, self.y() / rhs)
|
Pos2::new(self.x() / rhs, self.y() / rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Neg for Pos2<T>
|
||||||
|
where
|
||||||
|
T: Signed + Copy,
|
||||||
|
{
|
||||||
|
type Output = Pos2<T>;
|
||||||
|
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Self([-self.x(), -self.y()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + Signed + Copy,
|
T: Num + Signed + Copy,
|
||||||
{
|
{
|
||||||
pub fn taxicab_between(self, other: Pos<T>) -> T {
|
pub fn taxicab_between(self, other: Pos2<T>) -> T {
|
||||||
(self - other).abs()
|
(self - other).abs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos<T>
|
impl<T> Pos2<T>
|
||||||
where
|
where
|
||||||
T: Num + Copy + CheckedAdd + CheckedSub,
|
T: Num + Copy + CheckedAdd + CheckedSub,
|
||||||
{
|
{
|
||||||
|
|
@ -213,19 +228,19 @@ where
|
||||||
Direction::East => self
|
Direction::East => self
|
||||||
.x()
|
.x()
|
||||||
.checked_add(&T::one())
|
.checked_add(&T::one())
|
||||||
.map(|x| Pos::new(x, self.y())),
|
.map(|x| Pos2::new(x, self.y())),
|
||||||
Direction::North => self
|
Direction::North => self
|
||||||
.y()
|
.y()
|
||||||
.checked_sub(&T::one())
|
.checked_sub(&T::one())
|
||||||
.map(|y| Pos::new(self.x(), y)),
|
.map(|y| Pos2::new(self.x(), y)),
|
||||||
Direction::West => self
|
Direction::West => self
|
||||||
.x()
|
.x()
|
||||||
.checked_sub(&T::one())
|
.checked_sub(&T::one())
|
||||||
.map(|x| Pos::new(x, self.y())),
|
.map(|x| Pos2::new(x, self.y())),
|
||||||
Direction::South => self
|
Direction::South => self
|
||||||
.y()
|
.y()
|
||||||
.checked_add(&T::one())
|
.checked_add(&T::one())
|
||||||
.map(|y| Pos::new(self.x(), y)),
|
.map(|y| Pos2::new(self.x(), y)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
182
src/common/pos3.rs
Normal file
182
src/common/pos3.rs
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
use num_traits::{Num, PrimInt, Signed, Zero};
|
||||||
|
use std::fmt;
|
||||||
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
|
pub struct Pos3<T>([T; 3]);
|
||||||
|
|
||||||
|
impl<T> Pos3<T> {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(x: T, y: T, z: T) -> Pos3<T> {
|
||||||
|
Pos3([x, y, z])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_x(&self) -> &T {
|
||||||
|
&self.0[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_y(&self) -> &T {
|
||||||
|
&self.0[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_z(&self) -> &T {
|
||||||
|
&self.0[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Signed + PrimInt> Pos3<T> {
|
||||||
|
pub fn is_unit(&self) -> bool {
|
||||||
|
self.abs() == T::one()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + Default> From<&[T]> for Pos3<T> {
|
||||||
|
fn from(value: &[T]) -> Self {
|
||||||
|
match value.len() {
|
||||||
|
0 => Pos3::new(T::default(), T::default(), T::default()),
|
||||||
|
1 => Pos3::new(value[0], T::default(), T::default()),
|
||||||
|
2 => Pos3::new(value[0], value[1], T::default()),
|
||||||
|
_ => Pos3::new(value[0], value[1], value[2]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<[T; 3]> for Pos3<T> {
|
||||||
|
fn from(value: [T; 3]) -> Self {
|
||||||
|
Pos3(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<(T, T, T)> for Pos3<T> {
|
||||||
|
fn from(value: (T, T, T)) -> Self {
|
||||||
|
Pos3([value.0, value.1, value.2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pos3<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub fn splat(v: T) -> Pos3<T> {
|
||||||
|
Pos3::new(v, v, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn x(&self) -> T {
|
||||||
|
self.0[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn y(&self) -> T {
|
||||||
|
self.0[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn z(&self) -> T {
|
||||||
|
self.0[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Zero for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Num + Zero + Copy,
|
||||||
|
{
|
||||||
|
fn zero() -> Self {
|
||||||
|
Pos3::splat(T::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.x().is_zero() && self.y().is_zero() && self.z().is_zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pos3<T>
|
||||||
|
where
|
||||||
|
T: Signed,
|
||||||
|
{
|
||||||
|
pub fn abs(self) -> T {
|
||||||
|
self.get_x().abs() + self.get_y().abs() + self.get_z().abs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Display for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Num + fmt::Display,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "({}, {}, {})", self.get_x(), self.get_y(), self.get_z())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P: Into<Pos3<T>>> Add<P> for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Num + Copy,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, rhs: P) -> Self::Output {
|
||||||
|
let rhs = rhs.into();
|
||||||
|
Pos3::new(self.x() + rhs.x(), self.y() + rhs.y(), self.z() + rhs.z())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, P: Into<Pos3<T>>> Sub<P> for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Num + Copy,
|
||||||
|
{
|
||||||
|
type Output = Pos3<T>;
|
||||||
|
fn sub(self, rhs: P) -> Self::Output {
|
||||||
|
let rhs = rhs.into();
|
||||||
|
Pos3::new(self.x() - rhs.x(), self.y() - rhs.y(), self.z() - rhs.z())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Mul<T> for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Num + Copy,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, rhs: T) -> Self::Output {
|
||||||
|
Pos3::new(self.x() * rhs, self.y() * rhs, self.z() * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Div<T> for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Num + Copy,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
fn div(self, rhs: T) -> Self::Output {
|
||||||
|
Pos3::new(self.x() / rhs, self.y() / rhs, self.z() / rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Neg for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Signed + Copy,
|
||||||
|
{
|
||||||
|
type Output = Pos3<T>;
|
||||||
|
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Pos3::new(-self.x(), -self.y(), -self.z())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Mul<Pos3<T>> for Pos3<T>
|
||||||
|
where
|
||||||
|
T: Num + Copy,
|
||||||
|
{
|
||||||
|
type Output = Pos3<T>;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Pos3<T>) -> Self::Output {
|
||||||
|
Pos3([
|
||||||
|
self.y() * rhs.z() - self.z() * rhs.y(),
|
||||||
|
self.z() * rhs.x() - self.x() * rhs.z(),
|
||||||
|
self.x() * rhs.y() - self.y() * rhs.x(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/common/unit_vector.rs
Normal file
57
src/common/unit_vector.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
use super::pos3::Pos3;
|
||||||
|
use std::ops::{Mul, Neg};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct UnitVector(Pos3<i8>);
|
||||||
|
|
||||||
|
pub const X: UnitVector = UnitVector(Pos3::new(1, 0, 0));
|
||||||
|
pub const NEG_X: UnitVector = UnitVector(Pos3::new(-1, 0, 0));
|
||||||
|
pub const Y: UnitVector = UnitVector(Pos3::new(0, 1, 0));
|
||||||
|
pub const NEG_Y: UnitVector = UnitVector(Pos3::new(0, -1, 0));
|
||||||
|
pub const Z: UnitVector = UnitVector(Pos3::new(0, 0, 1));
|
||||||
|
pub const NEG_Z: UnitVector = UnitVector(Pos3::new(0, 0, -1));
|
||||||
|
|
||||||
|
impl UnitVector {
|
||||||
|
pub fn new(vector: Pos3<i8>) -> Option<Self> {
|
||||||
|
if vector.is_unit() {
|
||||||
|
Some(UnitVector(vector))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x(self) -> i8 {
|
||||||
|
self.0.x()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y(self) -> i8 {
|
||||||
|
self.0.y()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z(self) -> i8 {
|
||||||
|
self.0.z()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnitVector> for Pos3<i8> {
|
||||||
|
fn from(value: UnitVector) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for UnitVector {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
UnitVector(self.0 * rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for UnitVector {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
UnitVector(-self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use crate::common::{direction::Direction, file::split_lines, pos::Pos2D};
|
use crate::common::{direction::Direction, file::split_lines, pos2::Pos2};
|
||||||
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: Pos2D<i32>, second: Pos2D<i32>) -> Option<Pos2D<i32>> {
|
fn get_closer(first: Pos2<i32>, second: Pos2<i32>) -> Option<Pos2<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(Pos2D::new(diff.x().signum(), diff.y().signum()))
|
Some(Pos2::new(diff.x().signum(), diff.y().signum()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Rope {
|
struct Rope {
|
||||||
head: Pos2D<i32>,
|
head: Pos2<i32>,
|
||||||
knots: Vec<Pos2D<i32>>,
|
knots: Vec<Pos2<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: Pos2D::new(0, 0),
|
head: Pos2::new(0, 0),
|
||||||
knots: vec![Pos2D::new(0, 0); length - 1],
|
knots: vec![Pos2::new(0, 0); length - 1],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use crate::common::{file::split_lines, pos::Pos2D};
|
use crate::common::{file::split_lines, pos2::Pos2};
|
||||||
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: Pos2D<usize>,
|
pos: Pos2<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: Pos2D<usize>) -> Self {
|
pub fn new(length: usize, height: char, pos: Pos2<usize>) -> Self {
|
||||||
Path {
|
Path {
|
||||||
length,
|
length,
|
||||||
height,
|
height,
|
||||||
|
|
@ -104,28 +104,28 @@ impl<'a> Neighbors<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_pos(&mut self) -> Option<Pos2D<usize>> {
|
fn next_pos(&mut self) -> Option<Pos2<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(Pos2D::new(self.path.pos.x() + 1, self.path.pos.y()));
|
return Some(Pos2::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(Pos2D::new(self.path.pos.x(), self.path.pos.y() - 1));
|
return Some(Pos2::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(Pos2D::new(self.path.pos.x() - 1, self.path.pos.y()));
|
return Some(Pos2::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(Pos2D::new(self.path.pos.x(), self.path.pos.y() + 1));
|
return Some(Pos2::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: Pos2D<usize>,
|
start: Pos2<usize>,
|
||||||
exit: Pos2D<usize>,
|
exit: Pos2<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(Pos2D::new(x, y));
|
start = Some(Pos2::new(x, y));
|
||||||
height_row.push('a')
|
height_row.push('a')
|
||||||
}
|
}
|
||||||
'E' => {
|
'E' => {
|
||||||
exit = Some(Pos2D::new(x, y));
|
exit = Some(Pos2::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: Pos2D<usize>) -> char {
|
fn get_height(&self, pos: Pos2<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(Pos2D<usize>) -> bool,
|
F: Fn(Pos2<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, Pos2D::new(0, 0));
|
assert_eq!(valley.start, Pos2::new(0, 0));
|
||||||
assert_eq!(valley.exit, Pos2D::new(5, 2));
|
assert_eq!(valley.exit, Pos2::new(5, 2));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use crate::common::{file::split_lines, pos::Pos2D};
|
use crate::common::{file::split_lines, pos2::Pos2};
|
||||||
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<Pos2D<i32>> = HashSet::new();
|
let mut cave: HashSet<Pos2<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: Pos2D<i32>) -> bool {
|
fn get(&self, map: &[Vec<bool>], pos: Pos2<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,32 +98,28 @@ impl Cave {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&self, map: &mut [Vec<bool>], pos: Pos2D<i32>) {
|
fn set(&self, map: &mut [Vec<bool>], pos: Pos2<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: Pos2D<i32>, to: Pos2D<i32>) -> Result<Vec<Pos2D<i32>>, CaveError> {
|
fn walk(from: Pos2<i32>, to: Pos2<i32>) -> Result<Vec<Pos2<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())
|
Ok((from.y()..to.y()).map(|y| Pos2::new(from.x(), y)).collect())
|
||||||
.map(|y| Pos2D::new(from.x(), y))
|
|
||||||
.collect())
|
|
||||||
} else {
|
} else {
|
||||||
Ok(((to.y() + 1)..=from.y())
|
Ok(((to.y() + 1)..=from.y())
|
||||||
.map(|y| Pos2D::new(from.x(), y))
|
.map(|y| Pos2::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())
|
Ok((from.x()..to.x()).map(|x| Pos2::new(x, from.y())).collect())
|
||||||
.map(|x| Pos2D::new(x, from.y()))
|
|
||||||
.collect())
|
|
||||||
} else {
|
} else {
|
||||||
Ok(((to.x() + 1)..=from.x())
|
Ok(((to.x() + 1)..=from.x())
|
||||||
.map(|x| Pos2D::new(x, from.y()))
|
.map(|x| Pos2::new(x, from.y()))
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -131,14 +127,14 @@ impl Cave {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_one(line: &str) -> Result<HashSet<Pos2D<i32>>, CaveError> {
|
fn parse_one(line: &str) -> Result<HashSet<Pos2<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(Pos2D::new(x, y))
|
Ok(Pos2::new(x, y))
|
||||||
}
|
}
|
||||||
_ => Err(CaveError::NotAValidPos(coord.to_owned())),
|
_ => Err(CaveError::NotAValidPos(coord.to_owned())),
|
||||||
})
|
})
|
||||||
|
|
@ -175,8 +171,8 @@ impl Cave {
|
||||||
drops
|
drops
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos2D<i32>> {
|
fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos2<i32>> {
|
||||||
let mut drop = Pos2D::new(500, 0);
|
let mut drop = Pos2::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 {
|
||||||
|
|
@ -184,7 +180,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 = Pos2D::new(drop.x() + dx, next_y);
|
let next = Pos2::new(drop.x() + dx, next_y);
|
||||||
if !self.get(map, next) {
|
if !self.get(map, next) {
|
||||||
drop = next;
|
drop = next;
|
||||||
stuck = false;
|
stuck = false;
|
||||||
|
|
@ -256,7 +252,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! {Pos2D::new(498, 4), Pos2D::new(498, 5), Pos2D::new(498, 6), Pos2D::new(497, 6), Pos2D::new(496, 6)};
|
let expected = hashset! {Pos2::new(498, 4), Pos2::new(498, 5), Pos2::new(498, 6), Pos2::new(497, 6), Pos2::new(496, 6)};
|
||||||
let result = Cave::parse_one(input)?;
|
let result = Cave::parse_one(input)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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::Pos2D,
|
pos2::Pos2,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nom::{
|
use nom::{
|
||||||
|
|
@ -58,7 +58,7 @@ impl DayTrait for Day {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Day {
|
impl Day {
|
||||||
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos2D<i64>>), SensorError> {
|
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos2<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,11 +70,7 @@ impl Day {
|
||||||
Ok((sensors, beacons))
|
Ok((sensors, beacons))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_coverage_at(
|
fn count_coverage_at(sensors: &HashSet<Sensor>, beacons: &HashSet<Pos2<i64>>, row: i64) -> i64 {
|
||||||
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))
|
||||||
|
|
@ -121,7 +117,7 @@ impl From<Err<Error<&str>>> for SensorError {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Line {
|
struct Line {
|
||||||
start: Pos2D<i64>,
|
start: Pos2<i64>,
|
||||||
is_up: bool,
|
is_up: bool,
|
||||||
steps: i64,
|
steps: i64,
|
||||||
}
|
}
|
||||||
|
|
@ -143,16 +139,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 = Pos2D::new(one.pos.x(), one.pos.y() + one.radius + 1);
|
start = Pos2::new(one.pos.x(), one.pos.y() + one.radius + 1);
|
||||||
} else {
|
} else {
|
||||||
start = Pos2D::new(two.pos.x() - two.radius - 1, two.pos.y());
|
start = Pos2::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 = Pos2D::new(one.pos.x(), one.pos.y() - one.radius - 1);
|
start = Pos2::new(one.pos.x(), one.pos.y() - one.radius - 1);
|
||||||
} else {
|
} else {
|
||||||
start = Pos2D::new(two.pos.x() - two.radius - 1, two.pos.y());
|
start = Pos2::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();
|
||||||
|
|
@ -164,7 +160,7 @@ impl Line {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cross(&self, other: &Line) -> Option<Pos2D<i64>> {
|
fn cross(&self, other: &Line) -> Option<Pos2<i64>> {
|
||||||
if self.is_up == other.is_up {
|
if self.is_up == other.is_up {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +181,7 @@ impl Line {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = top_down.start + Pos2D::splat(r);
|
let pos = top_down.start + Pos2::splat(r);
|
||||||
Some(pos)
|
Some(pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +227,7 @@ impl Range {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
||||||
struct Sensor {
|
struct Sensor {
|
||||||
pos: Pos2D<i64>,
|
pos: Pos2<i64>,
|
||||||
radius: i64,
|
radius: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,16 +239,16 @@ impl Sensor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pos(input: &str) -> IResult<&str, Pos2D<i64>> {
|
fn parse_pos(input: &str) -> IResult<&str, Pos2<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, Pos2D::new(x, y)))
|
Ok((input, Pos2::new(x, y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos2D<i64>)> {
|
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos2<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)?;
|
||||||
|
|
@ -276,7 +272,7 @@ impl Sensor {
|
||||||
distance - self.radius - other.radius
|
distance - self.radius - other.radius
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, pos: Pos2D<i64>) -> bool {
|
pub fn contains(&self, pos: Pos2<i64>) -> bool {
|
||||||
self.pos.taxicab_between(pos) <= self.radius
|
self.pos.taxicab_between(pos) <= self.radius
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -291,10 +287,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: Pos2D::new(2, 18),
|
pos: Pos2::new(2, 18),
|
||||||
radius: 7,
|
radius: 7,
|
||||||
},
|
},
|
||||||
Pos2D::new(-2, 15),
|
Pos2::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);
|
||||||
|
|
@ -305,7 +301,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_width() {
|
fn test_width() {
|
||||||
let sensor = Sensor {
|
let sensor = Sensor {
|
||||||
pos: Pos2D::new(8, 7),
|
pos: Pos2::new(8, 7),
|
||||||
radius: 9,
|
radius: 9,
|
||||||
};
|
};
|
||||||
assert_eq!(sensor.range_at(17), None);
|
assert_eq!(sensor.range_at(17), None);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use super::template::{DayTrait, ResultType};
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
direction::Direction,
|
direction::Direction,
|
||||||
parser::{eol_terminated, extract_result, ignore, usize},
|
parser::{eol_terminated, extract_result, ignore, usize},
|
||||||
pos::Pos2D,
|
pos2::Pos2,
|
||||||
|
unit_vector::{self, UnitVector},
|
||||||
};
|
};
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
|
|
@ -12,7 +13,8 @@ use nom::{
|
||||||
multi::many1,
|
multi::many1,
|
||||||
Err, IResult, Parser,
|
Err, IResult, Parser,
|
||||||
};
|
};
|
||||||
use std::str::FromStr;
|
use num_traits::Zero;
|
||||||
|
use std::{collections::HashMap, str::FromStr};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const DAY_NUMBER: usize = 22;
|
const DAY_NUMBER: usize = 22;
|
||||||
|
|
@ -26,16 +28,14 @@ impl DayTrait for Day {
|
||||||
|
|
||||||
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let (world_map, instructions) = Day::parse(lines)?;
|
let (world_map, instructions) = Day::parse(lines)?;
|
||||||
let world = WrappingWorld::new(world_map);
|
let result = WrappingWalker::walk_all(world_map, instructions)?;
|
||||||
let walker = Walker::walk_all(&world, instructions)?;
|
Ok(ResultType::Integer(result.value()))
|
||||||
Ok(ResultType::Integer(walker.value()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let (world_map, instructions) = Day::parse(lines)?;
|
let (world_map, instructions) = Day::parse(lines)?;
|
||||||
let world = CubeWorld::new(world_map);
|
let result = CubeWalker::walk_all(world_map, instructions)?;
|
||||||
let walker = Walker::walk_all(&world, instructions)?;
|
Ok(ResultType::Integer(result.value()))
|
||||||
Ok(ResultType::Integer(walker.value()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,8 +52,12 @@ impl Day {
|
||||||
enum WorldError {
|
enum WorldError {
|
||||||
#[error("Not a valid description: {0}")]
|
#[error("Not a valid description: {0}")]
|
||||||
ParsingError(String),
|
ParsingError(String),
|
||||||
|
|
||||||
#[error("No Starting Point found")]
|
#[error("No Starting Point found")]
|
||||||
NoStartingPoint,
|
NoStartingPoint,
|
||||||
|
|
||||||
|
#[error("Map is not a valid cube")]
|
||||||
|
NotAValidCube,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Err<Error<&str>>> for WorldError {
|
impl From<Err<Error<&str>>> for WorldError {
|
||||||
|
|
@ -79,6 +83,14 @@ impl WorldMap {
|
||||||
Self { tiles }
|
Self { tiles }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.tiles.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> Option<usize> {
|
||||||
|
self.tiles.iter().map(|row| row.len()).max()
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> IResult<&str, WorldMap> {
|
fn parse(input: &str) -> IResult<&str, WorldMap> {
|
||||||
let tile = alt((
|
let tile = alt((
|
||||||
value(Some(false), char('#')),
|
value(Some(false), char('#')),
|
||||||
|
|
@ -90,32 +102,33 @@ impl WorldMap {
|
||||||
lines.parse(input)
|
lines.parse(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_first(&self, pos: Pos2D<usize>, facing: Direction) -> Option<Pos2D<usize>> {
|
fn get_first(&self, map_pos: Location) -> Option<Location> {
|
||||||
match facing {
|
match map_pos.facing {
|
||||||
Direction::East => self.tiles.get(pos.y()).and_then(|row| {
|
Direction::East => self.tiles.get(map_pos.pos.y()).and_then(|row| {
|
||||||
row.iter()
|
row.iter()
|
||||||
.position(|t| t.is_some())
|
.position(|t| t.is_some())
|
||||||
.map(|x| Pos2D::new(x, pos.y()))
|
.map(|x| Pos2::new(x, map_pos.pos.y()))
|
||||||
}),
|
}),
|
||||||
Direction::West => self.tiles.get(pos.y()).and_then(|row| {
|
Direction::West => self.tiles.get(map_pos.pos.y()).and_then(|row| {
|
||||||
row.iter()
|
row.iter()
|
||||||
.rposition(|t| t.is_some())
|
.rposition(|t| t.is_some())
|
||||||
.map(|x| Pos2D::new(x, pos.y()))
|
.map(|x| Pos2::new(x, map_pos.pos.y()))
|
||||||
}),
|
}),
|
||||||
Direction::South => self
|
Direction::South => self
|
||||||
.tiles
|
.tiles
|
||||||
.iter()
|
.iter()
|
||||||
.position(|row| pos.x() < row.len() && row[pos.x()].is_some())
|
.position(|row| map_pos.pos.x() < row.len() && row[map_pos.pos.x()].is_some())
|
||||||
.map(|y| Pos2D::new(pos.x(), y)),
|
.map(|y| Pos2::new(map_pos.pos.x(), y)),
|
||||||
Direction::North => self
|
Direction::North => self
|
||||||
.tiles
|
.tiles
|
||||||
.iter()
|
.iter()
|
||||||
.rposition(|row| pos.x() < row.len() && row[pos.x()].is_some())
|
.rposition(|row| map_pos.pos.x() < row.len() && row[map_pos.pos.x()].is_some())
|
||||||
.map(|y| Pos2D::new(pos.x(), y)),
|
.map(|y| Pos2::new(map_pos.pos.x(), y)),
|
||||||
}
|
}
|
||||||
|
.map(|pos| Location::new(pos, map_pos.facing))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tile(&self, pos: Pos2D<usize>) -> Option<bool> {
|
fn get_tile(&self, pos: Pos2<usize>) -> Option<bool> {
|
||||||
self.tiles
|
self.tiles
|
||||||
.get(pos.y())
|
.get(pos.y())
|
||||||
.and_then(|row| row.get(pos.x()))
|
.and_then(|row| row.get(pos.x()))
|
||||||
|
|
@ -124,82 +137,6 @@ impl WorldMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait World {
|
|
||||||
fn get_map(&self) -> &WorldMap;
|
|
||||||
|
|
||||||
fn wrap(&self, pos: Pos2D<usize>, facing: Direction) -> Option<(Pos2D<usize>, Direction)>;
|
|
||||||
|
|
||||||
fn get_start(&self, pos: Pos2D<usize>, facing: Direction) -> Option<Pos2D<usize>> {
|
|
||||||
self.get_map().get_first(pos, facing)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn step(&self, pos: Pos2D<usize>, facing: Direction) -> Option<(Pos2D<usize>, Direction)> {
|
|
||||||
let Some(pos) = pos.check_add(facing) else {
|
|
||||||
return self.wrap(pos, facing);
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.get_map().get_tile(pos) {
|
|
||||||
Some(true) => Some((pos, facing)),
|
|
||||||
Some(false) => None,
|
|
||||||
None => self.wrap(pos, facing),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WrappingWorld {
|
|
||||||
map: WorldMap,
|
|
||||||
}
|
|
||||||
impl WrappingWorld {
|
|
||||||
fn new(world_map: WorldMap) -> Self {
|
|
||||||
WrappingWorld { map: world_map }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl World for WrappingWorld {
|
|
||||||
fn get_map(&self) -> &WorldMap {
|
|
||||||
&self.map
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap(&self, pos: Pos2D<usize>, facing: Direction) -> Option<(Pos2D<usize>, Direction)> {
|
|
||||||
if let Some(pos) = self.map.get_first(pos, facing) {
|
|
||||||
match self.map.get_tile(pos) {
|
|
||||||
Some(true) => Some((pos, facing)),
|
|
||||||
Some(false) => None,
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CubeWorld {
|
|
||||||
map: WorldMap,
|
|
||||||
}
|
|
||||||
impl CubeWorld {
|
|
||||||
fn new(world_map: WorldMap) -> Self {
|
|
||||||
CubeWorld { map: world_map }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl World for CubeWorld {
|
|
||||||
fn get_map(&self) -> &WorldMap {
|
|
||||||
&self.map
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap(&self, pos: Pos2D<usize>, facing: Direction) -> Option<(Pos2D<usize>, Direction)> {
|
|
||||||
if let Some(pos) = self.map.get_first(pos, facing) {
|
|
||||||
match self.map.get_tile(pos) {
|
|
||||||
Some(true) => Some((pos, facing)),
|
|
||||||
Some(false) => None,
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Instruction {
|
enum Instruction {
|
||||||
Walk(usize),
|
Walk(usize),
|
||||||
|
|
@ -218,29 +155,34 @@ impl Instruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Walker {
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pos: Pos2D<usize>,
|
struct Location {
|
||||||
|
pos: Pos2<usize>,
|
||||||
facing: Direction,
|
facing: Direction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Walker {
|
impl Location {
|
||||||
fn new<W: World>(world: &W) -> Result<Self, WorldError> {
|
pub fn new(pos: Pos2<usize>, facing: Direction) -> Self {
|
||||||
let facing = Direction::East;
|
Location { pos, facing }
|
||||||
if let Some(pos) = world.get_start(Pos2D::new(0, 0), facing) {
|
|
||||||
Ok(Walker { pos, facing })
|
|
||||||
} else {
|
|
||||||
Err(WorldError::NoStartingPoint)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value(&self) -> i64 {
|
#[inline]
|
||||||
(self.pos.y() + 1) as i64 * 1000
|
fn pos(&self) -> Pos2<usize> {
|
||||||
+ (self.pos.x() + 1) as i64 * 4
|
self.pos
|
||||||
+ Walker::face_value(self.facing)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn face_value(facing: Direction) -> i64 {
|
fn one_step(&self) -> Option<Self> {
|
||||||
match facing {
|
self.pos
|
||||||
|
.check_add(self.facing)
|
||||||
|
.map(|pos| Location::new(pos, self.facing))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> i64 {
|
||||||
|
(self.pos.y() + 1) as i64 * 1000 + (self.pos.x() + 1) as i64 * 4 + self.face_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn face_value(&self) -> i64 {
|
||||||
|
match self.facing {
|
||||||
Direction::East => 0,
|
Direction::East => 0,
|
||||||
Direction::North => 3,
|
Direction::North => 3,
|
||||||
Direction::West => 2,
|
Direction::West => 2,
|
||||||
|
|
@ -248,33 +190,333 @@ impl Walker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn act<W: World>(mut self, world: &W, instruction: Instruction) -> Self {
|
fn turn_left(&mut self) {
|
||||||
|
self.facing = self.facing.turn_left()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_right(&mut self) {
|
||||||
|
self.facing = self.facing.turn_right()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Walker: Sized {
|
||||||
|
fn turn_right(&mut self);
|
||||||
|
fn turn_left(&mut self);
|
||||||
|
fn set_location(&mut self, pos: Location);
|
||||||
|
|
||||||
|
fn new(world_map: WorldMap) -> Result<Self, WorldError>;
|
||||||
|
fn act(&mut self, instruction: Instruction) {
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Right => self.facing = self.facing.turn_right(),
|
Instruction::Right => self.turn_right(),
|
||||||
Instruction::Left => self.facing = self.facing.turn_left(),
|
Instruction::Left => self.turn_left(),
|
||||||
Instruction::Walk(steps) => {
|
Instruction::Walk(steps) => {
|
||||||
for _ in 0..steps {
|
for _ in 0..steps {
|
||||||
let Some((next_pos, next_facing)) = world.step(self.pos, self.facing) else {
|
let Some(next_pos) = self.step() else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
self.pos = next_pos;
|
self.set_location(next_pos);
|
||||||
self.facing = next_facing;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk_all<W: World>(
|
fn get_map(&self) -> &WorldMap;
|
||||||
world: &W,
|
fn location(&self) -> Location;
|
||||||
instructions: Vec<Instruction>,
|
|
||||||
) -> Result<Self, WorldError> {
|
fn wrap(&self) -> Option<Location>;
|
||||||
let mut walker = Walker::new(world)?;
|
fn step(&self) -> Option<Location> {
|
||||||
|
if let Some(next_pos) = self.location().one_step() {
|
||||||
|
match self.get_map().get_tile(next_pos.pos()) {
|
||||||
|
Some(true) => return Some(next_pos),
|
||||||
|
Some(false) => return None,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(next_pos) = self.wrap() {
|
||||||
|
match self.get_map().get_tile(next_pos.pos()) {
|
||||||
|
Some(true) => return Some(next_pos),
|
||||||
|
Some(false) => return None,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_all(world_map: WorldMap, instructions: Vec<Instruction>) -> Result<Location, WorldError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let mut walker = Self::new(world_map)?;
|
||||||
|
|
||||||
for instruction in instructions {
|
for instruction in instructions {
|
||||||
walker = walker.act(world, instruction);
|
walker.act(instruction);
|
||||||
}
|
}
|
||||||
Ok(walker)
|
Ok(walker.location())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WrappingWalker {
|
||||||
|
world: WorldMap,
|
||||||
|
location: Location,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walker for WrappingWalker {
|
||||||
|
fn new(world: WorldMap) -> Result<Self, WorldError> {
|
||||||
|
if let Some(location) = world.get_first(Location::new(Pos2::new(0, 0), Direction::East)) {
|
||||||
|
Ok(WrappingWalker { world, location })
|
||||||
|
} else {
|
||||||
|
Err(WorldError::NoStartingPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap(&self) -> Option<Location> {
|
||||||
|
self.world.get_first(self.location)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_right(&mut self) {
|
||||||
|
self.location.turn_right();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_left(&mut self) {
|
||||||
|
self.location.turn_left();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_location(&mut self, pos: Location) {
|
||||||
|
self.location = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn location(&self) -> Location {
|
||||||
|
self.location
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_map(&self) -> &WorldMap {
|
||||||
|
&self.world
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
struct CubePos {
|
||||||
|
side: UnitVector,
|
||||||
|
facing: UnitVector,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CubePos {
|
||||||
|
fn new(side: UnitVector, facing: UnitVector) -> Self {
|
||||||
|
CubePos { side, facing }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn facing_east(&self, facing: Direction) -> UnitVector {
|
||||||
|
match facing {
|
||||||
|
Direction::East => self.facing,
|
||||||
|
Direction::North => -(self.side * self.facing),
|
||||||
|
Direction::West => -self.facing,
|
||||||
|
Direction::South => self.side * self.facing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn forward(&self) -> CubePos {
|
||||||
|
CubePos {
|
||||||
|
side: self.facing,
|
||||||
|
facing: -self.side,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn turn_right(&mut self) {
|
||||||
|
self.facing = -(self.side * self.facing);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_left(&mut self) {
|
||||||
|
self.facing = self.side * self.facing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CubeWalker {
|
||||||
|
world: WorldMap,
|
||||||
|
location: Location,
|
||||||
|
translation: HashMap<UnitVector, (Pos2<usize>, UnitVector)>,
|
||||||
|
side_length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CubeWalker {
|
||||||
|
fn get_side_length(world: &WorldMap) -> Result<usize, WorldError> {
|
||||||
|
let width = world.width().ok_or_else(|| WorldError::NotAValidCube)?;
|
||||||
|
let height = world.height();
|
||||||
|
if width % 3 == 0 && height % 4 == 0 && width / 3 == height / 4 {
|
||||||
|
Ok(height / 4)
|
||||||
|
} else if width % 4 == 0 && height % 3 == 0 && width / 4 == height / 3 {
|
||||||
|
Ok(width / 4)
|
||||||
|
} else if width % 5 == 0 && height % 2 == 0 && width / 5 == height / 2 {
|
||||||
|
Ok(height / 2)
|
||||||
|
} else if width % 2 == 0 && height % 5 == 0 && width / 2 == height / 5 {
|
||||||
|
Ok(width / 2)
|
||||||
|
} else {
|
||||||
|
Err(WorldError::NotAValidCube)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_cube(
|
||||||
|
world_map: &WorldMap,
|
||||||
|
mut normal_map_pos: Location,
|
||||||
|
mut cube_pos: CubePos,
|
||||||
|
mut sides: HashMap<UnitVector, (Pos2<usize>, UnitVector)>,
|
||||||
|
side_length: usize,
|
||||||
|
) -> HashMap<UnitVector, (Pos2<usize>, UnitVector)> {
|
||||||
|
let facing_east = cube_pos.facing_east(normal_map_pos.facing);
|
||||||
|
|
||||||
|
sides.insert(cube_pos.side, (normal_map_pos.pos, facing_east));
|
||||||
|
if sides.len() == 6 {
|
||||||
|
return sides;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..4 {
|
||||||
|
if let Some(next_normal) = normal_map_pos.one_step() {
|
||||||
|
if world_map
|
||||||
|
.get_tile(next_normal.pos() * side_length)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
let next_cube = cube_pos.forward();
|
||||||
|
if !sides.contains_key(&next_cube.side) {
|
||||||
|
sides = CubeWalker::analyze_cube(
|
||||||
|
world_map,
|
||||||
|
next_normal,
|
||||||
|
next_cube,
|
||||||
|
sides,
|
||||||
|
side_length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
normal_map_pos.turn_left();
|
||||||
|
cube_pos.turn_left();
|
||||||
|
}
|
||||||
|
|
||||||
|
sides
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cube_position(&self, location: Location) -> Option<(CubePos, usize)> {
|
||||||
|
let normal = location.pos / self.side_length;
|
||||||
|
for (side, (pos, right)) in self.translation.iter() {
|
||||||
|
if pos == &normal {
|
||||||
|
match location.facing {
|
||||||
|
Direction::East => {
|
||||||
|
return Some((
|
||||||
|
CubePos::new(*side, *right),
|
||||||
|
location.pos.y() - pos.y() * self.side_length,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Direction::North => {
|
||||||
|
return Some((
|
||||||
|
CubePos::new(*side, -(*right * *side)),
|
||||||
|
location.pos.x() - pos.x() * self.side_length,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Direction::West => {
|
||||||
|
return Some((
|
||||||
|
CubePos::new(*side, -*right),
|
||||||
|
(pos.y() + 1) * self.side_length - location.pos.y() - 1,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Direction::South => {
|
||||||
|
return Some((
|
||||||
|
CubePos::new(*side, *right * *side),
|
||||||
|
(pos.x() + 1) * self.side_length - location.pos.x() - 1,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walker for CubeWalker {
|
||||||
|
fn new(world: WorldMap) -> Result<Self, WorldError> {
|
||||||
|
if let Some(location) = world.get_first(Location::new(Pos2::zero(), Direction::East)) {
|
||||||
|
let side_length = CubeWalker::get_side_length(&world)?;
|
||||||
|
let normal_map_pos = Location::new(location.pos / side_length, location.facing);
|
||||||
|
let translation = CubeWalker::analyze_cube(
|
||||||
|
&world,
|
||||||
|
normal_map_pos,
|
||||||
|
CubePos::new(unit_vector::X, unit_vector::Y),
|
||||||
|
HashMap::new(),
|
||||||
|
side_length,
|
||||||
|
);
|
||||||
|
if translation.len() != 6 {
|
||||||
|
return Err(WorldError::NotAValidCube);
|
||||||
|
}
|
||||||
|
Ok(CubeWalker {
|
||||||
|
world,
|
||||||
|
location,
|
||||||
|
translation,
|
||||||
|
side_length,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(WorldError::NoStartingPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap(&self) -> Option<Location> {
|
||||||
|
let (old_pos, delta) = self.get_cube_position(self.location)?;
|
||||||
|
let new_pos = old_pos.forward();
|
||||||
|
let (position, normal_right) = self.translation[&new_pos.side];
|
||||||
|
let normal = CubePos::new(new_pos.side, normal_right);
|
||||||
|
|
||||||
|
if new_pos.facing == normal.facing {
|
||||||
|
Some(Location::new(
|
||||||
|
Pos2::new(
|
||||||
|
position.x() * self.side_length,
|
||||||
|
position.y() * self.side_length + delta,
|
||||||
|
),
|
||||||
|
Direction::East,
|
||||||
|
))
|
||||||
|
} else if new_pos.facing == -normal.facing {
|
||||||
|
Some(Location::new(
|
||||||
|
Pos2::new(
|
||||||
|
(position.x() + 1) * self.side_length - 1,
|
||||||
|
(position.y() + 1) * self.side_length - 1 - delta,
|
||||||
|
),
|
||||||
|
Direction::West,
|
||||||
|
))
|
||||||
|
} else if new_pos.facing == (normal.side * normal.facing) {
|
||||||
|
Some(Location::new(
|
||||||
|
Pos2::new(
|
||||||
|
position.x() * self.side_length + delta,
|
||||||
|
(position.y() + 1) * self.side_length - 1,
|
||||||
|
),
|
||||||
|
Direction::North,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Some(Location::new(
|
||||||
|
Pos2::new(
|
||||||
|
(position.x() + 1) * self.side_length - 1 - delta,
|
||||||
|
position.y() * self.side_length,
|
||||||
|
),
|
||||||
|
Direction::South,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_right(&mut self) {
|
||||||
|
self.location.turn_right();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_left(&mut self) {
|
||||||
|
self.location.turn_left();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_location(&mut self, location: Location) {
|
||||||
|
self.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn location(&self) -> Location {
|
||||||
|
self.location
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_map(&self) -> &WorldMap {
|
||||||
|
&self.world
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,7 +541,7 @@ mod test {
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Nothing;
|
let expected = ResultType::Integer(5031);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
|
@ -324,18 +566,25 @@ mod test {
|
||||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let (world_map, _) = Day::parse(&lines)?;
|
let (world_map, _) = Day::parse(&lines)?;
|
||||||
|
|
||||||
let world = WrappingWorld::new(world_map);
|
let mut walker = WrappingWalker::new(world_map)?;
|
||||||
|
assert_eq!(
|
||||||
|
walker.location,
|
||||||
|
Location::new(Pos2::new(8, 0), Direction::East)
|
||||||
|
);
|
||||||
|
|
||||||
let mut walker = Walker::new(&world)?;
|
walker.act(Instruction::Walk(10));
|
||||||
assert_eq!(walker.pos, Pos2D::new(8, 0));
|
assert_eq!(
|
||||||
|
walker.location,
|
||||||
|
Location::new(Pos2::new(10, 0), Direction::East)
|
||||||
|
);
|
||||||
|
|
||||||
walker = walker.act(&world, Instruction::Walk(10));
|
walker.act(Instruction::Left);
|
||||||
assert_eq!(walker.pos, Pos2D::new(10, 0));
|
|
||||||
|
|
||||||
walker = walker.act(&world, Instruction::Left);
|
walker.act(Instruction::Walk(2));
|
||||||
|
assert_eq!(
|
||||||
walker = walker.act(&world, Instruction::Walk(2));
|
walker.location,
|
||||||
assert_eq!(walker.pos, Pos2D::new(10, 10));
|
Location::new(Pos2::new(10, 10), Direction::North)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -345,12 +594,137 @@ mod test {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let (world_map, instructions) = Day::parse(&lines)?;
|
let (world_map, instructions) = Day::parse(&lines)?;
|
||||||
let world = WrappingWorld::new(world_map);
|
|
||||||
|
|
||||||
let walker = Walker::walk_all(&world, instructions)?;
|
let result = WrappingWalker::walk_all(world_map, instructions)?;
|
||||||
assert_eq!(walker.pos, Pos2D::new(7, 5));
|
assert_eq!(result.pos, Pos2::new(7, 5));
|
||||||
assert_eq!(walker.facing, Direction::East);
|
assert_eq!(result.facing, Direction::East);
|
||||||
assert_eq!(walker.value(), 6032);
|
assert_eq!(result.value(), 6032);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_cube() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world_map, _) = Day::parse(&lines)?;
|
||||||
|
|
||||||
|
let walker = CubeWalker::new(world_map)?;
|
||||||
|
|
||||||
|
assert_eq!(walker.side_length, 4);
|
||||||
|
assert_eq!(
|
||||||
|
walker.translation.get(&unit_vector::X),
|
||||||
|
Some(&(Pos2::new(2, 0), unit_vector::Y))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
walker.translation.get(&unit_vector::NEG_X),
|
||||||
|
Some(&(Pos2::new(2, 2), unit_vector::Y))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
walker.translation.get(&unit_vector::Y),
|
||||||
|
Some(&(Pos2::new(3, 2), unit_vector::X))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
walker.translation.get(&unit_vector::NEG_Y),
|
||||||
|
Some(&(Pos2::new(1, 1), unit_vector::NEG_Z))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
walker.translation.get(&unit_vector::Z),
|
||||||
|
Some(&(Pos2::new(0, 1), unit_vector::NEG_Y))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
walker.translation.get(&unit_vector::NEG_Z),
|
||||||
|
Some(&(Pos2::new(2, 1), unit_vector::Y))
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cube_position() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world_map, _) = Day::parse(&lines)?;
|
||||||
|
|
||||||
|
let walker = CubeWalker::new(world_map)?;
|
||||||
|
|
||||||
|
let pos = Location::new(Pos2::new(11, 5), Direction::East);
|
||||||
|
let expected = (CubePos::new(unit_vector::NEG_Z, unit_vector::Y), 1);
|
||||||
|
assert_eq!(walker.get_cube_position(pos), Some(expected));
|
||||||
|
|
||||||
|
let pos = Location::new(Pos2::new(10, 11), Direction::South);
|
||||||
|
let expected = (CubePos::new(unit_vector::NEG_X, unit_vector::Z), 1);
|
||||||
|
assert_eq!(walker.get_cube_position(pos), Some(expected));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cube_wrapping() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world_map, _) = Day::parse(&lines)?;
|
||||||
|
|
||||||
|
let mut walker = CubeWalker::new(world_map)?;
|
||||||
|
|
||||||
|
walker.location = Location::new(Pos2::new(11, 5), Direction::East);
|
||||||
|
let expected = Location::new(Pos2::new(14, 8), Direction::South);
|
||||||
|
assert_eq!(walker.wrap(), Some(expected));
|
||||||
|
|
||||||
|
walker.location = Location::new(Pos2::new(10, 11), Direction::South);
|
||||||
|
let expected = Location::new(Pos2::new(1, 7), Direction::North);
|
||||||
|
assert_eq!(walker.wrap(), Some(expected));
|
||||||
|
|
||||||
|
walker.location = Location::new(Pos2::new(14, 8), Direction::North);
|
||||||
|
let expected = Location::new(Pos2::new(11, 5), Direction::West);
|
||||||
|
assert_eq!(walker.wrap(), Some(expected));
|
||||||
|
|
||||||
|
walker.location = Location::new(Pos2::new(1, 7), Direction::South);
|
||||||
|
let expected = Location::new(Pos2::new(10, 11), Direction::North);
|
||||||
|
assert_eq!(walker.wrap(), Some(expected));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn walk_cube() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world_map, _) = Day::parse(&lines)?;
|
||||||
|
|
||||||
|
let mut walker = CubeWalker::new(world_map)?;
|
||||||
|
assert_eq!(
|
||||||
|
walker.location,
|
||||||
|
Location::new(Pos2::new(8, 0), Direction::East)
|
||||||
|
);
|
||||||
|
|
||||||
|
walker.act(Instruction::Walk(10));
|
||||||
|
assert_eq!(
|
||||||
|
walker.location,
|
||||||
|
Location::new(Pos2::new(10, 0), Direction::East)
|
||||||
|
);
|
||||||
|
|
||||||
|
walker.act(Instruction::Left);
|
||||||
|
|
||||||
|
walker.act(Instruction::Walk(2));
|
||||||
|
assert_eq!(
|
||||||
|
walker.location,
|
||||||
|
Location::new(Pos2::new(1, 5), Direction::South)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn walk_all_cube() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world_map, instructions) = Day::parse(&lines)?;
|
||||||
|
|
||||||
|
let result = CubeWalker::walk_all(world_map, instructions)?;
|
||||||
|
assert_eq!(result.pos, Pos2::new(6, 4));
|
||||||
|
assert_eq!(result.facing, Direction::North);
|
||||||
|
assert_eq!(result.value(), 5031);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue