Improve Pos
This commit is contained in:
parent
651ccb9cba
commit
88be9a39a0
6 changed files with 649 additions and 45 deletions
14
data/day22/example01.txt
Normal file
14
data/day22/example01.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
...#
|
||||||
|
.#..
|
||||||
|
#...
|
||||||
|
....
|
||||||
|
...#.......#
|
||||||
|
........#...
|
||||||
|
..#....#....
|
||||||
|
..........#.
|
||||||
|
...#....
|
||||||
|
.....#..
|
||||||
|
.#......
|
||||||
|
......#.
|
||||||
|
|
||||||
|
10R5L5R10L4R5L5
|
||||||
202
data/day22/input.txt
Normal file
202
data/day22/input.txt
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -89,9 +89,9 @@ where
|
||||||
fn from(value: Direction) -> Self {
|
fn from(value: Direction) -> Self {
|
||||||
match value {
|
match value {
|
||||||
East => Pos2D::new(T::one(), T::zero()),
|
East => Pos2D::new(T::one(), T::zero()),
|
||||||
North => Pos2D::new(T::zero(), T::one()),
|
North => Pos2D::new(T::zero(), -T::one()),
|
||||||
West => Pos2D::new(-T::one(), T::zero()),
|
West => Pos2D::new(-T::one(), T::zero()),
|
||||||
South => Pos2D::new(T::zero(), -T::one()),
|
South => Pos2D::new(T::zero(), T::one()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
|
use super::direction::Direction;
|
||||||
use super::math::gcd;
|
use super::math::gcd;
|
||||||
use num_traits::{Float, Num, NumCast, Signed};
|
use num_traits::{CheckedAdd, CheckedSub, Float, Num, NumCast, Signed};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Add, Div, Mul, Sub};
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
pub type Pos2D<T> = Pos<T>;
|
||||||
pub struct Pos2D<T>([T; 2]);
|
|
||||||
|
|
||||||
impl<T> Pos2D<T> {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
|
pub struct Pos<T>([T; 2]);
|
||||||
|
|
||||||
|
impl<T> Pos<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(x: T, y: T) -> Pos2D<T> {
|
pub fn new(x: T, y: T) -> Pos<T> {
|
||||||
Pos2D([x, y])
|
Pos([x, y])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_x(&self) -> &T {
|
pub fn get_x(&self) -> &T {
|
||||||
|
|
@ -21,34 +24,34 @@ impl<T> Pos2D<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + Default> From<&[T]> for Pos2D<T> {
|
impl<T: Copy + Default> From<&[T]> for Pos<T> {
|
||||||
fn from(value: &[T]) -> Self {
|
fn from(value: &[T]) -> Self {
|
||||||
match value.len() {
|
match value.len() {
|
||||||
0 => Pos2D::new(T::default(), T::default()),
|
0 => Pos::new(T::default(), T::default()),
|
||||||
1 => Pos2D::new(value[0], T::default()),
|
1 => Pos::new(value[0], T::default()),
|
||||||
_ => Pos2D::new(value[0], value[1]),
|
_ => Pos::new(value[0], value[1]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<[T; 2]> for Pos2D<T> {
|
impl<T> From<[T; 2]> for Pos<T> {
|
||||||
fn from(value: [T; 2]) -> Self {
|
fn from(value: [T; 2]) -> Self {
|
||||||
Pos2D(value)
|
Pos(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<(T, T)> for Pos2D<T> {
|
impl<T> From<(T, T)> for Pos<T> {
|
||||||
fn from(value: (T, T)) -> Self {
|
fn from(value: (T, T)) -> Self {
|
||||||
Pos2D([value.0, value.1])
|
Pos([value.0, value.1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Copy,
|
T: Copy,
|
||||||
{
|
{
|
||||||
pub fn splat(v: T) -> Pos2D<T> {
|
pub fn splat(v: T) -> Pos<T> {
|
||||||
Pos2D::new(v, v)
|
Pos::new(v, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x(&self) -> T {
|
pub fn x(&self) -> T {
|
||||||
|
|
@ -60,26 +63,26 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Num + Ord + Copy,
|
T: Num + Ord + Copy,
|
||||||
{
|
{
|
||||||
pub fn normalize(self) -> Result<(Pos2D<T>, T), Pos2D<T>> {
|
pub fn normalize(self) -> Result<(Pos<T>, T), Pos<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| (Pos2D::new(self.x() / ggt, self.y() / ggt), ggt))
|
.map(|ggt| (Pos::new(self.x() / ggt, self.y() / ggt), ggt))
|
||||||
.ok_or(self)
|
.ok_or(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Float,
|
T: Float,
|
||||||
{
|
{
|
||||||
pub fn normal(self) -> Result<(Pos2D<T>, T), Pos2D<T>> {
|
pub fn normal(self) -> Result<(Pos<T>, T), Pos<T>> {
|
||||||
let length = self.length();
|
let length = self.length();
|
||||||
if length == T::zero() {
|
if length == T::zero() {
|
||||||
Err(self)
|
Err(self)
|
||||||
|
|
@ -89,7 +92,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Num + NumCast,
|
T: Num + NumCast,
|
||||||
{
|
{
|
||||||
|
|
@ -110,20 +113,20 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Ord + Copy,
|
T: Ord + Copy,
|
||||||
{
|
{
|
||||||
pub fn max_components(self, other: Pos2D<T>) -> Self {
|
pub fn max_components(self, other: Pos<T>) -> Self {
|
||||||
Pos2D::new(self.x().max(other.x()), self.y().max(other.y()))
|
Pos::new(self.x().max(other.x()), self.y().max(other.y()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn min_components(self, other: Pos2D<T>) -> Self {
|
pub fn min_components(self, other: Pos<T>) -> Self {
|
||||||
Pos2D::new(self.x().min(other.x()), self.y().min(other.y()))
|
Pos::new(self.x().min(other.x()), self.y().min(other.y()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Signed,
|
T: Signed,
|
||||||
{
|
{
|
||||||
|
|
@ -132,7 +135,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Float,
|
T: Float,
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +144,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> fmt::Display for Pos2D<T>
|
impl<T> fmt::Display for Pos<T>
|
||||||
where
|
where
|
||||||
T: Num + fmt::Display,
|
T: Num + fmt::Display,
|
||||||
{
|
{
|
||||||
|
|
@ -150,53 +153,79 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, P: Into<Pos2D<T>>> Add<P> for Pos2D<T>
|
impl<'a, T, P: Into<Pos<T>>> Add<P> for Pos<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();
|
||||||
Pos2D::new(self.x() + rhs.x(), self.y() + rhs.y())
|
Pos::new(self.x() + rhs.x(), self.y() + rhs.y())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P: Into<Pos2D<T>>> Sub<P> for Pos2D<T>
|
impl<T, P: Into<Pos<T>>> Sub<P> for Pos<T>
|
||||||
where
|
where
|
||||||
T: Num + Copy,
|
T: Num + Copy,
|
||||||
{
|
{
|
||||||
type Output = Pos2D<T>;
|
type Output = Pos<T>;
|
||||||
fn sub(self, rhs: P) -> Self::Output {
|
fn sub(self, rhs: P) -> Self::Output {
|
||||||
let rhs = rhs.into();
|
let rhs = rhs.into();
|
||||||
Pos2D::new(self.x() - rhs.x(), self.y() - rhs.y())
|
Pos::new(self.x() - rhs.x(), self.y() - rhs.y())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Mul<T> for Pos2D<T>
|
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 {
|
||||||
Pos2D::new(self.x() * rhs, self.y() * rhs)
|
Pos::new(self.x() * rhs, self.y() * rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Div<T> for Pos2D<T>
|
impl<T> Div<T> for Pos<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 {
|
||||||
Pos2D::new(self.x() / rhs, self.y() / rhs)
|
Pos::new(self.x() / rhs, self.y() / rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pos2D<T>
|
impl<T> Pos<T>
|
||||||
where
|
where
|
||||||
T: Num + Signed + Copy,
|
T: Num + Signed + Copy,
|
||||||
{
|
{
|
||||||
pub fn taxicab_between(self, other: Pos2D<T>) -> T {
|
pub fn taxicab_between(self, other: Pos<T>) -> T {
|
||||||
(self - other).abs()
|
(self - other).abs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Pos<T>
|
||||||
|
where
|
||||||
|
T: Num + Copy + CheckedAdd + CheckedSub,
|
||||||
|
{
|
||||||
|
pub fn check_add(self, direction: Direction) -> Option<Self> {
|
||||||
|
match direction {
|
||||||
|
Direction::East => self
|
||||||
|
.x()
|
||||||
|
.checked_add(&T::one())
|
||||||
|
.map(|x| Pos::new(x, self.y())),
|
||||||
|
Direction::North => self
|
||||||
|
.y()
|
||||||
|
.checked_sub(&T::one())
|
||||||
|
.map(|y| Pos::new(self.x(), y)),
|
||||||
|
Direction::West => self
|
||||||
|
.x()
|
||||||
|
.checked_sub(&T::one())
|
||||||
|
.map(|x| Pos::new(x, self.y())),
|
||||||
|
Direction::South => self
|
||||||
|
.y()
|
||||||
|
.checked_add(&T::one())
|
||||||
|
.map(|y| Pos::new(self.x(), y)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
357
src/days/day22/mod.rs
Normal file
357
src/days/day22/mod.rs
Normal file
|
|
@ -0,0 +1,357 @@
|
||||||
|
use super::template::{DayTrait, ResultType};
|
||||||
|
use crate::common::{
|
||||||
|
direction::Direction,
|
||||||
|
parser::{eol_terminated, extract_result, ignore, usize},
|
||||||
|
pos::Pos2D,
|
||||||
|
};
|
||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
character::complete::{char, multispace0},
|
||||||
|
combinator::value,
|
||||||
|
error::Error,
|
||||||
|
multi::many1,
|
||||||
|
Err, IResult, Parser,
|
||||||
|
};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
const DAY_NUMBER: usize = 22;
|
||||||
|
|
||||||
|
pub struct Day;
|
||||||
|
|
||||||
|
impl DayTrait for Day {
|
||||||
|
fn get_day_number(&self) -> usize {
|
||||||
|
DAY_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
|
let (world_map, instructions) = Day::parse(lines)?;
|
||||||
|
let world = WrappingWorld::new(world_map);
|
||||||
|
let walker = Walker::walk_all(&world, instructions)?;
|
||||||
|
Ok(ResultType::Integer(walker.value()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
|
let (world_map, instructions) = Day::parse(lines)?;
|
||||||
|
let world = CubeWorld::new(world_map);
|
||||||
|
let walker = Walker::walk_all(&world, instructions)?;
|
||||||
|
Ok(ResultType::Integer(walker.value()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Day {
|
||||||
|
fn parse(input: &str) -> Result<(WorldMap, Vec<Instruction>), WorldError> {
|
||||||
|
let (input, world) = WorldMap::parse(input)?;
|
||||||
|
let input = ignore(multispace0)(input)?;
|
||||||
|
let instructions = extract_result(Instruction::parse)(input)?;
|
||||||
|
Ok((world, instructions))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum WorldError {
|
||||||
|
#[error("Not a valid description: {0}")]
|
||||||
|
ParsingError(String),
|
||||||
|
#[error("No Starting Point found")]
|
||||||
|
NoStartingPoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Err<Error<&str>>> for WorldError {
|
||||||
|
fn from(error: Err<Error<&str>>) -> Self {
|
||||||
|
WorldError::ParsingError(error.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WorldMap {
|
||||||
|
tiles: Vec<Vec<Option<bool>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for WorldMap {
|
||||||
|
type Err = WorldError;
|
||||||
|
|
||||||
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(extract_result(WorldMap::parse)(input)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldMap {
|
||||||
|
pub fn new(tiles: Vec<Vec<Option<bool>>>) -> Self {
|
||||||
|
Self { tiles }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &str) -> IResult<&str, WorldMap> {
|
||||||
|
let tile = alt((
|
||||||
|
value(Some(false), char('#')),
|
||||||
|
value(Some(true), char('.')),
|
||||||
|
value(None, char(' ')),
|
||||||
|
));
|
||||||
|
let line = eol_terminated(many1(tile));
|
||||||
|
let mut lines = many1(line).map(|tiles| WorldMap::new(tiles));
|
||||||
|
lines.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_first(&self, pos: Pos2D<usize>, facing: Direction) -> Option<Pos2D<usize>> {
|
||||||
|
match facing {
|
||||||
|
Direction::East => self.tiles.get(pos.y()).and_then(|row| {
|
||||||
|
row.iter()
|
||||||
|
.position(|t| t.is_some())
|
||||||
|
.map(|x| Pos2D::new(x, pos.y()))
|
||||||
|
}),
|
||||||
|
Direction::West => self.tiles.get(pos.y()).and_then(|row| {
|
||||||
|
row.iter()
|
||||||
|
.rposition(|t| t.is_some())
|
||||||
|
.map(|x| Pos2D::new(x, pos.y()))
|
||||||
|
}),
|
||||||
|
Direction::South => self
|
||||||
|
.tiles
|
||||||
|
.iter()
|
||||||
|
.position(|row| pos.x() < row.len() && row[pos.x()].is_some())
|
||||||
|
.map(|y| Pos2D::new(pos.x(), y)),
|
||||||
|
Direction::North => self
|
||||||
|
.tiles
|
||||||
|
.iter()
|
||||||
|
.rposition(|row| pos.x() < row.len() && row[pos.x()].is_some())
|
||||||
|
.map(|y| Pos2D::new(pos.x(), y)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tile(&self, pos: Pos2D<usize>) -> Option<bool> {
|
||||||
|
self.tiles
|
||||||
|
.get(pos.y())
|
||||||
|
.and_then(|row| row.get(pos.x()))
|
||||||
|
.copied()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
||||||
|
enum Instruction {
|
||||||
|
Walk(usize),
|
||||||
|
Right,
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
fn parse(input: &str) -> IResult<&str, Vec<Instruction>> {
|
||||||
|
many1(alt((
|
||||||
|
usize.map(|v| Instruction::Walk(v)),
|
||||||
|
value(Instruction::Right, char('R')),
|
||||||
|
value(Instruction::Left, char('L')),
|
||||||
|
)))
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Walker {
|
||||||
|
pos: Pos2D<usize>,
|
||||||
|
facing: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walker {
|
||||||
|
fn new<W: World>(world: &W) -> Result<Self, WorldError> {
|
||||||
|
let facing = Direction::East;
|
||||||
|
if let Some(pos) = world.get_start(Pos2D::new(0, 0), facing) {
|
||||||
|
Ok(Walker { pos, facing })
|
||||||
|
} else {
|
||||||
|
Err(WorldError::NoStartingPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(&self) -> i64 {
|
||||||
|
(self.pos.y() + 1) as i64 * 1000
|
||||||
|
+ (self.pos.x() + 1) as i64 * 4
|
||||||
|
+ Walker::face_value(self.facing)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn face_value(facing: Direction) -> i64 {
|
||||||
|
match facing {
|
||||||
|
Direction::East => 0,
|
||||||
|
Direction::North => 3,
|
||||||
|
Direction::West => 2,
|
||||||
|
Direction::South => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act<W: World>(mut self, world: &W, instruction: Instruction) -> Self {
|
||||||
|
match instruction {
|
||||||
|
Instruction::Right => self.facing = self.facing.turn_right(),
|
||||||
|
Instruction::Left => self.facing = self.facing.turn_left(),
|
||||||
|
Instruction::Walk(steps) => {
|
||||||
|
for _ in 0..steps {
|
||||||
|
let Some((next_pos, next_facing)) = world.step(self.pos, self.facing) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
self.pos = next_pos;
|
||||||
|
self.facing = next_facing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn walk_all<W: World>(
|
||||||
|
world: &W,
|
||||||
|
instructions: Vec<Instruction>,
|
||||||
|
) -> Result<Self, WorldError> {
|
||||||
|
let mut walker = Walker::new(world)?;
|
||||||
|
|
||||||
|
for instruction in instructions {
|
||||||
|
walker = walker.act(world, instruction);
|
||||||
|
}
|
||||||
|
Ok(walker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::common::file::read_string;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part1() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = ResultType::Integer(6032);
|
||||||
|
let result = day.part1(&lines)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = ResultType::Nothing;
|
||||||
|
let result = day.part2(&lines)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world, instructions) = Day::parse(&lines)?;
|
||||||
|
|
||||||
|
assert_eq!(world.tiles.len(), 12);
|
||||||
|
assert_eq!(instructions.len(), 13);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn walk() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world_map, _) = Day::parse(&lines)?;
|
||||||
|
|
||||||
|
let world = WrappingWorld::new(world_map);
|
||||||
|
|
||||||
|
let mut walker = Walker::new(&world)?;
|
||||||
|
assert_eq!(walker.pos, Pos2D::new(8, 0));
|
||||||
|
|
||||||
|
walker = walker.act(&world, Instruction::Walk(10));
|
||||||
|
assert_eq!(walker.pos, Pos2D::new(10, 0));
|
||||||
|
|
||||||
|
walker = walker.act(&world, Instruction::Left);
|
||||||
|
|
||||||
|
walker = walker.act(&world, Instruction::Walk(2));
|
||||||
|
assert_eq!(walker.pos, Pos2D::new(10, 10));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn walk_all() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let (world_map, instructions) = Day::parse(&lines)?;
|
||||||
|
let world = WrappingWorld::new(world_map);
|
||||||
|
|
||||||
|
let walker = Walker::walk_all(&world, instructions)?;
|
||||||
|
assert_eq!(walker.pos, Pos2D::new(7, 5));
|
||||||
|
assert_eq!(walker.facing, Direction::East);
|
||||||
|
assert_eq!(walker.value(), 6032);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ mod day18;
|
||||||
mod day19;
|
mod day19;
|
||||||
mod day20;
|
mod day20;
|
||||||
mod day21;
|
mod day21;
|
||||||
|
mod day22;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
pub use template::DayTrait;
|
pub use template::DayTrait;
|
||||||
|
|
@ -28,7 +29,7 @@ pub mod day_provider {
|
||||||
use super::*;
|
use super::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const MAX_DAY: usize = 21;
|
const MAX_DAY: usize = 22;
|
||||||
|
|
||||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||||
match day_num {
|
match day_num {
|
||||||
|
|
@ -53,6 +54,7 @@ pub mod day_provider {
|
||||||
19 => Ok(Box::new(day19::Day)),
|
19 => Ok(Box::new(day19::Day)),
|
||||||
20 => Ok(Box::new(day20::Day)),
|
20 => Ok(Box::new(day20::Day)),
|
||||||
21 => Ok(Box::new(day21::Day)),
|
21 => Ok(Box::new(day21::Day)),
|
||||||
|
22 => Ok(Box::new(day22::Day)),
|
||||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue