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

View file

@ -1,18 +1,27 @@
use super::{pos2::Pos2, turn::Turn}; use super::{pos::Pos, turn::Turn};
use num_traits::{Num, Signed};
use std::{fmt::Display, ops::Add}; use std::{fmt::Display, ops::Add};
use Direction::*; use Direction::*;
use Turn::*; use Turn::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Direction { pub enum Direction {
East = 0, East,
North = 1, North,
West = 2, West,
South = 3, South,
} }
impl Direction { impl Direction {
pub fn as_pos(&self) -> Pos<i32> {
match *self {
East => Pos::new(1, 0),
North => Pos::new(0, 1),
West => Pos::new(-1, 0),
South => Pos::new(0, -1),
}
}
pub fn is_perpendicular(&self, other: &Direction) -> bool { pub fn is_perpendicular(&self, other: &Direction) -> bool {
match *self { match *self {
East => *other != East && *other != West, East => *other != East && *other != West,
@ -82,26 +91,35 @@ impl Display for Direction {
} }
} }
impl<T> From<Direction> for Pos2<T> impl Add<Pos<i32>> for Direction {
where type Output = Pos<i32>;
T: Num + Signed,
{ fn add(self, rhs: Pos<i32>) -> Self::Output {
fn from(value: Direction) -> Self { Pos::add(rhs, 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<Direction> for Pos2<i32> { impl Add<Pos<i32>> for &Direction {
type Output = Pos2<i32>; type Output = Pos<i32>;
fn add(self, rhs: Direction) -> Self::Output { fn add(self, rhs: Pos<i32>) -> Self::Output {
let rhs: Pos2<i32> = rhs.into(); Pos::add(rhs, *self)
self + rhs }
}
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) 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 a
} }
pub fn gcd<T>(a: T, b: T) -> Option<T> pub fn gcd<T>(a: T, b: T) -> T
where where
T: Num + Ord + Copy, T: Num + Ord + Copy,
{ {
assert!(a >= T::zero()); assert!(a >= T::zero());
assert!(b >= T::zero()); assert!(b >= T::zero());
if a.is_zero() { if a.is_zero() {
if b.is_zero() { b
None } else if b.is_zero() {
} else { a
Some(b)
}
} else { } else {
Some(non_zero_gcd(a, b)) non_zero_gcd(a, b)
} }
} }
pub fn lcm<T>(a: T, b: T) -> T pub fn lcm<T>(a: T, b: T) -> T
where where
T: Num + Ord + Copy, T: Num + Ord + Copy,
@ -81,9 +77,9 @@ mod tests {
#[test] #[test]
fn some_simple_gcd() { fn some_simple_gcd() {
assert_eq!(Some(5), gcd(10, 15)); assert_eq!(5, gcd(10, 15));
assert_eq!(Some(7), gcd(21, 49)); assert_eq!(7, gcd(21, 49));
assert_eq!(Some(1), gcd(13, 17)); assert_eq!(1, gcd(13, 17));
} }
#[test] #[test]

View file

@ -1,12 +1,8 @@
pub mod abs;
pub mod area; pub mod area;
pub mod direction; pub mod direction;
pub mod file; pub mod file;
pub mod helper; pub mod helper;
pub mod math; pub mod math;
pub mod name;
pub mod parser; pub mod parser;
pub mod pos2; pub mod pos;
pub mod pos3;
pub mod turn; 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::{ use nom::{
branch::alt, branch::alt,
bytes::complete::tag, bytes::complete::tag,
character::complete::{line_ending, space0, space1, u32}, character::complete::{line_ending, space0, space1},
combinator::{eof, opt, value}, combinator::{eof, opt, value},
error::ParseError, error::ParseError,
multi::many0, multi::many0,
@ -34,10 +34,6 @@ where
terminated(line, alt((line_ending, eof))) terminated(line, alt((line_ending, eof)))
} }
pub fn usize<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, usize, E> {
u32.map(|v| v as usize).parse(input)
}
pub fn true_false<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> { pub fn true_false<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
alt((value(true, tag("true")), value(false, tag("false"))))(input) alt((value(true, tag("true")), value(false, tag("false"))))(input)
} }

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

View file

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

View file

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

View file

@ -1,5 +1,6 @@
use super::template::{DayTrait, ResultType};
use crate::common::file::split_lines; use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::Itertools; use itertools::Itertools;
use std::num::ParseIntError; use std::num::ParseIntError;
use thiserror::Error; use thiserror::Error;
@ -123,7 +124,7 @@ impl Move {
if self.to_stack >= stacks.len() { if self.to_stack >= stacks.len() {
return Err(CrateError::TooFewStacks(stacks.len(), self.to_stack)); 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)); return Err(CrateError::TooFewStacks(stacks.len(), self.from_stack));
}; };
if from_stack.len() < self.amount { 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::{ use std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
num::ParseIntError, num::ParseIntError,
rc::{Rc, Weak}, rc::{Rc, Weak},
}; };
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use thiserror::Error; use thiserror::Error;
const DAY_NUMBER: usize = 7; const DAY_NUMBER: usize = 7;
@ -73,7 +75,7 @@ enum DirectoryError {
#[derive(Debug)] #[derive(Debug)]
struct Node { struct Node {
parent: Option<Weak<RefCell<Node>>>, parent: Option<Weak<RefCell<Node>>>,
name: Name, name: String,
sub_dirs: Vec<Rc<RefCell<Node>>>, sub_dirs: Vec<Rc<RefCell<Node>>>,
file_size: i64, file_size: i64,
size: Cell<Option<i64>>, size: Cell<Option<i64>>,
@ -83,7 +85,7 @@ impl Node {
fn root() -> Node { fn root() -> Node {
Node { Node {
parent: None, parent: None,
name: "/".into(), name: "/".to_owned(),
sub_dirs: vec![], sub_dirs: vec![],
file_size: 0, file_size: 0,
size: Cell::new(None), size: Cell::new(None),
@ -94,7 +96,7 @@ impl Node {
self.sub_dirs.push(subdir); 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; self.file_size += size;
} }
@ -139,10 +141,10 @@ impl Directory {
self.node.borrow().file_size 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 { let subdir = Rc::new(RefCell::new(Node {
parent: Some(Rc::downgrade(&self.node)), parent: Some(Rc::downgrade(&self.node)),
name: name.into(), name: name.to_owned(),
sub_dirs: vec![], sub_dirs: vec![],
file_size: 0, file_size: 0,
size: Cell::new(None), size: Cell::new(None),
@ -150,7 +152,7 @@ impl Directory {
self.node.borrow_mut().add_subdir(subdir); 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) self.node.borrow_mut().add_file(name, size)
} }
@ -163,9 +165,8 @@ impl Directory {
.map(|node| Directory { node }) .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 node = self.node.borrow();
let name = name.into();
let sub_node = node.sub_dirs.iter().find(|node| node.borrow().name == name); let sub_node = node.sub_dirs.iter().find(|node| node.borrow().name == name);
sub_node.map(|node| Directory::create(node.clone())) sub_node.map(|node| Directory::create(node.clone()))
} }
@ -180,14 +181,14 @@ impl Directory {
"/" => root.clone(), "/" => root.clone(),
".." => { ".." => {
let Some(next) = current.parent() else { let Some(next) = current.parent() else {
return Err(DirectoryError::NoParentDirectory); return Err(DirectoryError::NoParentDirectory);
}; };
next next
} }
_ => { _ => {
let Some(next) = current.get_subdir(dir) else { let Some(next) = current.get_subdir(dir) else {
return Err(DirectoryError::NoSuchDirectory(dir.to_owned())); return Err(DirectoryError::NoSuchDirectory(dir.to_owned()))
}; };
next 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 std::str::FromStr;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::{iproduct, FoldWhile, Itertools};
use thiserror::Error; use thiserror::Error;
const DAY_NUMBER: usize = 8; const DAY_NUMBER: usize = 8;

View file

@ -1,5 +1,5 @@
use super::template::{DayTrait, ResultType}; 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 itertools::Itertools;
use std::{collections::HashSet, num::ParseIntError}; use std::{collections::HashSet, num::ParseIntError};
use thiserror::Error; use thiserror::Error;
@ -63,27 +63,27 @@ fn parse_line(line: &str) -> Result<(Direction, usize), RopeError> {
} }
} }
fn get_closer(first: Pos2<i32>, second: Pos2<i32>) -> Option<Pos2<i32>> { fn get_closer(first: Pos<i32>, second: Pos<i32>) -> Option<Pos<i32>> {
let diff = second - first; let diff = second - first;
if diff.x().abs() <= 1 && diff.y().abs() <= 1 { if diff.x().abs() <= 1 && diff.y().abs() <= 1 {
None None
} else { } else {
Some(Pos2::new(diff.x().signum(), diff.y().signum())) Some(Pos::new(diff.x().signum(), diff.y().signum()))
} }
} }
#[derive(Debug)] #[derive(Debug)]
struct Rope { struct Rope {
head: Pos2<i32>, head: Pos<i32>,
knots: Vec<Pos2<i32>>, knots: Vec<Pos<i32>>,
} }
impl Rope { impl Rope {
pub fn create(length: usize) -> Self { pub fn create(length: usize) -> Self {
assert!(length >= 2); assert!(length >= 2);
Rope { Rope {
head: Pos2::new(0, 0), head: Pos::new(0, 0),
knots: vec![Pos2::new(0, 0); length - 1], 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 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; use thiserror::Error;
const DAY_NUMBER: usize = 10; const DAY_NUMBER: usize = 10;
@ -14,13 +14,17 @@ impl DayTrait for Day {
} }
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> { 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]); let result = CpuCycles::signal_strength(&instructions, &[20, 60, 100, 140, 180, 220]);
Ok(ResultType::Integer(result as i64)) Ok(ResultType::Integer(result as i64))
} }
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> { 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); let result = CpuCycles::draw(&instructions);
Ok(ResultType::Lines(result)) Ok(ResultType::Lines(result))
} }
@ -35,18 +39,18 @@ enum CpuError {
InvalidInteger(#[from] ParseIntError), InvalidInteger(#[from] ParseIntError),
} }
#[derive(Debug, PartialEq, Eq)] enum Instruction {
struct Instruction(Option<i32>); Add(i32),
Noop,
}
impl FromStr for Instruction { impl Instruction {
type Err = CpuError; pub fn parse(line: &str) -> Result<Self, CpuError> {
fn from_str(line: &str) -> Result<Self, Self::Err> {
if line == "noop" { if line == "noop" {
Ok(Instruction(None)) Ok(Instruction::Noop)
} else if line.starts_with("addx") { } else if line.starts_with("addx") {
let value = line[5..].parse()?; let value = line[5..].parse()?;
Ok(Instruction(Some(value))) Ok(Instruction::Add(value))
} else { } else {
Err(CpuError::UnknownInstruction(line.to_owned())) Err(CpuError::UnknownInstruction(line.to_owned()))
} }
@ -110,7 +114,10 @@ impl<'a> Iterator for CpuCycles<'a> {
self.current = None; self.current = None;
Some(start_register) Some(start_register)
} else if let Some(instruction) = self.instructions.next() { } 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) Some(self.register)
} else { } else {
None None
@ -127,11 +134,7 @@ mod test {
#[test] #[test]
fn test_simple() -> Result<()> { fn test_simple() -> Result<()> {
let instructions = vec![ let instructions = vec![Instruction::Noop, Instruction::Add(3), Instruction::Add(-5)];
Instruction(None),
Instruction(Some(3)),
Instruction(Some(-5)),
];
let expected = vec![1, 1, 1, 4, 4]; let expected = vec![1, 1, 1, 4, 4];
let result = CpuCycles::create(&instructions).collect::<Vec<_>>(); let result = CpuCycles::create(&instructions).collect::<Vec<_>>();
assert_eq!(result, expected); assert_eq!(result, expected);

View file

@ -1,7 +1,8 @@
use super::template::{DayTrait, ResultType};
use crate::common::parser::{ use crate::common::parser::{
empty_lines, eol_terminated, extract_result, ignore, trim0, trim_left1, true_false, empty_lines, eol_terminated, extract_result, ignore, trim0, trim_left1, true_false,
}; };
use super::template::{DayTrait, ResultType};
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::complete::tag, 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 std::collections::{BinaryHeap, HashSet};
use crate::common::{file::split_lines, pos::Pos};
use super::template::{DayTrait, ResultType};
use thiserror::Error; use thiserror::Error;
const DAY_NUMBER: usize = 12; const DAY_NUMBER: usize = 12;
@ -43,7 +45,7 @@ enum ValleyError {
struct Path { struct Path {
length: usize, length: usize,
height: char, height: char,
pos: Pos2<usize>, pos: Pos<usize>,
} }
impl PartialOrd for Path { impl PartialOrd for Path {
@ -76,7 +78,7 @@ impl Ord for Path {
} }
impl 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 { Path {
length, length,
height, 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 { while self.state < 4 {
self.state += 1; self.state += 1;
match self.state { match self.state {
1 => { 1 => {
if self.path.pos.x() < self.valley.width() - 1 { if self.path.pos.x() < self.valley.width() - 1 {
return Some(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 => { 2 => {
if self.path.pos.y() > 0 { 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 => { 3 => {
if self.path.pos.x() > 0 { 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 => { 4 => {
if self.path.pos.y() < self.valley.height() - 1 { 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 { struct Valley {
map: Vec<Vec<char>>, map: Vec<Vec<char>>,
start: Pos2<usize>, start: Pos<usize>,
exit: Pos2<usize>, exit: Pos<usize>,
width: usize, width: usize,
} }
@ -170,11 +172,11 @@ impl TryFrom<&str> for Valley {
for (x, height_char) in row.chars().enumerate() { for (x, height_char) in row.chars().enumerate() {
match height_char { match height_char {
'S' => { 'S' => {
start = Some(Pos2::new(x, y)); start = Some(Pos::new(x, y));
height_row.push('a') height_row.push('a')
} }
'E' => { 'E' => {
exit = Some(Pos2::new(x, y)); exit = Some(Pos::new(x, y));
height_row.push('z') height_row.push('z')
} }
'a'..='z' => height_row.push(height_char), 'a'..='z' => height_row.push(height_char),
@ -209,13 +211,13 @@ impl TryFrom<&str> for Valley {
} }
impl 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()] self.map[pos.y()][pos.x()]
} }
fn do_walk<F>(&self, check: F) -> Result<usize, ValleyError> fn do_walk<F>(&self, check: F) -> Result<usize, ValleyError>
where where
F: Fn(Pos2<usize>) -> bool, F: Fn(Pos<usize>) -> bool,
{ {
let mut shortest = HashSet::with_capacity(self.width * self.map.len()); let mut shortest = HashSet::with_capacity(self.width * self.map.len());
let mut queue = BinaryHeap::new(); let mut queue = BinaryHeap::new();
@ -267,8 +269,8 @@ mod test {
let lines = read_string(day.get_day_number(), "example01.txt")?; let lines = read_string(day.get_day_number(), "example01.txt")?;
let valley = Valley::try_from(lines.as_str())?; let valley = Valley::try_from(lines.as_str())?;
assert_eq!(valley.width, 8); assert_eq!(valley.width, 8);
assert_eq!(valley.start, Pos2::new(0, 0)); assert_eq!(valley.start, Pos::new(0, 0));
assert_eq!(valley.exit, Pos2::new(5, 2)); assert_eq!(valley.exit, Pos::new(5, 2));
Ok(()) 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 std::cmp::Ordering;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use itertools::Itertools;
use thiserror::Error; use thiserror::Error;
const DAY_NUMBER: usize = 13; 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 itertools::Itertools;
use std::{collections::HashSet, num::ParseIntError}; use std::{collections::HashSet, num::ParseIntError};
use thiserror::Error; use thiserror::Error;
use crate::common::{file::split_lines, pos::Pos};
use super::template::{DayTrait, ResultType};
const DAY_NUMBER: usize = 14; const DAY_NUMBER: usize = 14;
pub struct Day; pub struct Day;
@ -54,7 +56,7 @@ impl TryFrom<&str> for Cave {
fn try_from(lines: &str) -> Result<Self, Self::Error> { fn try_from(lines: &str) -> Result<Self, Self::Error> {
let lines = split_lines(lines); let lines = split_lines(lines);
let mut cave: HashSet<Pos2<i32>> = HashSet::new(); let mut cave: HashSet<Pos<i32>> = HashSet::new();
for line in lines { for line in lines {
cave.extend(Cave::parse_one(line)?.iter()); cave.extend(Cave::parse_one(line)?.iter());
} }
@ -90,7 +92,7 @@ impl TryFrom<&str> for Cave {
} }
impl 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 { if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x {
false false
} else { } 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 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 { if from == to {
return Ok(vec![from]); return Ok(vec![from]);
} }
if from.x() == to.x() { if from.x() == to.x() {
if from.y() < to.y() { if from.y() < to.y() {
Ok((from.y()..to.y()).map(|y| Pos2::new(from.x(), y)).collect()) Ok((from.y()..to.y()).map(|y| Pos::new(from.x(), y)).collect())
} else { } else {
Ok(((to.y() + 1)..=from.y()) Ok(((to.y() + 1)..=from.y())
.map(|y| Pos2::new(from.x(), y)) .map(|y| Pos::new(from.x(), y))
.collect()) .collect())
} }
} else if from.y() == to.y() { } else if from.y() == to.y() {
if from.x() < to.x() { if from.x() < to.x() {
Ok((from.x()..to.x()).map(|x| Pos2::new(x, from.y())).collect()) Ok((from.x()..to.x()).map(|x| Pos::new(x, from.y())).collect())
} else { } else {
Ok(((to.x() + 1)..=from.x()) Ok(((to.x() + 1)..=from.x())
.map(|x| Pos2::new(x, from.y())) .map(|x| Pos::new(x, from.y()))
.collect()) .collect())
} }
} else { } 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 let corners = line
.split("->") .split("->")
.map(|coord| match coord.split(',').collect::<Vec<_>>()[..] { .map(|coord| match coord.split(',').collect::<Vec<_>>()[..] {
[first, second] => { [first, second] => {
let x: i32 = first.trim().parse()?; let x: i32 = first.trim().parse()?;
let y = second.trim().parse()?; let y = second.trim().parse()?;
Ok(Pos2::new(x, y)) Ok(Pos::new(x, y))
} }
_ => Err(CaveError::NotAValidPos(coord.to_owned())), _ => Err(CaveError::NotAValidPos(coord.to_owned())),
}) })
@ -171,8 +173,8 @@ impl Cave {
drops drops
} }
fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos2<i32>> { fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos<i32>> {
let mut drop = Pos2::new(500, 0); let mut drop = Pos::new(500, 0);
loop { loop {
let next_y = drop.y() + 1; let next_y = drop.y() + 1;
if next_y > self.height { if next_y > self.height {
@ -180,7 +182,7 @@ impl Cave {
} }
let mut stuck = true; let mut stuck = true;
for dx in [0, -1, 1] { for dx in [0, -1, 1] {
let next = Pos2::new(drop.x() + dx, next_y); let next = Pos::new(drop.x() + dx, next_y);
if !self.get(map, next) { if !self.get(map, next) {
drop = next; drop = next;
stuck = false; stuck = false;
@ -252,7 +254,7 @@ mod test {
#[test] #[test]
fn test_parse() -> Result<()> { fn test_parse() -> Result<()> {
let input = "498,4 -> 498,6 -> 496,6"; let input = "498,4 -> 498,6 -> 496,6";
let expected = hashset! {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)?; let result = Cave::parse_one(input)?;
assert_eq!(result, expected); assert_eq!(result, expected);

View file

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

View file

@ -1,8 +1,5 @@
use super::template::{DayTrait, ResultType}; use super::template::{DayTrait, ResultType};
use crate::common::{ use crate::common::parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1};
name::Name,
parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1},
};
use itertools::Itertools; use itertools::Itertools;
use nom::{ use nom::{
branch::alt, branch::alt,
@ -98,7 +95,6 @@ impl Mul<Flow> for Time {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
struct Flow(i64); struct Flow(i64);
impl Deref for Flow { impl Deref for Flow {
type Target = i64; type Target = i64;
@ -212,7 +208,7 @@ impl<'a> TryFrom<&'a str> for RawValve<'a> {
#[derive(Debug)] #[derive(Debug)]
struct Valve { struct Valve {
id: Name, id: String,
idx: Index, idx: Index,
flow: Flow, flow: Flow,
distances: Vec<Time>, distances: Vec<Time>,
@ -249,7 +245,7 @@ impl Valve {
distances.sort_unstable_by_key(|(id, _)| *id); distances.sort_unstable_by_key(|(id, _)| *id);
Ok(Valve { Ok(Valve {
id: valve.id.into(), id: valve.id.to_owned(),
idx, idx,
flow: valve.flow, flow: valve.flow,
distances: distances distances: distances
@ -261,7 +257,7 @@ impl Valve {
fn zero_flow(valve: &RawValve, idx: Index) -> Valve { fn zero_flow(valve: &RawValve, idx: Index) -> Valve {
Valve { Valve {
id: valve.id.into(), id: valve.id.to_owned(),
idx, idx,
flow: valve.flow, flow: valve.flow,
distances: vec![], distances: vec![],
@ -313,12 +309,11 @@ impl ValveSystem {
} }
#[allow(dead_code)] #[allow(dead_code)]
fn get_valve_by_id<N: Into<Name>>(&self, id: N) -> Result<&Valve, ValveError> { fn get_valve_by_id(&self, id: &str) -> Result<&Valve, ValveError> {
let id = id.into();
self.valves self.valves
.iter() .iter()
.find(|valve| valve.id == id) .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 { 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 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; use thiserror::Error;
const DAY_NUMBER: usize = 17; 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 itertools::Itertools;
use std::{collections::HashSet, num::ParseIntError}; use std::{collections::HashSet, num::ParseIntError};
use thiserror::Error; use thiserror::Error;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
const DAY_NUMBER: usize = 18; const DAY_NUMBER: usize = 18;
pub struct Day; pub struct Day;
@ -171,7 +173,7 @@ impl Blob {
} }
pub fn count_outside(&self) -> i64 { pub fn count_outside(&self) -> i64 {
let Some(mut extent) = self.extent() else { let Some(mut extent)= self.extent() else {
return 0; return 0;
}; };
let mut sides = 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 super::template::{DayTrait, ResultType};
use nom::{error::Error, Err, IResult, Parser};
use thiserror::Error; use thiserror::Error;
const DAY_NUMBER: usize = todo!(); const DAY_NUMBER: usize = todo!();
@ -22,14 +21,8 @@ impl DayTrait for Day {
#[derive(Debug, Error)] #[derive(Debug, Error)]
enum DayError { enum DayError {
#[error("Not a valid description: {0}")] #[error("Dummy")]
ParsingError(String), Dummy,
}
impl From<Err<Error<&str>>> for DayError {
fn from(error: Err<Error<&str>>) -> Self {
DayError::ParsingError(error.to_string())
}
} }
#[cfg(test)] #[cfg(test)]

View file

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

View file

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