Compare commits

..

No commits in common. "41b013b5e931a2407bf0a0e13e86dcbd1ebf4ba2" and "4a4117882a03d76c6f92d293cdbbaaa5a5f98820" have entirely different histories.

51 changed files with 10551 additions and 4970 deletions

View file

@ -4,6 +4,6 @@ These are my solutions for [Advent of Code 2022](https://adventofcode.com/). Tha
If you look at the code and see ways I could improve it, please do not hesitate to contact me. I am always grateful for everything that makes me a better programmer.
Also, if you ever look for a programmer and think my code is any good, please consider contacting me.
Also, if you ever look for a programmer and think my code is any good, please also contact me at once.
All code is published under the [Unlicense](https://unlicense.org/)

View file

@ -1,15 +0,0 @@
root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32

File diff suppressed because it is too large Load diff

View file

@ -1,14 +0,0 @@
...#
.#..
#...
....
...#.......#
........#...
..#....#....
..........#.
...#....
.....#..
.#......
......#.
10R5L5R10L4R5L5

File diff suppressed because one or more lines are too long

View file

@ -1,7 +0,0 @@
....#..
..###.#
#...#.#
.#...##
#.###..
##.#.##
.#..#..

View file

@ -1,6 +0,0 @@
.....
..##.
..#..
.....
..##.
.....

View file

@ -1,70 +0,0 @@
###.###..#.....#..#.##..##.#...###.#####..#.##.#####.###..######.#.#.#
....##.#.#.#......#.###..##..###...#.#...####.##...#.#....#.##.##....#
#..#.#.#.##.........#####....#..##.......#.#.#.##....#.#....##..##..##
#.##.##....#.##.#.###.#...#..#.##.####.#.#.#.#.###.###.####....#.#.#.#
.#.#.#####.##...##.##..#..#.#..####.#..#.#.#######..#.##.#.###.#.....#
..#..#....###.#....###.##.#..#..#.#..#.#.#...##...#....##....#..##...#
###..........#..#.###....##.##...#####.###.#.##...#.##.....#.##.#.####
#.###.####...#..#.##.....#.####.##..##...#..##.....#...###..##.######.
.....#.####..##.#...#..##.###..#.##.####..#...#.###..##..#.#.###...#.#
.#.#.#..##.##..#.#.##.#.##.#.##..##.##.#..##.#.##..#####.#.###.#.##..#
###.#.###.##.##....#.#.#...##...#.#..#.#..#.#..##.#....#####...#...###
.#.#.#.#.#..#....###.#.##.#.#.####..#.#.#...#.#.#.###....#....#...##..
#.#....#........#....######...#....#..#.###...#.....###..###...##..#..
.###.##....#.#.#.###..###....#.#.#..###.##..#..#..#####.#.###.#..##..#
..###.##..####.....#...##.#....#...###..#.###.#...##....####........#.
#####.##..#..##..###....#.##....#.#...##.########...#.#.#..##..##..###
.##..#...##.....###.#...#..###.######.#.#...#...##..#.....#..#.##..#.#
.#####..#.######.#..#..#.#.#.##....##..#.......#....#.##.#.#..##...#.#
.#.##..#..###..#.######........##.##.##..#....#.###.######.##.#.#..##.
##.##.#..####.#....###.#..##..#...#.#####.#..#...#....##...#.###..#.##
#....##..#...#.#.###.#.#...##.#.#..###...####..##.#..##.#.#####.....##
.##.#..#.##.##.#.##.....#######..#...#.##.####...#.....#.##..#...##...
#.#.#.#####...#####..#.##....#.#.####.#..#.##..#.#.#...#..##.#...##...
#.#.#.#..#.#.#..#..###.#.....#.#.#..#.#####.#.#..#...#.###.###...#.#..
.####..####.#.#..#...##.##.#....#.#.....#.##.###..#...###.###.#....##.
....##.#.##.###.#..##.#..#.......##...#.###...#.......#..####.#.#.####
....#.#......####..#.#..#..#..#.##..#..#..###..#.#.##.#.##..###..#..#.
.##.#.....##.#..#..#..####..#.#.#...###.##.##.##...#..##..##.##...##..
#..####..#...#.#....##...##..#.....##.#######..#.####...##..######.#..
..#.##.##....##.#######..##...#..#..###.....##.#.#..#.#...##..###.####
#####......##..#.#..#......#..####.####...#####..######...##.###..##..
###..##.#.##....##..######.#.#.#......#..##.#.##.#.##...#.##...###..##
.....#.#.#.##..#.#####.##.#.#.....#.#..#.###.##..#.###.##..##.###....#
.###.####..####.###....####.#.##..##..#.....##.###.##.######.#..#.#...
.#.#######.#.#.###....#####..#.#.#..#.#.#.#.#..##..#..#...##..##..#.#.
.##..####..#.#...###.#...#....####...###.#..#.#.....#.#.##.#..##.#.#..
.#.#.#.#####..##.####..#..##......#..#.###.#..#....#...#....#...#.####
..##.##.###.#..#..#.#..#.###......##.#..#..#.#.####.#.#.#.####.##.....
##.#..#..##.#######..#..#....##..#...########.##.#.#...##...##.##.....
.#...#####.#..##.##.###.####.....###.#......########.#..##.##.#....###
...####.###....##..#..#....#..##.####..##..#..###.......#.#.###.##...#
.#.#......#####...##....#.#..#######.######.#.#.##.#..###..#....##..#.
..###.#.#####..##.#.####....#.###..#..#.##.#####.##..##.##..##..#.##.#
.####.#.######.###.###..#......#...#...#..#..##.###.##.#.####.#.#.##.#
#####.###.##.#########..###..#.#.##.#..#..##...#.###.#.#......###..##.
###...###.##.#.###.#.##.####.#######..##.#....#####.###.#..#.#...###..
.######...#...#..###......#.#.##........#..##.#.#.##.#....###.....##.#
#.#.....##.##.##..##..##.#.##.###.##.###.##.#.##.###....##..#.#.##....
#.####.##...##....#.#.....#.#.###..#..####.....#.##..##...##.###.#..##
#####.##.#...#....#..###.##..##...#....#....##.#.#....#####....####.##
####...##...#.###.##.##.#####...#.....##..#####...########..####......
#.#..##.##.#.######.###.#.##.......#......####..##.#.#.#..##.###.#..#.
#..#..########..##..#.######..#.#...#..#.#####.###.####......##..###.#
#..#.#...#..#....#..##.##.#..###.####.###########.#..#.###.#.##.###..#
##.#..#..#.....#.##.#..######..#..#...#.#...###.#.#.#.#.#.########...#
..###.#.##......###..#...##......#####.#.#....####.....##.#..#.#..#.#.
..######....#..##.#.#..#.##.#..##.##.#....##....###.#.##.##..##....###
.#####....##....###.#.#######.....##.###.#..#.#.#..###...#.##..#..#.#.
#..#####...#..##..#..#..#....#.##....#..##.......##..#....#.#..#.#.###
#.#..#####...#...#...##.#....#..##.#.##....#####.....#.##.....#..#.###
##.#.#....#...#.####....###....#...#.##...##.#....#.#.#.##......##.###
...##.#.#..#.###..#.#...##.#...##.#...##...#..###.#.###...###..#..##.#
.#####...#.#..###.#.##.##.#.##..##.#.#.....##....#.#..#.#.#..#####.###
#.####.####..###......##.....#.#.....##.#..##.##.#....###.....####..##
#..#####...#....#...###..##...###..####..####..#...#####.#####..#.###.
####.###.###....####....##..#..####...#....###.##.#.##...##.......####
.#.###....####..#..#.###.##.##.####..##.#..###..#..#.#.#..#..#.##..##.
....##......####.####.######...###.....###.#.#..##.#.##.......#...#...
##.###.##...#.....#.##.#.......####.##.###..###.#.#..#.##..#.####....#
....#..##.#...##.##.#.#.#..#.#..#..#.####....#####..#.....#####.#.....

View file

@ -1,6 +0,0 @@
#.######
#>>.<^<#
#.<..<<#
#>v.><>#
#<^v^^>#
######.#

View file

@ -1,7 +0,0 @@
#.#####
#.....#
#>....#
#.....#
#...v.#
#.....#
#####.#

View file

@ -1,37 +0,0 @@
#.####################################################################################################
#>^v^v>><.>>><>^v<>^v>v<>^<<>>v>>.>><v>^<>^.<>v.>v^>vvvv<<<<>>^v^^^>><><.^^^.<<<>><^v^vv^v<.v.v>>.vv<#
#>^><>^v^<>.<v><.<.v<v>vv<^<vvvv.^^...<<<^^vv<<v<>>>>v^<>>^^^><.>>>>v<<>^.>.^^<v<.><<.<><.>>>^^^>>.<>#
#<.vvv^^v>^<<^^^>.^<..v.v<<^>vv<>>>v^>vv<><v<<vv<v^>v<^^^^v>.<.vv><v>v<v<^v^>>^<^^<>^>v^<^.^^v><>.><>#
#>..<^^<>>v>><>vv^<v.^v.>^<.v^<v><<^<><v^v<.vv^>>vvv.vvv<>v>><<.>^><v^<<<v<<>><v><^v.v.<>^>v><v<^<<.>#
#>^^<v>v><^<^>^<vvv^.>^^><^>><^^.vv>v<>v<>^>^v><v>>.>>^<v><^v^v^>v^<.<v^^^<>vvv^^<.<v<>>>.^>><<.vvv.>#
#><v>v^>^<>v^>>^^<>.><^v<^vvv>vv<.><>v>v<..<^>>v^v<<<<v>v^.<<<^^^^.^v^<<>^vvv>^><.^v<^<>vv><<^<>^<^<<#
#.^vvv<vv>^^v<>v<<v^^>.<<<v><<vv<v>>^<vv<v>><<^>^vv<<>^<<>^v.>>.<>>^<^<v<^^^<.vv^vv><^><v^^v>v^v>^vv.#
#<v.>^<^><^..>^.>>v>>vv<^<.<vv<<v^v<.vvv>>.^v^^<^v<^<.^vv>v>^><v<v^>^v>>>>^^^v>^^>>>v<v^vv<.^><vv<>^<#
#><>v^>^v<.<.>.<^.<>>^^>v^<v^<v>vvv<<^>vv<^^>^<^>v<.^.v<<.v.v^v>v><>..^^<.v^^<.<<<<<><>v><v<^^v<^<vv<#
#><<>v><>^^^.<.<<>^v>v^>^>>>>v>.^>.v>.^>^.v<^<>v.v>v^v^^>^v>>>.<^v^^.>>>>^<<v<.^^>>>>^><v^v>vvv^>^><<#
#><vvvv.>v^.v>>><>^>><><v^<.>.^<^v.>.^<^>>^v<.<^^v<^><..<vv^v<>vv<<vv^v.>>^.<v^>^>^<^<^>><><v<<>.><<>#
#>.<^^v^><<<.v<vv<>^<>v^vv<^^.<>>.^<vv>>^.v.>^><v^^>.<<>v<>^v<v><v^^<v.v><<>vvv.v<>v>vvv^<>v^>^v<<<><#
#.>><<vv><>vv>v..<<^^vv^>^<>^^v>v^vv>.><>^<vv^^<v><^^>><>>v<<v>^v^^><>.^.vv>v.^^>^vv<><.>>^^<^<><>v>.#
#><<v>v<><^<<^><..<^>v<<^.^^<.^^v<>>^v>.^^^<<v>v>><><.<>>^v<^.v<vv<<^>>>vv^<vv.>^^^<><>v.>.^<^v<vv^><#
#<.>vvv<v>^.vv.>.<v^.<..<>^v<>>^^vv>.>^v^>>>vv^<v><^v.v^>>>>><>v>v<>v^^>.^^v>^v><<>vv^<><>>v<^<v<<<<>#
#<v<><^v<>>.<v^><<>^>v<<.v^v.<^v.^<vv<.v.^<v><><><v^^>v<>.vv^>vv<<^^<^^^><<>^>.^v<<<^<^<^^>>vv.<v.v<.#
#<>vv^^<<^^^v^^<v^vvv^>^^.vvv^^^^v^>^^^v<^v><<^^^.<><v.vv<<^v<^>^><.^><^<>.v>^vv^v<<^<.<^.^>vv>^v>^<.#
#<.vv>><.<vv<^^^<.^v><>>>><<^^<><^<vv<<>^v..>v<^^v><^><<^^^<<>^^>.>v><^<v<><v>v^<v<v^v.vv>v^<<<v>.^>>#
#<^vv<<v^><>^^<<^.<v^>^vv.><^>v.>^vv.<^v^^<v<<v<><^<>v<<v<<><>^v^>v^^^><^>>^.<v.^<.^^.v<^v<>>.<<<v>^<#
#>>..^^^<v.v<>>^^<v^..^vv<>.<>>>^><>^<v><<.v.^v^^<^<><>..>^<>.>^^<v.^v<v<<^v>^vv.v>.<.^^<v^<<>^^<^v<.#
#>>vvv^v^v^>v<<vv.^.^<<vv^>^>v<.^>^<v^>^^<^<>^.><^v<<<v>>..v><>v^>^>><^^<>.v<vv^>>>>v<^v.v>v.>^vv^.^<#
#<^<>.^<>>^<^v>.^v.><.<v.v.^v><^>.>^<>^^>>^^^.<^<>><>^><vvv^>^.v^^v>vv<<vvv>^.^>><.<>^.^<>.>>>v>v<v^>#
#<^^vv<<^<^.<^>^<vv.>>.>><^<^^^<<>.v>v>v>v.^<<v<.<>v<<<>v<>^<v.^<<<v<<>>>>.^><^^<>>>^<><^.^.v^^v^^>..#
#..v>vv^<vv>><^>^^^<v>><v^^><>.^^><^<><>^>><v>^v<><v.^<>><.>^>v^>^v<><v^v>><><^>v>.v^v>v<><.^^.>.v.^<#
#.<v>v<^v.<<<vv<>^v^vv<^v^>^^v^<>^>>vv<^^v^^^^<.^v<>^<<^<v^v<><v>v^<<>>.vvv.>>^v>>>vv<<><^v<v^v<<.>>>#
#<<vv.v^.>..v><v<><vv<<<<.>vv>v>>^v^v<v^<v<>v<<v^v^v^<<v^>v<^^^>v<^^^^^>>><<v<>v<>.>v^v<><.<<v<>^.^^.#
#>>>>>^^.><^<>^<>v>.<^vv^^<>>^v><^<^>vv^<v><.>^v>.v<v<>>>>^.^>^v^^^<vvv^.<<..^v<.>^><<>><<^<.<<v<>^v<#
#<.v.^>.^<.v<^^<.<^<<^.^<>><^<<^<><.<>.><<<><^v<v>^^v<<v>><^<>>^>^^>vv>^<.^<v><>.><v^v<vv^<^^<v>>>^v<#
#<>v^>^^v>>^<^>v>>>.>^.><>..>^v>><>v<>^>.^v<<<<^>v>v><v^<^>^^>^<<>^>vv><<<v>v^^v<vv<^<^>vv^<.v^<^v>^>#
#><<vvv.<<<vv<vv^^^v^v<^>vv^<>^^v<>v<^^^^><>^^>^<<<>v.vv^<><<><.^<<<^>>^vv<.v^^>vv><<<>v.<><v^><^^^v.#
#<><v<v><^<^^vvv^>><><<<.<<vv<^<>.^<>>^v^>>vvv>>v..><<^^.v<><^^><^>>^<<v<<^v^>v>vvvvv...^<v^<^<v<.^<.#
#>><<<v^^v^>^<<.>>vvv^v<^<>.^v^<^v<^^>>v<.>v<v..^^v.^>.v>^>>v^<<.v<^<..>.v<<..^^^v>v<.^<.>v^v^<v^v>^>#
#<^>v<>^<<v>>vv>.><^v<^.><<^v>v<^<<<v^.^v.><>v><>^v><<<v.>vv<>vv>><>>^><><v>>v^.^v<<v^vvv<.<<>.v^^v<>#
#<^<^><<>v..>v^>v>.v<<<^<<<v^v<<>^<.<.<v.v^<><v^<<>^><vv.v>>vv^.v^.^^>^^<<^^>.vv<.>.^^<>.<<v><vv<<>>>#
#>v>>vv<.v<v.<^.^^><><^^v>^>>>>^>vv><<v>vv^.>>v^^vv^v^.>^v<>>>^^^>..vv^.^><..>>v<>^>^^<<.v<>v^^^v>^v>#
####################################################################################################.#

View file

@ -1,13 +0,0 @@
1=-0-2
12111
2=0=
21
2=01
111
20012
112
1=-1=
1-12
12
1=
122

View file

@ -1,135 +0,0 @@
1--
1-
1--02
1122-=2-==0-0=1-2
2=1-==-1022=10
1==-02211022=000
101==210-21=2
11010==0==221=0=02
11=1101020=02-11
1===
10-2
10=02=-=02
1-1--112-202--0
11-=-0-002=0112-1
2--0-2=02=-22
1001-2
1=101=2=2102-201--1
1-2==1100--
2-01=--=1-11
101-=0010-210==
1=-21-022121110
1==21
22==1
2-0-=2=1
2--0=10-00--2-
1--1=011-2
1-100-2
11-1
112
222200110-11110-2=
1==2-210-1122=-2022
2==1-20-2-=-21
21=1-=0=-1-=-=-12
210---=-1===-===00
1--21=01-022=2-
11-=
21-2
2-22-0
1==-
1-----01
1-0-21-
211212-22-12--=2
1=220=10=20200-0
21-=11-==20=1
102-2==2-==-0
1=0=01-
22=1--0111
1-----=1==00===0-
20==-1-121-
1=--2--21-202=21
1-0122=--=
2==-2--===-=-1=-012
2220=-00-10-
1==0=---10212-1-
1221=-022120
100112
2-=0=220-=01-1=12-
1=00
2122121=2
2-==
12-21===10220
1=0
22-1=--01-==0
1-2-2202=1=02
11-===21-
1-22=0102-=-222-1
1021=0=21=0=11=2-2
1===2222=0-0=
20=2-=0=
1=2==02-1-100200
1=1211211----
12=1=1---=20
2--2=2=1100020200
1=0-002-11-10----
1=1=-1=-0
1-21=1=02=21=20
1-=0-002-0=--=0=
20-===-1=
220021012
12===1001=--=2=1-
1=-02
21
2=200222122
1-2=110111=012=1-
1=02=
101==1-=1=0001
1=22--1-021
120
201-0211
1-1012211--2=-1=
12=10202=20
11-22=2=2=-0==1=00
11=-
12-1--2222
22
1120--0==
10=-0=001-
1=00200200=
1-20
1-0-
10112-202=-011
11-2=
1-=10=1-0=-21==11200
11
12-0-=01--12
1-=1121-=0
120-21=-1=10121=
1102-22
21=
1==--1==---1-==
12=0--=2210-1==
22210=1-=0
1102-20-1
1=1=2102
11--=2==-0
2=0=1=
21-0-10=
12-212-2=-==102--
1==0100210-=
12210-0-1
1=0-2-0=
10-1=211=1-2=
201-0--
1--2=00=-1-211
121=-22100=210--
122-0-1-
2=211
2=1-=2-2-0001=-1
1102--11=0=1=10==2
1==01121-2--0=12
2=21-20=-0-0
2-212=21-02=1-2-02
2=1-0
10-0-=20
2=2=201

5005
rest.txt Normal file

File diff suppressed because it is too large Load diff

5001
result.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,49 +0,0 @@
pub trait Abs {
fn abs(&self) -> Self;
fn abs_beween(&self, other: &Self) -> Self;
fn is_negative(&self) -> bool;
}
macro_rules! signed_impl {
($($t:ty)*) => ($(
impl Abs for $t {
#[inline]
fn abs(&self) -> $t {
if self.is_negative() { -*self } else { *self }
}
#[inline]
fn abs_beween(&self, other: &Self) -> Self {
(*self - *other).abs()
}
#[inline]
fn is_negative(&self) -> bool {
*self < 0
}
}
)*)
}
signed_impl!(isize i8 i16 i32 i64 i128);
macro_rules! unsigned_impl {
($($t:ty)*) => ($(
impl Abs for $t {
#[inline]
fn abs(&self) -> $t {
*self
}
#[inline]
fn abs_beween(&self, other: &Self) -> Self {
if *self >= *other {
*self - *other
} else {
*other-*self
}
}
#[inline]
fn is_negative(&self) -> bool {
false
}
}
)*)
}
unsigned_impl!(usize u8 u16 u32 u64 u128);

View file

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

View file

@ -1,18 +1,27 @@
use super::{pos2::Pos2, turn::Turn};
use num_traits::{Num, Signed};
use super::{pos::Pos, turn::Turn};
use std::{fmt::Display, ops::Add};
use Direction::*;
use Turn::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Direction {
East = 0,
North = 1,
West = 2,
South = 3,
East,
North,
West,
South,
}
impl Direction {
pub fn as_pos(&self) -> Pos<i32> {
match *self {
East => Pos::new(1, 0),
North => Pos::new(0, 1),
West => Pos::new(-1, 0),
South => Pos::new(0, -1),
}
}
pub fn is_perpendicular(&self, other: &Direction) -> bool {
match *self {
East => *other != East && *other != West,
@ -82,26 +91,35 @@ impl Display for Direction {
}
}
impl<T> From<Direction> for Pos2<T>
where
T: Num + Signed,
{
fn from(value: Direction) -> Self {
match value {
East => Pos2::new(T::one(), T::zero()),
North => Pos2::new(T::zero(), -T::one()),
West => Pos2::new(-T::one(), T::zero()),
South => Pos2::new(T::zero(), T::one()),
}
impl Add<Pos<i32>> for Direction {
type Output = Pos<i32>;
fn add(self, rhs: Pos<i32>) -> Self::Output {
Pos::add(rhs, self)
}
}
impl Add<Direction> for Pos2<i32> {
type Output = Pos2<i32>;
impl Add<Pos<i32>> for &Direction {
type Output = Pos<i32>;
fn add(self, rhs: Direction) -> Self::Output {
let rhs: Pos2<i32> = rhs.into();
self + rhs
fn add(self, rhs: Pos<i32>) -> Self::Output {
Pos::add(rhs, *self)
}
}
impl Add<&Pos<i32>> for Direction {
type Output = Pos<i32>;
fn add(self, rhs: &Pos<i32>) -> Self::Output {
Pos::add(*rhs, self)
}
}
impl Add<&Pos<i32>> for &Direction {
type Output = Pos<i32>;
fn add(self, rhs: &Pos<i32>) -> Self::Output {
Pos::add(*rhs, *self)
}
}
@ -112,3 +130,27 @@ impl Add<Turn> for Direction {
self.turn(rhs)
}
}
impl Add<&Turn> for Direction {
type Output = Self;
fn add(self, rhs: &Turn) -> Direction {
Direction::add(self, *rhs)
}
}
impl Add<Turn> for &Direction {
type Output = Direction;
fn add(self, rhs: Turn) -> Direction {
Direction::add(*self, rhs)
}
}
impl Add<&Turn> for &Direction {
type Output = Direction;
fn add(self, rhs: &Turn) -> Direction {
Direction::add(*self, *rhs)
}
}

View file

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

View file

@ -1,12 +1,8 @@
pub mod abs;
pub mod area;
pub mod direction;
pub mod file;
pub mod helper;
pub mod math;
pub mod name;
pub mod parser;
pub mod pos2;
pub mod pos3;
pub mod pos;
pub mod turn;
pub mod unit_vector;

View file

@ -1,30 +0,0 @@
use std::{fmt::Display, ops::Deref, rc::Rc};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Name(Rc<str>);
impl Name {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Deref for Name {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<&str> for Name {
fn from(value: &str) -> Self {
Self(Rc::from(value))
}
}
impl Display for Name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

View file

@ -1,7 +1,7 @@
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{line_ending, space0, space1, u32},
character::complete::{line_ending, space0, space1},
combinator::{eof, opt, value},
error::ParseError,
multi::many0,
@ -34,10 +34,6 @@ where
terminated(line, alt((line_ending, eof)))
}
pub fn usize<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, usize, E> {
u32.map(|v| v as usize).parse(input)
}
pub fn true_false<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
alt((value(true, tag("true")), value(false, tag("false"))))(input)
}

278
src/common/pos.rs Normal file
View file

@ -0,0 +1,278 @@
use super::direction::Direction;
use super::math::gcd;
use num_traits::{Num, NumCast, Signed};
use std::fmt;
use std::ops::{Add, Mul, Sub};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Pos<T>(T, T)
where
T: Num;
impl<T> Pos<T>
where
T: Num,
{
pub fn new(x: T, y: T) -> Pos<T> {
Pos(x, y)
}
}
impl<T> Pos<T>
where
T: Num + Copy,
{
pub fn splat(v: T) -> Pos<T> {
Pos(v, v)
}
pub fn x(&self) -> T {
self.0
}
pub fn y(&self) -> T {
self.1
}
}
impl<T> Pos<T>
where
T: Num + Ord + Copy,
{
pub fn normalize(&self) -> (Pos<T>, T) {
if self.0.is_zero() && self.1.is_zero() {
(*self, T::one())
} else {
let ggt = gcd(self.0, self.1);
(Pos::new(self.0 / ggt, self.1 / ggt), ggt)
}
}
}
impl<T> Pos<T>
where
T: Num + NumCast,
{
pub fn angle(&self) -> f64 {
if let (Some(x), Some(y)) = (self.0.to_f64(), self.1.to_f64()) {
y.atan2(x)
} else {
f64::NAN
}
}
pub fn angle2(&self) -> f64 {
if let (Some(x), Some(y)) = (self.0.to_f64(), self.1.to_f64()) {
(-x.atan2(-y) + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI)
} else {
f64::NAN
}
}
}
impl<T> Pos<T>
where
T: Num + Ord + Copy,
{
pub fn max_components(&self, other: &Pos<T>) -> Self {
Self(self.0.max(other.0), self.1.max(other.1))
}
pub fn min_components(&self, other: &Pos<T>) -> Self {
Self(self.0.min(other.0), self.1.min(other.1))
}
}
impl<T> Pos<T>
where
T: Num + Signed,
{
pub fn abs(&self) -> T {
self.0.abs() + self.1.abs()
}
}
impl<T> fmt::Display for Pos<T>
where
T: Num + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.0, self.1)
}
}
impl<T> Add for Pos<T>
where
T: Num,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Pos(self.0 + rhs.0, self.1 + rhs.1)
}
}
impl<T> Add for &Pos<T>
where
T: Num + Copy,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: Self) -> Self::Output {
Pos::add(*self, *rhs)
}
}
impl<T> Add<&Pos<T>> for Pos<T>
where
T: Num + Copy,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: &Self) -> Self::Output {
Pos::add(self, *rhs)
}
}
impl<T> Add<Pos<T>> for &Pos<T>
where
T: Num + Copy,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: Pos<T>) -> Self::Output {
Pos::add(*self, rhs)
}
}
impl<T> Add<(T, T)> for Pos<T>
where
T: Num,
{
type Output = Self;
fn add(self, rhs: (T, T)) -> Self::Output {
Pos(self.0 + rhs.0, self.1 + rhs.1)
}
}
impl Add<Direction> for Pos<i32> {
type Output = Self;
fn add(self, rhs: Direction) -> Self::Output {
Pos::add(self, rhs.as_pos())
}
}
impl Add<&Direction> for Pos<i32> {
type Output = Self;
fn add(self, rhs: &Direction) -> Self::Output {
Pos::add(self, rhs.as_pos())
}
}
impl Add<&Direction> for &Pos<i32> {
type Output = Pos<i32>;
fn add(self, rhs: &Direction) -> Self::Output {
Pos::add(*self, rhs.as_pos())
}
}
impl Add<Direction> for &Pos<i32> {
type Output = Pos<i32>;
fn add(self, rhs: Direction) -> Self::Output {
Pos::add(*self, rhs.as_pos())
}
}
impl<T> Sub for Pos<T>
where
T: Num,
{
type Output = Pos<T>;
fn sub(self, rhs: Self) -> Self::Output {
Pos(self.0 - rhs.0, self.1 - rhs.1)
}
}
impl<T> Sub<&Self> for Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn sub(self, rhs: &Self) -> Self::Output {
Pos::sub(self, *rhs)
}
}
impl<T> Sub for &Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn sub(self, rhs: &Pos<T>) -> Self::Output {
Pos::sub(*self, *rhs)
}
}
impl<T> Sub<Pos<T>> for &Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn sub(self, rhs: Pos<T>) -> Self::Output {
Pos::sub(*self, rhs)
}
}
impl<T> Mul<T> for Pos<T>
where
T: Num + Copy,
{
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
Pos(self.0 * rhs, self.1 * rhs)
}
}
impl<T> Mul<T> for &Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn mul(self, rhs: T) -> Self::Output {
Pos::mul(*self, rhs)
}
}
impl<T> Mul<&T> for Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn mul(self, rhs: &T) -> Self::Output {
Pos::mul(self, *rhs)
}
}
impl<T> Mul<&T> for &Pos<T>
where
T: Num + Copy,
{
type Output = Pos<T>;
fn mul(self, rhs: &T) -> Self::Output {
Pos::mul(*self, *rhs)
}
}
impl<T> Pos<T>
where
T: Num + Signed + Copy,
{
pub fn taxicab_origin(&self) -> T {
self.0.abs() + self.1.abs()
}
pub fn taxicab(&self, other: &Pos<T>) -> T {
(self.0 - other.0).abs() + (self.1 - other.1).abs()
}
}

View file

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

View file

@ -1,182 +0,0 @@
#![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(),
])
}
}

View file

@ -1,53 +0,0 @@
#![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> {
vector.is_unit().then(|| UnitVector(vector))
}
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)
}
}

View file

@ -1,9 +1,12 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use itertools::Itertools;
use std::num::ParseIntError;
use thiserror::Error;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
pub struct Day;
const DAY_NUMBER: usize = 1;

View file

@ -1,6 +1,8 @@
use super::template::{DayTrait, ResultType};
use std::cmp::Ordering;
use crate::common::file::split_lines;
use std::{cmp::Ordering, str::FromStr};
use super::template::{DayTrait, ResultType};
use thiserror::Error;
const DAY_NUMBER: usize = 2;
@ -58,7 +60,9 @@ impl Rps {
Err(RPSError::IllegalLine(line.to_owned()))?
};
Ok((first.parse()?, second.parse()?))
let first = Rps::try_from(first)?;
let second = Rps::try_from(second)?;
Ok((first, second))
}
pub fn value(&self) -> i64 {
@ -94,10 +98,10 @@ impl PartialOrd for Rps {
}
}
impl FromStr for Rps {
type Err = RPSError;
impl TryFrom<&str> for Rps {
type Error = RPSError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
match value {
"A" | "X" => Ok(Rps::Rock),
"B" | "Y" => Ok(Rps::Paper),
@ -106,7 +110,6 @@ impl FromStr for Rps {
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
enum Strategy {
Loose,
@ -121,7 +124,9 @@ impl Strategy {
Err(RPSError::IllegalLine(line.to_owned()))?
};
Ok((first.parse()?, second.parse()?))
let first = Rps::try_from(first)?;
let second = Strategy::try_from(second)?;
Ok((first, second))
}
pub fn fullfill(&self, other: &Rps) -> Rps {
@ -134,10 +139,10 @@ impl Strategy {
}
}
impl FromStr for Strategy {
type Err = RPSError;
impl TryFrom<&str> for Strategy {
type Error = RPSError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
match value {
"X" => Ok(Strategy::Loose),
"Y" => Ok(Strategy::Draw),

View file

@ -1,5 +1,6 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::Itertools;
use thiserror::Error;

View file

@ -1,5 +1,6 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::Itertools;
use std::num::ParseIntError;
use thiserror::Error;

View file

@ -1,5 +1,6 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::Itertools;
use std::num::ParseIntError;
use thiserror::Error;
@ -123,7 +124,7 @@ impl Move {
if self.to_stack >= stacks.len() {
return Err(CrateError::TooFewStacks(stacks.len(), self.to_stack));
}
let Some(from_stack) = stacks.get(self.from_stack) else {
let Some(from_stack) = stacks.get(self.from_stack ) else {
return Err(CrateError::TooFewStacks(stacks.len(), self.from_stack));
};
if from_stack.len() < self.amount {

View file

@ -1,10 +1,12 @@
use super::template::{DayTrait, ResultType};
use crate::common::{file::split_lines, name::Name};
use std::{
cell::{Cell, RefCell},
num::ParseIntError,
rc::{Rc, Weak},
};
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use thiserror::Error;
const DAY_NUMBER: usize = 7;
@ -73,7 +75,7 @@ enum DirectoryError {
#[derive(Debug)]
struct Node {
parent: Option<Weak<RefCell<Node>>>,
name: Name,
name: String,
sub_dirs: Vec<Rc<RefCell<Node>>>,
file_size: i64,
size: Cell<Option<i64>>,
@ -83,7 +85,7 @@ impl Node {
fn root() -> Node {
Node {
parent: None,
name: "/".into(),
name: "/".to_owned(),
sub_dirs: vec![],
file_size: 0,
size: Cell::new(None),
@ -94,7 +96,7 @@ impl Node {
self.sub_dirs.push(subdir);
}
fn add_file<N: Into<Name>>(&mut self, _name: N, size: i64) {
fn add_file(&mut self, _name: &str, size: i64) {
self.file_size += size;
}
@ -139,10 +141,10 @@ impl Directory {
self.node.borrow().file_size
}
pub fn add_subdir<N: Into<Name>>(&mut self, name: N) {
pub fn add_subdir(&mut self, name: &str) {
let subdir = Rc::new(RefCell::new(Node {
parent: Some(Rc::downgrade(&self.node)),
name: name.into(),
name: name.to_owned(),
sub_dirs: vec![],
file_size: 0,
size: Cell::new(None),
@ -150,7 +152,7 @@ impl Directory {
self.node.borrow_mut().add_subdir(subdir);
}
pub fn add_file<N: Into<Name>>(&mut self, name: N, size: i64) {
pub fn add_file(&mut self, name: &str, size: i64) {
self.node.borrow_mut().add_file(name, size)
}
@ -163,9 +165,8 @@ impl Directory {
.map(|node| Directory { node })
}
pub fn get_subdir<N: Into<Name>>(&self, name: N) -> Option<Directory> {
pub fn get_subdir(&self, name: &str) -> Option<Directory> {
let node = self.node.borrow();
let name = name.into();
let sub_node = node.sub_dirs.iter().find(|node| node.borrow().name == name);
sub_node.map(|node| Directory::create(node.clone()))
}
@ -186,7 +187,7 @@ impl Directory {
}
_ => {
let Some(next) = current.get_subdir(dir) else {
return Err(DirectoryError::NoSuchDirectory(dir.to_owned()));
return Err(DirectoryError::NoSuchDirectory(dir.to_owned()))
};
next
}

View file

@ -1,7 +1,9 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use itertools::{iproduct, FoldWhile, Itertools};
use std::str::FromStr;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::{iproduct, FoldWhile, Itertools};
use thiserror::Error;
const DAY_NUMBER: usize = 8;

View file

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

View file

@ -1,7 +1,7 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use itertools::Itertools;
use std::{num::ParseIntError, slice::Iter, str::FromStr};
use super::template::{DayTrait, ResultType};
use std::{num::ParseIntError, slice::Iter};
use thiserror::Error;
const DAY_NUMBER: usize = 10;
@ -14,13 +14,17 @@ impl DayTrait for Day {
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let instructions: Vec<_> = split_lines(lines).map(|line| line.parse()).try_collect()?;
let instructions = split_lines(lines)
.map(|line| Instruction::parse(line))
.collect::<Result<Vec<_>, _>>()?;
let result = CpuCycles::signal_strength(&instructions, &[20, 60, 100, 140, 180, 220]);
Ok(ResultType::Integer(result as i64))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let instructions: Vec<_> = split_lines(lines).map(|line| line.parse()).try_collect()?;
let instructions = split_lines(lines)
.map(|line| Instruction::parse(line))
.collect::<Result<Vec<_>, _>>()?;
let result = CpuCycles::draw(&instructions);
Ok(ResultType::Lines(result))
}
@ -35,18 +39,18 @@ enum CpuError {
InvalidInteger(#[from] ParseIntError),
}
#[derive(Debug, PartialEq, Eq)]
struct Instruction(Option<i32>);
enum Instruction {
Add(i32),
Noop,
}
impl FromStr for Instruction {
type Err = CpuError;
fn from_str(line: &str) -> Result<Self, Self::Err> {
impl Instruction {
pub fn parse(line: &str) -> Result<Self, CpuError> {
if line == "noop" {
Ok(Instruction(None))
Ok(Instruction::Noop)
} else if line.starts_with("addx") {
let value = line[5..].parse()?;
Ok(Instruction(Some(value)))
Ok(Instruction::Add(value))
} else {
Err(CpuError::UnknownInstruction(line.to_owned()))
}
@ -110,7 +114,10 @@ impl<'a> Iterator for CpuCycles<'a> {
self.current = None;
Some(start_register)
} else if let Some(instruction) = self.instructions.next() {
self.current = instruction.0;
match instruction {
Instruction::Add(value) => self.current = Some(*value),
Instruction::Noop => {}
}
Some(self.register)
} else {
None
@ -127,11 +134,7 @@ mod test {
#[test]
fn test_simple() -> Result<()> {
let instructions = vec![
Instruction(None),
Instruction(Some(3)),
Instruction(Some(-5)),
];
let instructions = vec![Instruction::Noop, Instruction::Add(3), Instruction::Add(-5)];
let expected = vec![1, 1, 1, 4, 4];
let result = CpuCycles::create(&instructions).collect::<Vec<_>>();
assert_eq!(result, expected);

View file

@ -1,7 +1,8 @@
use super::template::{DayTrait, ResultType};
use crate::common::parser::{
empty_lines, eol_terminated, extract_result, ignore, trim0, trim_left1, true_false,
};
use super::template::{DayTrait, ResultType};
use nom::{
branch::alt,
bytes::complete::tag,

View file

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

View file

@ -1,7 +1,9 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use itertools::Itertools;
use std::cmp::Ordering;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::Itertools;
use thiserror::Error;
const DAY_NUMBER: usize = 13;

View file

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

View file

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

View file

@ -1,8 +1,5 @@
use super::template::{DayTrait, ResultType};
use crate::common::{
name::Name,
parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1},
};
use crate::common::parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1};
use itertools::Itertools;
use nom::{
branch::alt,
@ -98,7 +95,6 @@ impl Mul<Flow> for Time {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
struct Flow(i64);
impl Deref for Flow {
type Target = i64;
@ -212,7 +208,7 @@ impl<'a> TryFrom<&'a str> for RawValve<'a> {
#[derive(Debug)]
struct Valve {
id: Name,
id: String,
idx: Index,
flow: Flow,
distances: Vec<Time>,
@ -249,7 +245,7 @@ impl Valve {
distances.sort_unstable_by_key(|(id, _)| *id);
Ok(Valve {
id: valve.id.into(),
id: valve.id.to_owned(),
idx,
flow: valve.flow,
distances: distances
@ -261,7 +257,7 @@ impl Valve {
fn zero_flow(valve: &RawValve, idx: Index) -> Valve {
Valve {
id: valve.id.into(),
id: valve.id.to_owned(),
idx,
flow: valve.flow,
distances: vec![],
@ -313,12 +309,11 @@ impl ValveSystem {
}
#[allow(dead_code)]
fn get_valve_by_id<N: Into<Name>>(&self, id: N) -> Result<&Valve, ValveError> {
let id = id.into();
fn get_valve_by_id(&self, id: &str) -> Result<&Valve, ValveError> {
self.valves
.iter()
.find(|valve| valve.id == id)
.ok_or(ValveError::ValveNotFound(id.to_string()))
.ok_or(ValveError::ValveNotFound(id.to_owned()))
}
fn get_valve(&self, idx: Index) -> &Valve {

View file

@ -1,7 +1,9 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::{read_string, split_lines};
use itertools::Itertools;
use std::{cell::Cell, marker::PhantomData, ops::Index};
use crate::common::file::{read_string, split_lines};
use super::template::{DayTrait, ResultType};
use itertools::Itertools;
use thiserror::Error;
const DAY_NUMBER: usize = 17;

View file

@ -1,9 +1,11 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use itertools::Itertools;
use std::{collections::HashSet, num::ParseIntError};
use thiserror::Error;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
const DAY_NUMBER: usize = 18;
pub struct Day;
@ -171,7 +173,7 @@ impl Blob {
}
pub fn count_outside(&self) -> i64 {
let Some(mut extent) = self.extent() else {
let Some(mut extent)= self.extent() else {
return 0;
};
let mut sides = 0;

View file

@ -1,372 +0,0 @@
use super::template::{DayTrait, ResultType};
use crate::common::{
name::Name,
parser::{extract_result, ignore, trim0},
};
use nom::{
branch::alt,
character::complete::{alpha1, char, i64, multispace0, one_of},
error::Error,
multi::many0,
sequence::{terminated, tuple},
Err, IResult, Parser,
};
use std::{collections::HashMap, str::FromStr};
use thiserror::Error;
const DAY_NUMBER: usize = 21;
const ROOT: &'static str = "root";
const HUMAN: &'static str = "humn";
pub struct Day;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let troop: Troop = lines.parse()?;
Ok(ResultType::Integer(troop.value(ROOT)?))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let troop: Troop = lines.parse()?;
Ok(ResultType::Integer(troop.make_equal(ROOT)?))
}
}
#[derive(Debug, Error)]
enum MonkeyError {
#[error("Not a valid description: {0}")]
ParsingError(String),
#[error("Unknown Monkey: {0}")]
UnknownMonkey(String),
#[error("Not an algebraic Operation")]
NoAlgebraicOperation,
#[error("There are too many Humans in this input")]
TooManyHumans,
#[error("Unknown operand: {0}")]
UnknownOperand(char),
}
impl From<Err<Error<&str>>> for MonkeyError {
fn from(error: Err<Error<&str>>) -> Self {
MonkeyError::ParsingError(error.to_string())
}
}
#[derive(Debug, PartialEq, Eq)]
enum MonkeyJob {
Yell(i64),
Add(Name, Name),
Sub(Name, Name),
Mul(Name, Name),
Div(Name, Name),
}
impl MonkeyJob {
pub fn new<N1: Into<Name>, N2: Into<Name>>(
operand: char,
name1: N1,
name2: N2,
) -> Result<Self, MonkeyError> {
match operand {
'+' => Ok(MonkeyJob::Add(name1.into(), name2.into())),
'-' => Ok(MonkeyJob::Sub(name1.into(), name2.into())),
'*' => Ok(MonkeyJob::Mul(name1.into(), name2.into())),
'/' => Ok(MonkeyJob::Div(name1.into(), name2.into())),
c => Err(MonkeyError::UnknownOperand(c)),
}
}
fn parse_operation(input: &str) -> IResult<&str, MonkeyJob> {
tuple((alpha1, trim0(one_of("+-*/")), alpha1))
.map(|(name1, operand, name2)| MonkeyJob::new(operand, name1, name2).unwrap())
.parse(input)
}
fn parse(input: &str) -> IResult<&str, MonkeyJob> {
trim0(alt((
i64.map(|val| MonkeyJob::Yell(val)),
MonkeyJob::parse_operation,
)))
.parse(input)
}
fn perform(&self, troop: &Troop) -> Result<i64, MonkeyError> {
Ok(match self {
MonkeyJob::Yell(value) => *value,
MonkeyJob::Add(name1, name2) => {
troop.value(name1.clone())? + troop.value(name2.clone())?
}
MonkeyJob::Sub(name1, name2) => {
troop.value(name1.clone())? - troop.value(name2.clone())?
}
MonkeyJob::Mul(name1, name2) => {
troop.value(name1.clone())? * troop.value(name2.clone())?
}
MonkeyJob::Div(name1, name2) => {
troop.value(name1.clone())? / troop.value(name2.clone())?
}
})
}
fn get_names(&self) -> Result<(Name, Name), MonkeyError> {
match self {
MonkeyJob::Add(name1, name2)
| MonkeyJob::Sub(name1, name2)
| MonkeyJob::Mul(name1, name2)
| MonkeyJob::Div(name1, name2) => Ok((name1.clone(), name2.clone())),
MonkeyJob::Yell(_) => Err(MonkeyError::NoAlgebraicOperation),
}
}
fn perform_reverse(&self, troop: &Troop) -> Result<HumanJob, MonkeyError> {
match self {
MonkeyJob::Yell(value) => Ok(HumanJob::Const(*value)),
MonkeyJob::Add(name1, name2) => match troop
.shouting_match(name1.clone(), name2.clone())?
{
(HumanJob::Const(val1), HumanJob::Const(val2)) => Ok(HumanJob::Const(val1 + val2)),
(other, HumanJob::Const(val2)) => Ok(HumanJob::Add(Box::new(other), val2)),
(HumanJob::Const(val1), other) => Ok(HumanJob::Add(Box::new(other), val1)),
_ => Err(MonkeyError::TooManyHumans),
},
MonkeyJob::Sub(name1, name2) => match troop
.shouting_match(name1.clone(), name2.clone())?
{
(HumanJob::Const(val1), HumanJob::Const(val2)) => Ok(HumanJob::Const(val1 - val2)),
(other, HumanJob::Const(val2)) => Ok(HumanJob::Sub1(Box::new(other), val2)),
(HumanJob::Const(val1), other) => Ok(HumanJob::Sub2(val1, Box::new(other))),
_ => Err(MonkeyError::TooManyHumans),
},
MonkeyJob::Mul(name1, name2) => match troop
.shouting_match(name1.clone(), name2.clone())?
{
(HumanJob::Const(val1), HumanJob::Const(val2)) => Ok(HumanJob::Const(val1 * val2)),
(other, HumanJob::Const(val2)) => Ok(HumanJob::Mul(Box::new(other), val2)),
(HumanJob::Const(val1), other) => Ok(HumanJob::Mul(Box::new(other), val1)),
_ => Err(MonkeyError::TooManyHumans),
},
MonkeyJob::Div(name1, name2) => match troop
.shouting_match(name1.clone(), name2.clone())?
{
(HumanJob::Const(val1), HumanJob::Const(val2)) => Ok(HumanJob::Const(val1 / val2)),
(other, HumanJob::Const(val2)) => Ok(HumanJob::Div1(Box::new(other), val2)),
(HumanJob::Const(val1), other) => Ok(HumanJob::Div2(val1, Box::new(other))),
_ => Err(MonkeyError::TooManyHumans),
},
}
}
}
enum HumanJob {
Human,
Const(i64),
Add(Box<HumanJob>, i64),
Sub1(Box<HumanJob>, i64),
Sub2(i64, Box<HumanJob>),
Mul(Box<HumanJob>, i64),
Div1(Box<HumanJob>, i64),
Div2(i64, Box<HumanJob>),
}
impl HumanJob {
fn calc(&self, result: i64) -> i64 {
match self {
HumanJob::Human => result,
HumanJob::Add(job, value) => job.calc(result - value),
HumanJob::Sub1(job, value) => job.calc(result + value),
HumanJob::Sub2(value, job) => job.calc(value - result),
HumanJob::Mul(job, value) => job.calc(result / value),
HumanJob::Div1(job, value) => job.calc(result * value),
HumanJob::Div2(value, job) => job.calc(value / result),
HumanJob::Const(_) => {
unreachable!("There can be not Const in a legal HumanJob tree")
}
}
}
}
#[derive(Debug, PartialEq, Eq)]
struct Monkey {
name: Name,
job: MonkeyJob,
}
impl Monkey {
fn new<N: Into<Name>>(name: N, job: MonkeyJob) -> Self {
Monkey {
name: name.into(),
job,
}
}
fn parse(input: &str) -> IResult<&str, Monkey> {
let (input, name) = trim0(alpha1)(input)?;
let input = ignore(trim0(char(':')))(input)?;
let (input, job) = MonkeyJob::parse(input)?;
Ok((input, Monkey::new(name, job)))
}
fn shouting(&self, troop: &Troop) -> Result<i64, MonkeyError> {
self.job.perform(troop)
}
fn reverse_shouting(&self, troop: &Troop) -> Result<HumanJob, MonkeyError> {
if self.name.as_str() == HUMAN {
return Ok(HumanJob::Human);
}
self.job.perform_reverse(troop)
}
}
impl FromStr for Monkey {
type Err = MonkeyError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
Ok(extract_result(Monkey::parse)(input)?)
}
}
struct Troop {
monkeys: HashMap<Name, Monkey>,
}
impl Troop {
pub fn get_monkey<N: Into<Name>>(&self, name: N) -> Result<&Monkey, MonkeyError> {
let name = name.into();
self.monkeys
.get(&name)
.ok_or_else(|| MonkeyError::UnknownMonkey(String::from(name.as_str())))
}
pub fn value<N: Into<Name>>(&self, name: N) -> Result<i64, MonkeyError> {
self.get_monkey(name)?.shouting(self)
}
pub fn make_equal<N: Into<Name>>(&self, name: N) -> Result<i64, MonkeyError> {
let monkey = self.get_monkey(name)?;
let (name1, name2) = monkey.job.get_names()?;
let monkey1 = self.get_monkey(name1)?;
let monkey2 = self.get_monkey(name2)?;
match (
monkey1.reverse_shouting(self)?,
monkey2.reverse_shouting(self)?,
) {
(job, HumanJob::Const(value)) | (HumanJob::Const(value), job) => Ok(job.calc(value)),
_ => Err(MonkeyError::TooManyHumans),
}
}
fn shouting_match<N1: Into<Name>, N2: Into<Name>>(
&self,
name1: N1,
name2: N2,
) -> Result<(HumanJob, HumanJob), MonkeyError> {
let monkey1 = self.get_monkey(name1)?;
let monkey2 = self.get_monkey(name2)?;
Ok((
monkey1.reverse_shouting(self)?,
monkey2.reverse_shouting(self)?,
))
}
}
impl FromStr for Troop {
type Err = MonkeyError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let monkeys = extract_result(many0(terminated(Monkey::parse, multispace0)))(input)?;
let monkeys: HashMap<Name, Monkey> = monkeys
.into_iter()
.map(|monkey| (monkey.name.clone(), monkey))
.collect();
Ok(Troop { monkeys })
}
}
#[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(152);
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::Integer(301);
let result = day.part2(&lines)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn parse_lines() -> Result<()> {
let input = "root: pppw + sjmn";
let expected = Monkey {
name: Name::from("root"),
job: MonkeyJob::Add(Name::from("pppw"), Name::from("sjmn")),
};
let result: Monkey = input.parse()?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn parse_troop() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let troop: Troop = lines.parse()?;
assert_eq!(troop.monkeys.len(), 15);
assert_eq!(
troop.monkeys[&Name::from("sjmn")],
Monkey::new(
"sjmn",
MonkeyJob::Mul(Name::from("drzm"), Name::from("dbpl"))
)
);
Ok(())
}
#[test]
fn values() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let troop: Troop = lines.parse()?;
assert_eq!(troop.value(Name::from("lfqf"))?, 4);
assert_eq!(troop.value(Name::from("root"))?, 152);
Ok(())
}
#[test]
fn make_equal() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let troop: Troop = lines.parse()?;
assert_eq!(troop.make_equal(Name::from("root"))?, 301);
Ok(())
}
}

View file

@ -1,731 +0,0 @@
use super::template::{DayTrait, ResultType};
use crate::common::{
direction::Direction,
parser::{eol_terminated, extract_result, ignore, usize},
pos2::Pos2,
unit_vector::{self, UnitVector},
};
use nom::{
branch::alt,
character::complete::{char, multispace0},
combinator::value,
error::Error,
multi::many1,
Err, IResult, Parser,
};
use num_traits::Zero;
use std::{collections::HashMap, 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 result = WrappingWalker::walk_all(world_map, instructions)?;
Ok(ResultType::Integer(result.value()))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let (world_map, instructions) = Day::parse(lines)?;
let result = CubeWalker::walk_all(world_map, instructions)?;
Ok(ResultType::Integer(result.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,
#[error("Map is not a valid cube")]
NotAValidCube,
}
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 }
}
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> {
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, map_pos: Location) -> Option<Location> {
match map_pos.facing {
Direction::East => self.tiles.get(map_pos.pos.y()).and_then(|row| {
row.iter()
.position(|t| t.is_some())
.map(|x| Pos2::new(x, map_pos.pos.y()))
}),
Direction::West => self.tiles.get(map_pos.pos.y()).and_then(|row| {
row.iter()
.rposition(|t| t.is_some())
.map(|x| Pos2::new(x, map_pos.pos.y()))
}),
Direction::South => self
.tiles
.iter()
.position(|row| map_pos.pos.x() < row.len() && row[map_pos.pos.x()].is_some())
.map(|y| Pos2::new(map_pos.pos.x(), y)),
Direction::North => self
.tiles
.iter()
.rposition(|row| map_pos.pos.x() < row.len() && row[map_pos.pos.x()].is_some())
.map(|y| Pos2::new(map_pos.pos.x(), y)),
}
.map(|pos| Location::new(pos, map_pos.facing))
}
fn get_tile(&self, pos: Pos2<usize>) -> Option<bool> {
self.tiles
.get(pos.y())
.and_then(|row| row.get(pos.x()))
.copied()
.flatten()
}
}
#[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)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Location {
pos: Pos2<usize>,
facing: Direction,
}
impl Location {
pub fn new(pos: Pos2<usize>, facing: Direction) -> Self {
Location { pos, facing }
}
#[inline]
fn pos(&self) -> Pos2<usize> {
self.pos
}
fn one_step(&self) -> Option<Self> {
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::North => 3,
Direction::West => 2,
Direction::South => 1,
}
}
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 {
Instruction::Right => self.turn_right(),
Instruction::Left => self.turn_left(),
Instruction::Walk(steps) => {
for _ in 0..steps {
let Some(next_pos) = self.step() else {
break;
};
self.set_location(next_pos);
}
}
}
}
fn get_map(&self) -> &WorldMap;
fn location(&self) -> Location;
fn wrap(&self) -> Option<Location>;
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 {
walker.act(instruction);
}
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
}
}
#[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::Integer(5031);
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 mut walker = WrappingWalker::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(10, 10), Direction::North)
);
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 result = WrappingWalker::walk_all(world_map, instructions)?;
assert_eq!(result.pos, Pos2::new(7, 5));
assert_eq!(result.facing, Direction::East);
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(())
}
}

View file

@ -1,336 +0,0 @@
use super::template::{DayTrait, ResultType};
use crate::common::{area::Area, direction::Direction, pos2::Pos2};
use itertools::Itertools;
use std::{collections::HashMap, str::FromStr};
use thiserror::Error;
const DAY_NUMBER: usize = 23;
pub struct Day;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let field: Field = lines.parse()?;
let elves = field.rounds(10);
Ok(ResultType::Integer(count_empty_tiles(&elves)))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let field: Field = lines.parse()?;
let round = field.forever() as i64;
Ok(ResultType::Integer(round))
}
}
fn count_empty_tiles(elves: &[Pos2<i32>]) -> i64 {
let Some(area) = Area::from_iterator(elves.iter()) else {
return 0;
};
area.area() as i64 - elves.len() as i64
}
#[derive(Debug, Error)]
enum FieldError {
#[error("Unknown Char: {0}")]
UnknownChar(char),
}
fn next_direction(current: Direction) -> Direction {
match current {
Direction::North => Direction::South,
Direction::South => Direction::West,
Direction::West => Direction::East,
Direction::East => Direction::North,
}
}
struct DirectionDispenser(Direction);
impl DirectionDispenser {
fn new() -> DirectionDispenser {
Self(Direction::North)
}
fn progress(&mut self) {
self.0 = next_direction(self.0);
}
fn iter(&self) -> DispenserIterator {
DispenserIterator::new(self.0)
}
}
struct DispenserIterator {
start: Direction,
current: Option<Direction>,
}
impl DispenserIterator {
pub fn new(start: Direction) -> Self {
DispenserIterator {
start,
current: Some(start),
}
}
}
impl Iterator for DispenserIterator {
type Item = Direction;
fn next(&mut self) -> Option<Self::Item> {
let result = self.current;
if let Some(current) = self.current {
let next = next_direction(current);
if next == self.start {
self.current = None;
} else {
self.current = Some(next);
}
}
result
}
}
struct PosIterator {
pos: Pos2<i32>,
inc: Pos2<i32>,
count: usize,
}
impl PosIterator {
fn new(pos: Pos2<i32>, direction: Direction) -> Self {
let inc = direction.turn_right().into();
let pos = pos + direction - inc * 2;
Self { pos, inc, count: 0 }
}
}
impl Iterator for PosIterator {
type Item = Pos2<i32>;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 2 {
return None;
}
self.pos += self.inc;
self.count += 1;
Some(self.pos)
}
}
enum MoveCheck {
NoNeighbors,
NoFreeSpace,
MoveTo(Pos2<i32>, Direction),
}
struct Field {
direction: DirectionDispenser,
elves: HashMap<Pos2<i32>, bool>,
}
impl FromStr for Field {
type Err = FieldError;
fn from_str(lines: &str) -> Result<Self, Self::Err> {
let elves: HashMap<Pos2<i32>, bool> = lines
.split('\n')
.enumerate()
.map(|(y, row)| {
row.chars().enumerate().filter_map(move |(x, c)| match c {
'.' => None,
'#' => Some(Ok((Pos2::new(x as i32, y as i32), true))),
_ => Some(Err(FieldError::UnknownChar(c))),
})
})
.flatten()
.try_collect()?;
Ok(Field {
direction: DirectionDispenser::new(),
elves,
})
}
}
impl Field {
fn has_neighbors(&self, pos: Pos2<i32>) -> bool {
for x in -1..=1 {
for y in -1..=1 {
if x != 0 || y != 0 {
let check_pos = pos + (x, y);
if self.elves.contains_key(&check_pos) {
return true;
}
}
}
}
false
}
fn check(&self, pos: Pos2<i32>, directions: DispenserIterator) -> MoveCheck {
if !self.has_neighbors(pos) {
return MoveCheck::NoNeighbors;
}
for direction in directions {
if self.is_empty(pos, direction) {
return MoveCheck::MoveTo(pos + direction, direction);
}
}
MoveCheck::NoFreeSpace
}
fn one_round(&mut self) -> bool {
let mut proposals: HashMap<Pos2<i32>, (Pos2<i32>, Direction)> = HashMap::new();
let mut deactivate = HashMap::new();
for elf in self
.elves
.iter()
.filter_map(|(elf, active)| active.then_some(*elf))
{
match self.check(elf, self.direction.iter()) {
MoveCheck::MoveTo(proposal, direction) => {
if proposals.contains_key(&proposal) {
proposals.remove(&proposal);
} else {
proposals.insert(proposal, (elf, direction));
}
}
MoveCheck::NoNeighbors => {
deactivate.insert(elf, false);
}
MoveCheck::NoFreeSpace => {}
}
}
if proposals.is_empty() {
return false;
}
self.elves.extend(deactivate.into_iter());
for (to, (from, direction)) in proposals {
self.elves.remove(&from);
self.elves.insert(to, true);
for pos in PosIterator::new(to, direction) {
if let Some(active) = self.elves.get_mut(&pos) {
*active = true;
}
}
}
self.direction.progress();
true
}
fn is_empty(&self, pos: Pos2<i32>, direction: Direction) -> bool {
PosIterator::new(pos, direction).all(|pos| !self.elves.contains_key(&pos))
}
fn forever(mut self) -> usize {
for round in 0.. {
if !self.one_round() {
return round + 1;
}
}
unreachable!()
}
fn rounds(mut self, arg: usize) -> Vec<Pos2<i32>> {
for _ in 0..arg {
self.one_round();
}
self.elves.into_keys().collect_vec()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{common::file::read_string, hashmap, hashset};
use anyhow::Result;
use std::collections::HashSet;
#[test]
fn test_part1() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let expected = ResultType::Integer(110);
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::Integer(20);
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(), "example02.txt")?;
let expected = hashmap!(
Pos2::new(2, 1) => true,
Pos2::new(3, 1) => true,
Pos2::new(2, 2) => true,
Pos2::new(2, 4) => true,
Pos2::new(3, 4) => true
);
let field: Field = lines.parse()?;
assert_eq!(field.elves, expected);
assert_eq!(field.direction.0, Direction::North);
Ok(())
}
#[test]
fn round1() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example02.txt")?;
let expected = hashmap!(
Pos2::new(2, 0) => true,
Pos2::new(3, 0) => true,
Pos2::new(2, 2) => true,
Pos2::new(3, 3) => true,
Pos2::new(2, 4) => true
);
let mut field: Field = lines.parse()?;
field.one_round();
assert_eq!(field.elves, expected);
assert_eq!(field.direction.0, Direction::South);
Ok(())
}
#[test]
fn round2() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example02.txt")?;
let expected = hashset!(
Pos2::new(2, 1),
Pos2::new(3, 1),
Pos2::new(1, 2),
Pos2::new(4, 3),
Pos2::new(2, 5)
);
let field: Field = lines.parse()?;
let elves = HashSet::from_iter(field.rounds(2).into_iter());
assert_eq!(elves, expected);
Ok(())
}
}

View file

@ -1,487 +0,0 @@
use super::template::{DayTrait, ResultType};
use crate::common::{direction::Direction, file::split_lines, math::lcm, pos2::Pos2};
use itertools::Itertools;
use std::{collections::HashSet, str::FromStr};
use thiserror::Error;
const DAY_NUMBER: usize = 24;
pub struct Day;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let valley: Valley = lines.parse()?;
Ok(ResultType::Integer(valley.cross(Trip(1))? as i64))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let valley: Valley = lines.parse()?;
Ok(ResultType::Integer(valley.cross(Trip(3))? as i64))
}
}
#[derive(Debug, Error)]
enum BlizzardError {
#[error("Not a valid description: {0}")]
ParsingError(String),
#[error("Illegal char: {0}")]
IllegalChar(char),
#[error("Need exacly one door. Found: {0}")]
NeedExactlyOneDoor(String),
#[error("Valley has no legal shape")]
ValleyHasIllegalShape,
#[error("No valid path was found")]
NoPathFound,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Trip(usize);
impl Trip {
#[inline]
pub fn inc(&self) -> Trip {
Trip(self.0 + 1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Time(usize);
impl Time {
#[inline]
pub fn get(&self) -> usize {
self.0
}
#[inline]
pub fn inc(&self) -> Time {
Time(self.0 + 1)
}
}
struct Valley {
height: usize,
entry: usize,
exit: usize,
storm: Storm,
}
#[derive(Debug, PartialEq, Eq)]
struct State {
time: Time,
trip: Trip,
position: Pos2<usize>,
start: Pos2<usize>,
target: Pos2<usize>,
}
impl State {
fn new(entry: Pos2<usize>, exit: Pos2<usize>) -> Self {
Self {
time: Time(0),
trip: Trip(0),
position: entry,
start: entry,
target: exit,
}
}
fn hit_target(&self) -> Self {
Self {
time: self.time.inc(),
trip: self.trip.inc(),
position: self.target,
start: self.target,
target: self.start,
}
}
fn walk_to(&self, position: Pos2<usize>) -> Self {
Self {
time: self.time.inc(),
trip: self.trip,
position,
start: self.start,
target: self.target,
}
}
fn next_states(&self, valley: &Valley) -> Vec<State> {
let frame = valley.storm.get(self.time.inc());
let mut states = Vec::with_capacity(4);
let mut dir = Direction::East;
for _ in 0..4 {
if let Some(next_pos) = self.position.check_add(dir) {
if next_pos == self.target {
states.push(self.hit_target());
} else if frame.is_accessible(&next_pos) {
states.push(self.walk_to(next_pos));
}
}
dir = dir.turn_left();
}
if frame.is_accessible(&self.position) || self.position == self.start {
states.push(self.walk_to(self.position));
}
states
}
}
impl Valley {
fn doorline(input: &str) -> Result<usize, BlizzardError> {
let doors = input
.char_indices()
.map(|(col, c)| match c {
'.' => Ok(Some(col)),
'#' => Ok(None),
_ => Err(BlizzardError::IllegalChar(c)),
})
.filter_map_ok(|item| item)
.collect::<Result<Vec<_>, _>>()?;
if doors.len() != 1 {
Err(BlizzardError::NeedExactlyOneDoor(input.to_owned()))
} else {
Ok(doors[0] - 1)
}
}
fn storm(input: &[&str]) -> Result<Blizzards, BlizzardError> {
let height = input.len();
if height == 0 {
return Err(BlizzardError::ValleyHasIllegalShape);
}
let width = input[0].len();
if width == 0
|| input[0].chars().nth(0) != Some('#')
|| input[0].chars().nth(width - 1) != Some('#')
{
return Err(BlizzardError::ValleyHasIllegalShape);
}
if !input
.iter()
.map(|row| {
(
row.len(),
row.chars().nth(0),
row.chars().nth(row.len() - 1),
)
})
.all_equal()
{
return Err(BlizzardError::ValleyHasIllegalShape);
}
let raw = input
.iter()
.enumerate()
.map(|(y, row)| {
row.char_indices().filter_map(move |(x, c)| match c {
'#' => {
if x != 0 && x != row.len() - 1 {
Some(Err(BlizzardError::IllegalChar('#')))
} else {
None
}
}
'>' => Some(Ok((Pos2::new(x - 1, y), Direction::East))),
'^' => Some(Ok((Pos2::new(x - 1, y), Direction::North))),
'<' => Some(Ok((Pos2::new(x - 1, y), Direction::West))),
'v' => Some(Ok((Pos2::new(x - 1, y), Direction::South))),
'.' => None,
_ => Some(Err(BlizzardError::IllegalChar(c))),
})
})
.flatten()
.collect::<Result<Vec<_>, _>>()?;
Ok(Blizzards::new(raw, width - 2, height))
}
fn cross(&self, trips: Trip) -> Result<usize, BlizzardError> {
let mut seen = HashSet::new();
let start = State::new(self.get_entry(), self.get_exit());
let mut queue = vec![start];
let mut next_queue = vec![];
while let Some(current) = queue.pop() {
for next_state in current.next_states(&self) {
if next_state.trip == trips {
return Ok(next_state.time.get());
}
let fingerprint = (next_state.position, next_state.trip);
if !seen.contains(&fingerprint) {
seen.insert(fingerprint);
next_queue.push(next_state);
}
}
if queue.is_empty() {
std::mem::swap(&mut queue, &mut next_queue);
seen.clear();
}
}
Err(BlizzardError::NoPathFound)
}
fn get_entry(&self) -> Pos2<usize> {
Pos2::new(self.entry, 0)
}
fn get_exit(&self) -> Pos2<usize> {
Pos2::new(self.exit, self.height + 1)
}
}
impl FromStr for Valley {
type Err = BlizzardError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let lines = split_lines(input).collect_vec();
if lines.len() < 3 {
return Err(BlizzardError::ParsingError(input.to_owned()));
}
let entry = Valley::doorline(lines[0])?;
let exit = Valley::doorline(lines[lines.len() - 1])?;
let blizzards = Valley::storm(&lines[1..lines.len() - 1])?;
let height = blizzards.height;
let storm = Storm::new(blizzards);
Ok(Self {
entry,
exit,
height,
storm,
})
}
}
#[derive(Debug, Clone)]
struct BlizzardLine {
direction: Direction,
blizzards: Vec<usize>,
len: usize,
}
impl BlizzardLine {
fn new(direction: Direction, len: usize) -> Self {
BlizzardLine {
direction,
blizzards: vec![],
len,
}
}
fn iter(&self, pos: usize, time: usize) -> impl Iterator<Item = Pos2<usize>> + '_ {
let time = time % self.len;
self.blizzards
.iter()
.map(move |delta| match self.direction {
Direction::East => Pos2::new((time + *delta) % self.len, pos),
Direction::West => Pos2::new((self.len - time + *delta) % self.len, pos),
Direction::North => Pos2::new(pos, (self.len - time + *delta) % self.len),
Direction::South => Pos2::new(pos, (time + *delta) % self.len),
})
}
fn push(&mut self, x: usize) {
self.blizzards.push(x);
}
}
struct Blizzards {
blizzards: Vec<Vec<BlizzardLine>>,
width: usize,
height: usize,
period: usize,
}
impl Blizzards {
pub fn new(raw: Vec<(Pos2<usize>, Direction)>, width: usize, height: usize) -> Self {
let mut blizzards = vec![
vec![BlizzardLine::new(Direction::East, width); height],
vec![BlizzardLine::new(Direction::North, height); width],
vec![BlizzardLine::new(Direction::West, width); height],
vec![BlizzardLine::new(Direction::South, height); width],
];
for (pos, direction) in raw {
match direction {
Direction::East | Direction::West => {
blizzards[direction as usize][pos.y()].push(pos.x())
}
Direction::North | Direction::South => {
blizzards[direction as usize][pos.x()].push(pos.y())
}
}
}
Self {
blizzards,
width,
height,
period: lcm(width, height),
}
}
pub fn frame_at_time(&self, time: usize) -> Frame {
let time = time % self.period;
let mut valley = vec![vec![true; self.width]; self.height];
for _ in 0..4 {
for dir in self.blizzards.iter() {
for (pos, blizzard) in dir.iter().enumerate() {
for item in blizzard.iter(pos, time) {
valley[item.y()][item.x()] = false;
}
}
}
}
Frame(valley)
}
}
struct Frame(Vec<Vec<bool>>);
impl Frame {
fn is_accessible(&self, pos: &Pos2<usize>) -> bool {
if pos.y() == 0 {
false
} else {
self.0
.get(pos.y() - 1)
.and_then(|row| row.get(pos.x()))
.copied()
.unwrap_or(false)
}
}
}
struct Storm(Vec<Frame>);
impl Storm {
fn new(blizzards: Blizzards) -> Self {
let storm = (0..blizzards.period)
.map(|time| blizzards.frame_at_time(time))
.collect_vec();
Self(storm)
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn get(&self, time: Time) -> &Frame {
&self.0[time.get() % self.len()]
}
}
#[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(18);
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::Integer(54);
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(), "example02.txt")?;
let valley: Valley = lines.parse()?;
assert_eq!(valley.entry, 0);
assert_eq!(valley.exit, 4);
assert_eq!(valley.height, 5);
assert_eq!(valley.storm.len(), 5);
assert_eq!(valley.get_entry(), Pos2::new(0, 0));
assert_eq!(valley.get_exit(), Pos2::new(4, 6));
assert_eq!(
valley.storm.get(Time(3)).is_accessible(&Pos2::new(3, 2)),
false
);
Ok(())
}
#[test]
fn parse2() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let valley: Valley = lines.parse()?;
assert_eq!(valley.entry, 0);
assert_eq!(valley.exit, 5);
assert_eq!(valley.height, 4);
assert_eq!(valley.storm.len(), 12);
assert_eq!(valley.get_entry(), Pos2::new(0, 0));
assert_eq!(valley.get_exit(), Pos2::new(5, 5));
let expected = vec![
vec![true, false, false, true, false, true],
vec![false, true, true, false, false, true],
vec![false, false, true, false, false, true],
vec![false, false, true, true, false, false],
];
assert_eq!(valley.storm.get(Time(1)).0, expected);
Ok(())
}
#[test]
fn possible_moves() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let valley: Valley = lines.parse()?;
let position = State {
time: Time(17),
trip: Trip(0),
position: Pos2::new(5, 4),
start: Pos2::new(0, 0),
target: Pos2::new(5, 5),
};
let expected = vec![
State {
time: Time(18),
trip: Trip(0),
position: Pos2::new(4, 4),
start: Pos2::new(0, 0),
target: Pos2::new(5, 5),
},
State {
time: Time(18),
trip: Trip(1),
position: Pos2::new(5, 5),
start: Pos2::new(5, 5),
target: Pos2::new(0, 0),
},
];
let next_states = position.next_states(&valley);
assert_eq!(next_states, expected);
Ok(())
}
}

View file

@ -1,141 +0,0 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines;
use itertools::{unfold, Itertools};
use std::iter::Sum;
use std::ops::Add;
use std::str::FromStr;
use thiserror::Error;
const DAY_NUMBER: usize = 25;
pub struct Day;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let snafu: Snafu = split_lines(lines)
.map(|line| line.parse::<Snafu>())
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.sum();
Ok(ResultType::String(snafu.as_str()))
}
fn part2(&self, _lines: &str) -> anyhow::Result<ResultType> {
Ok(ResultType::Nothing)
}
}
impl Snafu {
pub fn as_str(&self) -> String {
if self.0 == 0 {
return String::from("0");
}
unfold(self.0, |value| {
if *value == 0 {
return None;
}
let m = (*value + 2) % 5;
*value = (*value + 2 - m) / 5;
Some(m)
})
.map(|digit| "=-012".chars().nth(digit).unwrap())
.collect_vec()
.into_iter()
.rev()
.join("")
}
fn new(value: i64) -> Result<Snafu, SnafuError> {
if value >= 0 {
Ok(Snafu(value as usize))
} else {
Err(SnafuError::OnlyNonNegativeSnafus(value))
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Snafu(usize);
impl Add for Snafu {
type Output = Snafu;
fn add(self, rhs: Self) -> Self::Output {
Snafu(self.0 + rhs.0)
}
}
impl Sum for Snafu {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Snafu(0), Add::add)
}
}
impl FromStr for Snafu {
type Err = SnafuError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Snafu::new(
s.chars()
.map(|c| {
"=-012"
.chars()
.position(|d| d == c)
.ok_or_else(|| SnafuError::IllegalChar(c))
})
.fold_ok(0, |acc, next| acc * 5 + (next as i64 - 2))?,
)
}
}
#[derive(Debug, Error)]
enum SnafuError {
#[error("Illegal Char: {0}")]
IllegalChar(char),
#[error("Only non negative Snafus exist. Got: {0}")]
OnlyNonNegativeSnafus(i64),
}
#[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::String(String::from("2=-1=0"));
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 input = String::from("1=-0-2");
let snafu: Snafu = input.parse()?;
assert_eq!(snafu, Snafu(1747));
assert_eq!(snafu.as_str(), input);
Ok(())
}
}

View file

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

View file

@ -18,11 +18,6 @@ mod day17;
mod day18;
mod day19;
mod day20;
mod day21;
mod day22;
mod day23;
mod day24;
mod day25;
mod template;
pub use template::DayTrait;
@ -32,7 +27,7 @@ pub mod day_provider {
use super::*;
use thiserror::Error;
const MAX_DAY: usize = 25;
const MAX_DAY: usize = 20;
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
match day_num {
@ -56,11 +51,6 @@ pub mod day_provider {
18 => Ok(Box::new(day18::Day)),
19 => Ok(Box::new(day19::Day)),
20 => Ok(Box::new(day20::Day)),
21 => Ok(Box::new(day21::Day)),
22 => Ok(Box::new(day22::Day)),
23 => Ok(Box::new(day23::Day)),
24 => Ok(Box::new(day24::Day)),
25 => Ok(Box::new(day25::Day)),
_ => Err(ProviderError::InvalidNumber(day_num)),
}
}

View file

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