Compare commits
No commits in common. "41b013b5e931a2407bf0a0e13e86dcbd1ebf4ba2" and "4a4117882a03d76c6f92d293cdbbaaa5a5f98820" have entirely different histories.
41b013b5e9
...
4a4117882a
51 changed files with 10551 additions and 4970 deletions
|
|
@ -4,6 +4,6 @@ These are my solutions for [Advent of Code 2022](https://adventofcode.com/). Tha
|
|||
|
||||
If you look at the code and see ways I could improve it, please do not hesitate to contact me. I am always grateful for everything that makes me a better programmer.
|
||||
|
||||
Also, if you ever look for a programmer and think my code is any good, please consider contacting me.
|
||||
Also, if you ever look for a programmer and think my code is any good, please also contact me at once.
|
||||
|
||||
All code is published under the [Unlicense](https://unlicense.org/)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
1579
data/day21/input.txt
1579
data/day21/input.txt
File diff suppressed because it is too large
Load diff
|
|
@ -1,14 +0,0 @@
|
|||
...#
|
||||
.#..
|
||||
#...
|
||||
....
|
||||
...#.......#
|
||||
........#...
|
||||
..#....#....
|
||||
..........#.
|
||||
...#....
|
||||
.....#..
|
||||
.#......
|
||||
......#.
|
||||
|
||||
10R5L5R10L4R5L5
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +0,0 @@
|
|||
....#..
|
||||
..###.#
|
||||
#...#.#
|
||||
.#...##
|
||||
#.###..
|
||||
##.#.##
|
||||
.#..#..
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
.....
|
||||
..##.
|
||||
..#..
|
||||
.....
|
||||
..##.
|
||||
.....
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
###.###..#.....#..#.##..##.#...###.#####..#.##.#####.###..######.#.#.#
|
||||
....##.#.#.#......#.###..##..###...#.#...####.##...#.#....#.##.##....#
|
||||
#..#.#.#.##.........#####....#..##.......#.#.#.##....#.#....##..##..##
|
||||
#.##.##....#.##.#.###.#...#..#.##.####.#.#.#.#.###.###.####....#.#.#.#
|
||||
.#.#.#####.##...##.##..#..#.#..####.#..#.#.#######..#.##.#.###.#.....#
|
||||
..#..#....###.#....###.##.#..#..#.#..#.#.#...##...#....##....#..##...#
|
||||
###..........#..#.###....##.##...#####.###.#.##...#.##.....#.##.#.####
|
||||
#.###.####...#..#.##.....#.####.##..##...#..##.....#...###..##.######.
|
||||
.....#.####..##.#...#..##.###..#.##.####..#...#.###..##..#.#.###...#.#
|
||||
.#.#.#..##.##..#.#.##.#.##.#.##..##.##.#..##.#.##..#####.#.###.#.##..#
|
||||
###.#.###.##.##....#.#.#...##...#.#..#.#..#.#..##.#....#####...#...###
|
||||
.#.#.#.#.#..#....###.#.##.#.#.####..#.#.#...#.#.#.###....#....#...##..
|
||||
#.#....#........#....######...#....#..#.###...#.....###..###...##..#..
|
||||
.###.##....#.#.#.###..###....#.#.#..###.##..#..#..#####.#.###.#..##..#
|
||||
..###.##..####.....#...##.#....#...###..#.###.#...##....####........#.
|
||||
#####.##..#..##..###....#.##....#.#...##.########...#.#.#..##..##..###
|
||||
.##..#...##.....###.#...#..###.######.#.#...#...##..#.....#..#.##..#.#
|
||||
.#####..#.######.#..#..#.#.#.##....##..#.......#....#.##.#.#..##...#.#
|
||||
.#.##..#..###..#.######........##.##.##..#....#.###.######.##.#.#..##.
|
||||
##.##.#..####.#....###.#..##..#...#.#####.#..#...#....##...#.###..#.##
|
||||
#....##..#...#.#.###.#.#...##.#.#..###...####..##.#..##.#.#####.....##
|
||||
.##.#..#.##.##.#.##.....#######..#...#.##.####...#.....#.##..#...##...
|
||||
#.#.#.#####...#####..#.##....#.#.####.#..#.##..#.#.#...#..##.#...##...
|
||||
#.#.#.#..#.#.#..#..###.#.....#.#.#..#.#####.#.#..#...#.###.###...#.#..
|
||||
.####..####.#.#..#...##.##.#....#.#.....#.##.###..#...###.###.#....##.
|
||||
....##.#.##.###.#..##.#..#.......##...#.###...#.......#..####.#.#.####
|
||||
....#.#......####..#.#..#..#..#.##..#..#..###..#.#.##.#.##..###..#..#.
|
||||
.##.#.....##.#..#..#..####..#.#.#...###.##.##.##...#..##..##.##...##..
|
||||
#..####..#...#.#....##...##..#.....##.#######..#.####...##..######.#..
|
||||
..#.##.##....##.#######..##...#..#..###.....##.#.#..#.#...##..###.####
|
||||
#####......##..#.#..#......#..####.####...#####..######...##.###..##..
|
||||
###..##.#.##....##..######.#.#.#......#..##.#.##.#.##...#.##...###..##
|
||||
.....#.#.#.##..#.#####.##.#.#.....#.#..#.###.##..#.###.##..##.###....#
|
||||
.###.####..####.###....####.#.##..##..#.....##.###.##.######.#..#.#...
|
||||
.#.#######.#.#.###....#####..#.#.#..#.#.#.#.#..##..#..#...##..##..#.#.
|
||||
.##..####..#.#...###.#...#....####...###.#..#.#.....#.#.##.#..##.#.#..
|
||||
.#.#.#.#####..##.####..#..##......#..#.###.#..#....#...#....#...#.####
|
||||
..##.##.###.#..#..#.#..#.###......##.#..#..#.#.####.#.#.#.####.##.....
|
||||
##.#..#..##.#######..#..#....##..#...########.##.#.#...##...##.##.....
|
||||
.#...#####.#..##.##.###.####.....###.#......########.#..##.##.#....###
|
||||
...####.###....##..#..#....#..##.####..##..#..###.......#.#.###.##...#
|
||||
.#.#......#####...##....#.#..#######.######.#.#.##.#..###..#....##..#.
|
||||
..###.#.#####..##.#.####....#.###..#..#.##.#####.##..##.##..##..#.##.#
|
||||
.####.#.######.###.###..#......#...#...#..#..##.###.##.#.####.#.#.##.#
|
||||
#####.###.##.#########..###..#.#.##.#..#..##...#.###.#.#......###..##.
|
||||
###...###.##.#.###.#.##.####.#######..##.#....#####.###.#..#.#...###..
|
||||
.######...#...#..###......#.#.##........#..##.#.#.##.#....###.....##.#
|
||||
#.#.....##.##.##..##..##.#.##.###.##.###.##.#.##.###....##..#.#.##....
|
||||
#.####.##...##....#.#.....#.#.###..#..####.....#.##..##...##.###.#..##
|
||||
#####.##.#...#....#..###.##..##...#....#....##.#.#....#####....####.##
|
||||
####...##...#.###.##.##.#####...#.....##..#####...########..####......
|
||||
#.#..##.##.#.######.###.#.##.......#......####..##.#.#.#..##.###.#..#.
|
||||
#..#..########..##..#.######..#.#...#..#.#####.###.####......##..###.#
|
||||
#..#.#...#..#....#..##.##.#..###.####.###########.#..#.###.#.##.###..#
|
||||
##.#..#..#.....#.##.#..######..#..#...#.#...###.#.#.#.#.#.########...#
|
||||
..###.#.##......###..#...##......#####.#.#....####.....##.#..#.#..#.#.
|
||||
..######....#..##.#.#..#.##.#..##.##.#....##....###.#.##.##..##....###
|
||||
.#####....##....###.#.#######.....##.###.#..#.#.#..###...#.##..#..#.#.
|
||||
#..#####...#..##..#..#..#....#.##....#..##.......##..#....#.#..#.#.###
|
||||
#.#..#####...#...#...##.#....#..##.#.##....#####.....#.##.....#..#.###
|
||||
##.#.#....#...#.####....###....#...#.##...##.#....#.#.#.##......##.###
|
||||
...##.#.#..#.###..#.#...##.#...##.#...##...#..###.#.###...###..#..##.#
|
||||
.#####...#.#..###.#.##.##.#.##..##.#.#.....##....#.#..#.#.#..#####.###
|
||||
#.####.####..###......##.....#.#.....##.#..##.##.#....###.....####..##
|
||||
#..#####...#....#...###..##...###..####..####..#...#####.#####..#.###.
|
||||
####.###.###....####....##..#..####...#....###.##.#.##...##.......####
|
||||
.#.###....####..#..#.###.##.##.####..##.#..###..#..#.#.#..#..#.##..##.
|
||||
....##......####.####.######...###.....###.#.#..##.#.##.......#...#...
|
||||
##.###.##...#.....#.##.#.......####.##.###..###.#.#..#.##..#.####....#
|
||||
....#..##.#...##.##.#.#.#..#.#..#..#.####....#####..#.....#####.#.....
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#.######
|
||||
#>>.<^<#
|
||||
#.<..<<#
|
||||
#>v.><>#
|
||||
#<^v^^>#
|
||||
######.#
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#.#####
|
||||
#.....#
|
||||
#>....#
|
||||
#.....#
|
||||
#...v.#
|
||||
#.....#
|
||||
#####.#
|
||||
|
|
@ -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>#
|
||||
####################################################################################################.#
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
1=-0-2
|
||||
12111
|
||||
2=0=
|
||||
21
|
||||
2=01
|
||||
111
|
||||
20012
|
||||
112
|
||||
1=-1=
|
||||
1-12
|
||||
12
|
||||
1=
|
||||
122
|
||||
|
|
@ -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
|
||||
5001
result.txt
Normal file
5001
result.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
|
@ -3,25 +3,25 @@ use std::fmt::Display;
|
|||
|
||||
use num_traits::{Num, NumAssignOps};
|
||||
|
||||
use super::pos2::Pos2;
|
||||
use super::pos::Pos;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Area<T>
|
||||
where
|
||||
T: Num,
|
||||
{
|
||||
lower_left: Pos2<T>,
|
||||
upper_right: Pos2<T>,
|
||||
lower_left: Pos<T>,
|
||||
upper_right: Pos<T>,
|
||||
}
|
||||
|
||||
impl<T> Area<T>
|
||||
where
|
||||
T: Num + Ord + Copy,
|
||||
{
|
||||
pub fn new(p1: Pos2<T>, p2: Pos2<T>) -> Area<T> {
|
||||
pub fn new(p1: Pos<T>, p2: Pos<T>) -> Area<T> {
|
||||
Area {
|
||||
lower_left: p1.min_components(p2),
|
||||
upper_right: p1.max_components(p2),
|
||||
lower_left: p1.min_components(&p2),
|
||||
upper_right: p1.max_components(&p2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,25 +30,25 @@ impl<T> Area<T>
|
|||
where
|
||||
T: Num + Ord + Copy,
|
||||
{
|
||||
pub fn extend(&self, pos: Pos2<T>) -> Area<T> {
|
||||
pub fn extend(&self, pos: Pos<T>) -> Area<T> {
|
||||
if self.contains(pos) {
|
||||
return *self;
|
||||
}
|
||||
|
||||
Area {
|
||||
lower_left: self.lower_left.min_components(pos),
|
||||
upper_right: self.upper_right.max_components(pos),
|
||||
lower_left: self.lower_left.min_components(&pos),
|
||||
upper_right: self.upper_right.max_components(&pos),
|
||||
}
|
||||
}
|
||||
pub fn get_lower_left(&self) -> Pos2<T> {
|
||||
pub fn get_lower_left(&self) -> Pos<T> {
|
||||
self.lower_left
|
||||
}
|
||||
|
||||
pub fn get_upper_right(&self) -> Pos2<T> {
|
||||
pub fn get_upper_right(&self) -> Pos<T> {
|
||||
self.upper_right
|
||||
}
|
||||
|
||||
pub fn contains(&self, pos: Pos2<T>) -> bool {
|
||||
pub fn contains(&self, pos: Pos<T>) -> bool {
|
||||
self.lower_left.x() >= pos.x()
|
||||
&& pos.x() >= self.upper_right.x()
|
||||
&& self.lower_left.y() >= pos.y()
|
||||
|
|
@ -62,11 +62,11 @@ where
|
|||
{
|
||||
pub fn from_iterator<I>(mut iter: I) -> Option<Self>
|
||||
where
|
||||
I: Iterator<Item = &'a Pos2<T>>,
|
||||
I: Iterator<Item = &'a Pos<T>>,
|
||||
{
|
||||
let first = *iter.next()?;
|
||||
let (upper, lower) = iter.fold((first, first), |(mx, mn), p| {
|
||||
(mx.max_components(*p), mn.min_components(*p))
|
||||
(mx.max_components(p), mn.min_components(p))
|
||||
});
|
||||
|
||||
Some(Area::new(lower, upper))
|
||||
|
|
@ -214,12 +214,12 @@ impl<'a, T> Iterator for ColIterator<'a, T>
|
|||
where
|
||||
T: Num + Ord + NumAssignOps + Copy,
|
||||
{
|
||||
type Item = Pos2<T>;
|
||||
type Item = Pos<T>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if (self.ascending && self.col <= self.area.upper_right.x())
|
||||
|| (!self.ascending && self.col >= self.area.lower_left.x())
|
||||
{
|
||||
let pos = Pos2::new(self.col, self.row);
|
||||
let pos = Pos::new(self.col, self.row);
|
||||
if self.ascending {
|
||||
self.col += T::one();
|
||||
} else {
|
||||
|
|
@ -266,13 +266,13 @@ impl<'a, T> Iterator for CellIterator<'a, T>
|
|||
where
|
||||
T: Num + Ord + NumAssignOps + Copy,
|
||||
{
|
||||
type Item = Pos2<T>;
|
||||
type Item = Pos<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if (self.ascending && self.row <= self.area.upper_right.y())
|
||||
|| (!self.ascending && self.row >= self.area.lower_left.y())
|
||||
{
|
||||
let pos = Pos2::new(self.col, self.row);
|
||||
let pos = Pos::new(self.col, self.row);
|
||||
if self.ascending {
|
||||
self.col += T::one();
|
||||
if self.col > self.area.upper_right.x() {
|
||||
|
|
@ -300,18 +300,18 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_cell_iterator() {
|
||||
let area = Area::new(Pos2::new(-1, -1), Pos2::new(1, 1));
|
||||
let area = Area::new(Pos::new(-1, -1), Pos::new(1, 1));
|
||||
let result = area.cells(true).collect::<Vec<_>>();
|
||||
let expected = vec![
|
||||
Pos2::new(-1, -1),
|
||||
Pos2::new(0, -1),
|
||||
Pos2::new(1, -1),
|
||||
Pos2::new(-1, 0),
|
||||
Pos2::new(0, 0),
|
||||
Pos2::new(1, 0),
|
||||
Pos2::new(-1, 1),
|
||||
Pos2::new(0, 1),
|
||||
Pos2::new(1, 1),
|
||||
Pos::new(-1, -1),
|
||||
Pos::new(0, -1),
|
||||
Pos::new(1, -1),
|
||||
Pos::new(-1, 0),
|
||||
Pos::new(0, 0),
|
||||
Pos::new(1, 0),
|
||||
Pos::new(-1, 1),
|
||||
Pos::new(0, 1),
|
||||
Pos::new(1, 1),
|
||||
];
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
use super::{pos2::Pos2, turn::Turn};
|
||||
use num_traits::{Num, Signed};
|
||||
use super::{pos::Pos, turn::Turn};
|
||||
use std::{fmt::Display, ops::Add};
|
||||
|
||||
use Direction::*;
|
||||
use Turn::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Direction {
|
||||
East = 0,
|
||||
North = 1,
|
||||
West = 2,
|
||||
South = 3,
|
||||
East,
|
||||
North,
|
||||
West,
|
||||
South,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn as_pos(&self) -> Pos<i32> {
|
||||
match *self {
|
||||
East => Pos::new(1, 0),
|
||||
North => Pos::new(0, 1),
|
||||
West => Pos::new(-1, 0),
|
||||
South => Pos::new(0, -1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_perpendicular(&self, other: &Direction) -> bool {
|
||||
match *self {
|
||||
East => *other != East && *other != West,
|
||||
|
|
@ -82,26 +91,35 @@ impl Display for Direction {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<Direction> for Pos2<T>
|
||||
where
|
||||
T: Num + Signed,
|
||||
{
|
||||
fn from(value: Direction) -> Self {
|
||||
match value {
|
||||
East => Pos2::new(T::one(), T::zero()),
|
||||
North => Pos2::new(T::zero(), -T::one()),
|
||||
West => Pos2::new(-T::one(), T::zero()),
|
||||
South => Pos2::new(T::zero(), T::one()),
|
||||
}
|
||||
impl Add<Pos<i32>> for Direction {
|
||||
type Output = Pos<i32>;
|
||||
|
||||
fn add(self, rhs: Pos<i32>) -> Self::Output {
|
||||
Pos::add(rhs, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Direction> for Pos2<i32> {
|
||||
type Output = Pos2<i32>;
|
||||
impl Add<Pos<i32>> for &Direction {
|
||||
type Output = Pos<i32>;
|
||||
|
||||
fn add(self, rhs: Direction) -> Self::Output {
|
||||
let rhs: Pos2<i32> = rhs.into();
|
||||
self + rhs
|
||||
fn add(self, rhs: Pos<i32>) -> Self::Output {
|
||||
Pos::add(rhs, *self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Pos<i32>> for Direction {
|
||||
type Output = Pos<i32>;
|
||||
|
||||
fn add(self, rhs: &Pos<i32>) -> Self::Output {
|
||||
Pos::add(*rhs, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Pos<i32>> for &Direction {
|
||||
type Output = Pos<i32>;
|
||||
|
||||
fn add(self, rhs: &Pos<i32>) -> Self::Output {
|
||||
Pos::add(*rhs, *self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,3 +130,27 @@ impl Add<Turn> for Direction {
|
|||
self.turn(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Turn> for Direction {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: &Turn) -> Direction {
|
||||
Direction::add(self, *rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Turn> for &Direction {
|
||||
type Output = Direction;
|
||||
|
||||
fn add(self, rhs: Turn) -> Direction {
|
||||
Direction::add(*self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Turn> for &Direction {
|
||||
type Output = Direction;
|
||||
|
||||
fn add(self, rhs: &Turn) -> Direction {
|
||||
Direction::add(*self, *rhs)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,24 +27,20 @@ where
|
|||
a
|
||||
}
|
||||
|
||||
pub fn gcd<T>(a: T, b: T) -> Option<T>
|
||||
pub fn gcd<T>(a: T, b: T) -> T
|
||||
where
|
||||
T: Num + Ord + Copy,
|
||||
{
|
||||
assert!(a >= T::zero());
|
||||
assert!(b >= T::zero());
|
||||
|
||||
if a.is_zero() {
|
||||
if b.is_zero() {
|
||||
None
|
||||
b
|
||||
} else if b.is_zero() {
|
||||
a
|
||||
} else {
|
||||
Some(b)
|
||||
}
|
||||
} else {
|
||||
Some(non_zero_gcd(a, b))
|
||||
non_zero_gcd(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lcm<T>(a: T, b: T) -> T
|
||||
where
|
||||
T: Num + Ord + Copy,
|
||||
|
|
@ -81,9 +77,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn some_simple_gcd() {
|
||||
assert_eq!(Some(5), gcd(10, 15));
|
||||
assert_eq!(Some(7), gcd(21, 49));
|
||||
assert_eq!(Some(1), gcd(13, 17));
|
||||
assert_eq!(5, gcd(10, 15));
|
||||
assert_eq!(7, gcd(21, 49));
|
||||
assert_eq!(1, gcd(13, 17));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
pub mod abs;
|
||||
pub mod area;
|
||||
pub mod direction;
|
||||
pub mod file;
|
||||
pub mod helper;
|
||||
pub mod math;
|
||||
pub mod name;
|
||||
pub mod parser;
|
||||
pub mod pos2;
|
||||
pub mod pos3;
|
||||
pub mod pos;
|
||||
pub mod turn;
|
||||
pub mod unit_vector;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete::{line_ending, space0, space1, u32},
|
||||
character::complete::{line_ending, space0, space1},
|
||||
combinator::{eof, opt, value},
|
||||
error::ParseError,
|
||||
multi::many0,
|
||||
|
|
@ -34,10 +34,6 @@ where
|
|||
terminated(line, alt((line_ending, eof)))
|
||||
}
|
||||
|
||||
pub fn usize<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, usize, E> {
|
||||
u32.map(|v| v as usize).parse(input)
|
||||
}
|
||||
|
||||
pub fn true_false<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
|
||||
alt((value(true, tag("true")), value(false, tag("false"))))(input)
|
||||
}
|
||||
|
|
|
|||
278
src/common/pos.rs
Normal file
278
src/common/pos.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
use itertools::Itertools;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
|
||||
pub struct Day;
|
||||
const DAY_NUMBER: usize = 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::common::file::split_lines;
|
||||
use std::{cmp::Ordering, str::FromStr};
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 2;
|
||||
|
|
@ -58,7 +60,9 @@ impl Rps {
|
|||
Err(RPSError::IllegalLine(line.to_owned()))?
|
||||
};
|
||||
|
||||
Ok((first.parse()?, second.parse()?))
|
||||
let first = Rps::try_from(first)?;
|
||||
let second = Rps::try_from(second)?;
|
||||
Ok((first, second))
|
||||
}
|
||||
|
||||
pub fn value(&self) -> i64 {
|
||||
|
|
@ -94,10 +98,10 @@ impl PartialOrd for Rps {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Rps {
|
||||
type Err = RPSError;
|
||||
impl TryFrom<&str> for Rps {
|
||||
type Error = RPSError;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
"A" | "X" => Ok(Rps::Rock),
|
||||
"B" | "Y" => Ok(Rps::Paper),
|
||||
|
|
@ -106,7 +110,6 @@ impl FromStr for Rps {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
enum Strategy {
|
||||
Loose,
|
||||
|
|
@ -121,7 +124,9 @@ impl Strategy {
|
|||
Err(RPSError::IllegalLine(line.to_owned()))?
|
||||
};
|
||||
|
||||
Ok((first.parse()?, second.parse()?))
|
||||
let first = Rps::try_from(first)?;
|
||||
let second = Strategy::try_from(second)?;
|
||||
Ok((first, second))
|
||||
}
|
||||
|
||||
pub fn fullfill(&self, other: &Rps) -> Rps {
|
||||
|
|
@ -134,10 +139,10 @@ impl Strategy {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Strategy {
|
||||
type Err = RPSError;
|
||||
impl TryFrom<&str> for Strategy {
|
||||
type Error = RPSError;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
"X" => Ok(Strategy::Loose),
|
||||
"Y" => Ok(Strategy::Draw),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use itertools::Itertools;
|
||||
use std::num::ParseIntError;
|
||||
use thiserror::Error;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use itertools::Itertools;
|
||||
use std::num::ParseIntError;
|
||||
use thiserror::Error;
|
||||
|
|
@ -123,7 +124,7 @@ impl Move {
|
|||
if self.to_stack >= stacks.len() {
|
||||
return Err(CrateError::TooFewStacks(stacks.len(), self.to_stack));
|
||||
}
|
||||
let Some(from_stack) = stacks.get(self.from_stack) else {
|
||||
let Some(from_stack) = stacks.get(self.from_stack ) else {
|
||||
return Err(CrateError::TooFewStacks(stacks.len(), self.from_stack));
|
||||
};
|
||||
if from_stack.len() < self.amount {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{file::split_lines, name::Name};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
num::ParseIntError,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 7;
|
||||
|
|
@ -73,7 +75,7 @@ enum DirectoryError {
|
|||
#[derive(Debug)]
|
||||
struct Node {
|
||||
parent: Option<Weak<RefCell<Node>>>,
|
||||
name: Name,
|
||||
name: String,
|
||||
sub_dirs: Vec<Rc<RefCell<Node>>>,
|
||||
file_size: i64,
|
||||
size: Cell<Option<i64>>,
|
||||
|
|
@ -83,7 +85,7 @@ impl Node {
|
|||
fn root() -> Node {
|
||||
Node {
|
||||
parent: None,
|
||||
name: "/".into(),
|
||||
name: "/".to_owned(),
|
||||
sub_dirs: vec![],
|
||||
file_size: 0,
|
||||
size: Cell::new(None),
|
||||
|
|
@ -94,7 +96,7 @@ impl Node {
|
|||
self.sub_dirs.push(subdir);
|
||||
}
|
||||
|
||||
fn add_file<N: Into<Name>>(&mut self, _name: N, size: i64) {
|
||||
fn add_file(&mut self, _name: &str, size: i64) {
|
||||
self.file_size += size;
|
||||
}
|
||||
|
||||
|
|
@ -139,10 +141,10 @@ impl Directory {
|
|||
self.node.borrow().file_size
|
||||
}
|
||||
|
||||
pub fn add_subdir<N: Into<Name>>(&mut self, name: N) {
|
||||
pub fn add_subdir(&mut self, name: &str) {
|
||||
let subdir = Rc::new(RefCell::new(Node {
|
||||
parent: Some(Rc::downgrade(&self.node)),
|
||||
name: name.into(),
|
||||
name: name.to_owned(),
|
||||
sub_dirs: vec![],
|
||||
file_size: 0,
|
||||
size: Cell::new(None),
|
||||
|
|
@ -150,7 +152,7 @@ impl Directory {
|
|||
self.node.borrow_mut().add_subdir(subdir);
|
||||
}
|
||||
|
||||
pub fn add_file<N: Into<Name>>(&mut self, name: N, size: i64) {
|
||||
pub fn add_file(&mut self, name: &str, size: i64) {
|
||||
self.node.borrow_mut().add_file(name, size)
|
||||
}
|
||||
|
||||
|
|
@ -163,9 +165,8 @@ impl Directory {
|
|||
.map(|node| Directory { node })
|
||||
}
|
||||
|
||||
pub fn get_subdir<N: Into<Name>>(&self, name: N) -> Option<Directory> {
|
||||
pub fn get_subdir(&self, name: &str) -> Option<Directory> {
|
||||
let node = self.node.borrow();
|
||||
let name = name.into();
|
||||
let sub_node = node.sub_dirs.iter().find(|node| node.borrow().name == name);
|
||||
sub_node.map(|node| Directory::create(node.clone()))
|
||||
}
|
||||
|
|
@ -186,7 +187,7 @@ impl Directory {
|
|||
}
|
||||
_ => {
|
||||
let Some(next) = current.get_subdir(dir) else {
|
||||
return Err(DirectoryError::NoSuchDirectory(dir.to_owned()));
|
||||
return Err(DirectoryError::NoSuchDirectory(dir.to_owned()))
|
||||
};
|
||||
next
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
use itertools::{iproduct, FoldWhile, Itertools};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use itertools::{iproduct, FoldWhile, Itertools};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 8;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{direction::Direction, file::split_lines, pos2::Pos2};
|
||||
use crate::common::{direction::Direction, file::split_lines, pos::Pos};
|
||||
use itertools::Itertools;
|
||||
use std::{collections::HashSet, num::ParseIntError};
|
||||
use thiserror::Error;
|
||||
|
|
@ -63,27 +63,27 @@ fn parse_line(line: &str) -> Result<(Direction, usize), RopeError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_closer(first: Pos2<i32>, second: Pos2<i32>) -> Option<Pos2<i32>> {
|
||||
fn get_closer(first: Pos<i32>, second: Pos<i32>) -> Option<Pos<i32>> {
|
||||
let diff = second - first;
|
||||
if diff.x().abs() <= 1 && diff.y().abs() <= 1 {
|
||||
None
|
||||
} else {
|
||||
Some(Pos2::new(diff.x().signum(), diff.y().signum()))
|
||||
Some(Pos::new(diff.x().signum(), diff.y().signum()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Rope {
|
||||
head: Pos2<i32>,
|
||||
knots: Vec<Pos2<i32>>,
|
||||
head: Pos<i32>,
|
||||
knots: Vec<Pos<i32>>,
|
||||
}
|
||||
|
||||
impl Rope {
|
||||
pub fn create(length: usize) -> Self {
|
||||
assert!(length >= 2);
|
||||
Rope {
|
||||
head: Pos2::new(0, 0),
|
||||
knots: vec![Pos2::new(0, 0); length - 1],
|
||||
head: Pos::new(0, 0),
|
||||
knots: vec![Pos::new(0, 0); length - 1],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
use itertools::Itertools;
|
||||
use std::{num::ParseIntError, slice::Iter, str::FromStr};
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use std::{num::ParseIntError, slice::Iter};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 10;
|
||||
|
|
@ -14,13 +14,17 @@ impl DayTrait for Day {
|
|||
}
|
||||
|
||||
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let instructions: Vec<_> = split_lines(lines).map(|line| line.parse()).try_collect()?;
|
||||
let instructions = split_lines(lines)
|
||||
.map(|line| Instruction::parse(line))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let result = CpuCycles::signal_strength(&instructions, &[20, 60, 100, 140, 180, 220]);
|
||||
Ok(ResultType::Integer(result as i64))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let instructions: Vec<_> = split_lines(lines).map(|line| line.parse()).try_collect()?;
|
||||
let instructions = split_lines(lines)
|
||||
.map(|line| Instruction::parse(line))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let result = CpuCycles::draw(&instructions);
|
||||
Ok(ResultType::Lines(result))
|
||||
}
|
||||
|
|
@ -35,18 +39,18 @@ enum CpuError {
|
|||
InvalidInteger(#[from] ParseIntError),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Instruction(Option<i32>);
|
||||
enum Instruction {
|
||||
Add(i32),
|
||||
Noop,
|
||||
}
|
||||
|
||||
impl FromStr for Instruction {
|
||||
type Err = CpuError;
|
||||
|
||||
fn from_str(line: &str) -> Result<Self, Self::Err> {
|
||||
impl Instruction {
|
||||
pub fn parse(line: &str) -> Result<Self, CpuError> {
|
||||
if line == "noop" {
|
||||
Ok(Instruction(None))
|
||||
Ok(Instruction::Noop)
|
||||
} else if line.starts_with("addx") {
|
||||
let value = line[5..].parse()?;
|
||||
Ok(Instruction(Some(value)))
|
||||
Ok(Instruction::Add(value))
|
||||
} else {
|
||||
Err(CpuError::UnknownInstruction(line.to_owned()))
|
||||
}
|
||||
|
|
@ -110,7 +114,10 @@ impl<'a> Iterator for CpuCycles<'a> {
|
|||
self.current = None;
|
||||
Some(start_register)
|
||||
} else if let Some(instruction) = self.instructions.next() {
|
||||
self.current = instruction.0;
|
||||
match instruction {
|
||||
Instruction::Add(value) => self.current = Some(*value),
|
||||
Instruction::Noop => {}
|
||||
}
|
||||
Some(self.register)
|
||||
} else {
|
||||
None
|
||||
|
|
@ -127,11 +134,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_simple() -> Result<()> {
|
||||
let instructions = vec![
|
||||
Instruction(None),
|
||||
Instruction(Some(3)),
|
||||
Instruction(Some(-5)),
|
||||
];
|
||||
let instructions = vec![Instruction::Noop, Instruction::Add(3), Instruction::Add(-5)];
|
||||
let expected = vec![1, 1, 1, 4, 4];
|
||||
let result = CpuCycles::create(&instructions).collect::<Vec<_>>();
|
||||
assert_eq!(result, expected);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::parser::{
|
||||
empty_lines, eol_terminated, extract_result, ignore, trim0, trim_left1, true_false,
|
||||
};
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{file::split_lines, pos2::Pos2};
|
||||
use std::collections::{BinaryHeap, HashSet};
|
||||
|
||||
use crate::common::{file::split_lines, pos::Pos};
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 12;
|
||||
|
|
@ -43,7 +45,7 @@ enum ValleyError {
|
|||
struct Path {
|
||||
length: usize,
|
||||
height: char,
|
||||
pos: Pos2<usize>,
|
||||
pos: Pos<usize>,
|
||||
}
|
||||
|
||||
impl PartialOrd for Path {
|
||||
|
|
@ -76,7 +78,7 @@ impl Ord for Path {
|
|||
}
|
||||
|
||||
impl Path {
|
||||
pub fn new(length: usize, height: char, pos: Pos2<usize>) -> Self {
|
||||
pub fn new(length: usize, height: char, pos: Pos<usize>) -> Self {
|
||||
Path {
|
||||
length,
|
||||
height,
|
||||
|
|
@ -104,28 +106,28 @@ impl<'a> Neighbors<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn next_pos(&mut self) -> Option<Pos2<usize>> {
|
||||
fn next_pos(&mut self) -> Option<Pos<usize>> {
|
||||
while self.state < 4 {
|
||||
self.state += 1;
|
||||
match self.state {
|
||||
1 => {
|
||||
if self.path.pos.x() < self.valley.width() - 1 {
|
||||
return Some(Pos2::new(self.path.pos.x() + 1, self.path.pos.y()));
|
||||
return Some(Pos::new(self.path.pos.x() + 1, self.path.pos.y()));
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if self.path.pos.y() > 0 {
|
||||
return Some(Pos2::new(self.path.pos.x(), self.path.pos.y() - 1));
|
||||
return Some(Pos::new(self.path.pos.x(), self.path.pos.y() - 1));
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if self.path.pos.x() > 0 {
|
||||
return Some(Pos2::new(self.path.pos.x() - 1, self.path.pos.y()));
|
||||
return Some(Pos::new(self.path.pos.x() - 1, self.path.pos.y()));
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
if self.path.pos.y() < self.valley.height() - 1 {
|
||||
return Some(Pos2::new(self.path.pos.x(), self.path.pos.y() + 1));
|
||||
return Some(Pos::new(self.path.pos.x(), self.path.pos.y() + 1));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -151,8 +153,8 @@ impl Iterator for Neighbors<'_> {
|
|||
|
||||
struct Valley {
|
||||
map: Vec<Vec<char>>,
|
||||
start: Pos2<usize>,
|
||||
exit: Pos2<usize>,
|
||||
start: Pos<usize>,
|
||||
exit: Pos<usize>,
|
||||
width: usize,
|
||||
}
|
||||
|
||||
|
|
@ -170,11 +172,11 @@ impl TryFrom<&str> for Valley {
|
|||
for (x, height_char) in row.chars().enumerate() {
|
||||
match height_char {
|
||||
'S' => {
|
||||
start = Some(Pos2::new(x, y));
|
||||
start = Some(Pos::new(x, y));
|
||||
height_row.push('a')
|
||||
}
|
||||
'E' => {
|
||||
exit = Some(Pos2::new(x, y));
|
||||
exit = Some(Pos::new(x, y));
|
||||
height_row.push('z')
|
||||
}
|
||||
'a'..='z' => height_row.push(height_char),
|
||||
|
|
@ -209,13 +211,13 @@ impl TryFrom<&str> for Valley {
|
|||
}
|
||||
|
||||
impl Valley {
|
||||
fn get_height(&self, pos: Pos2<usize>) -> char {
|
||||
fn get_height(&self, pos: Pos<usize>) -> char {
|
||||
self.map[pos.y()][pos.x()]
|
||||
}
|
||||
|
||||
fn do_walk<F>(&self, check: F) -> Result<usize, ValleyError>
|
||||
where
|
||||
F: Fn(Pos2<usize>) -> bool,
|
||||
F: Fn(Pos<usize>) -> bool,
|
||||
{
|
||||
let mut shortest = HashSet::with_capacity(self.width * self.map.len());
|
||||
let mut queue = BinaryHeap::new();
|
||||
|
|
@ -267,8 +269,8 @@ mod test {
|
|||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let valley = Valley::try_from(lines.as_str())?;
|
||||
assert_eq!(valley.width, 8);
|
||||
assert_eq!(valley.start, Pos2::new(0, 0));
|
||||
assert_eq!(valley.exit, Pos2::new(5, 2));
|
||||
assert_eq!(valley.start, Pos::new(0, 0));
|
||||
assert_eq!(valley.exit, Pos::new(5, 2));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
use itertools::Itertools;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 13;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{file::split_lines, pos2::Pos2};
|
||||
use itertools::Itertools;
|
||||
use std::{collections::HashSet, num::ParseIntError};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::common::{file::split_lines, pos::Pos};
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
|
||||
const DAY_NUMBER: usize = 14;
|
||||
|
||||
pub struct Day;
|
||||
|
|
@ -54,7 +56,7 @@ impl TryFrom<&str> for Cave {
|
|||
|
||||
fn try_from(lines: &str) -> Result<Self, Self::Error> {
|
||||
let lines = split_lines(lines);
|
||||
let mut cave: HashSet<Pos2<i32>> = HashSet::new();
|
||||
let mut cave: HashSet<Pos<i32>> = HashSet::new();
|
||||
for line in lines {
|
||||
cave.extend(Cave::parse_one(line)?.iter());
|
||||
}
|
||||
|
|
@ -90,7 +92,7 @@ impl TryFrom<&str> for Cave {
|
|||
}
|
||||
|
||||
impl Cave {
|
||||
fn get(&self, map: &[Vec<bool>], pos: Pos2<i32>) -> bool {
|
||||
fn get(&self, map: &[Vec<bool>], pos: Pos<i32>) -> bool {
|
||||
if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x {
|
||||
false
|
||||
} else {
|
||||
|
|
@ -98,28 +100,28 @@ impl Cave {
|
|||
}
|
||||
}
|
||||
|
||||
fn set(&self, map: &mut [Vec<bool>], pos: Pos2<i32>) {
|
||||
fn set(&self, map: &mut [Vec<bool>], pos: Pos<i32>) {
|
||||
map[pos.y() as usize][(pos.x() - self.min_x) as usize] = true
|
||||
}
|
||||
|
||||
fn walk(from: Pos2<i32>, to: Pos2<i32>) -> Result<Vec<Pos2<i32>>, CaveError> {
|
||||
fn walk(from: Pos<i32>, to: Pos<i32>) -> Result<Vec<Pos<i32>>, CaveError> {
|
||||
if from == to {
|
||||
return Ok(vec![from]);
|
||||
}
|
||||
if from.x() == to.x() {
|
||||
if from.y() < to.y() {
|
||||
Ok((from.y()..to.y()).map(|y| Pos2::new(from.x(), y)).collect())
|
||||
Ok((from.y()..to.y()).map(|y| Pos::new(from.x(), y)).collect())
|
||||
} else {
|
||||
Ok(((to.y() + 1)..=from.y())
|
||||
.map(|y| Pos2::new(from.x(), y))
|
||||
.map(|y| Pos::new(from.x(), y))
|
||||
.collect())
|
||||
}
|
||||
} else if from.y() == to.y() {
|
||||
if from.x() < to.x() {
|
||||
Ok((from.x()..to.x()).map(|x| Pos2::new(x, from.y())).collect())
|
||||
Ok((from.x()..to.x()).map(|x| Pos::new(x, from.y())).collect())
|
||||
} else {
|
||||
Ok(((to.x() + 1)..=from.x())
|
||||
.map(|x| Pos2::new(x, from.y()))
|
||||
.map(|x| Pos::new(x, from.y()))
|
||||
.collect())
|
||||
}
|
||||
} else {
|
||||
|
|
@ -127,14 +129,14 @@ impl Cave {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_one(line: &str) -> Result<HashSet<Pos2<i32>>, CaveError> {
|
||||
fn parse_one(line: &str) -> Result<HashSet<Pos<i32>>, CaveError> {
|
||||
let corners = line
|
||||
.split("->")
|
||||
.map(|coord| match coord.split(',').collect::<Vec<_>>()[..] {
|
||||
[first, second] => {
|
||||
let x: i32 = first.trim().parse()?;
|
||||
let y = second.trim().parse()?;
|
||||
Ok(Pos2::new(x, y))
|
||||
Ok(Pos::new(x, y))
|
||||
}
|
||||
_ => Err(CaveError::NotAValidPos(coord.to_owned())),
|
||||
})
|
||||
|
|
@ -171,8 +173,8 @@ impl Cave {
|
|||
drops
|
||||
}
|
||||
|
||||
fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos2<i32>> {
|
||||
let mut drop = Pos2::new(500, 0);
|
||||
fn drop_one(&self, map: &[Vec<bool>]) -> Option<Pos<i32>> {
|
||||
let mut drop = Pos::new(500, 0);
|
||||
loop {
|
||||
let next_y = drop.y() + 1;
|
||||
if next_y > self.height {
|
||||
|
|
@ -180,7 +182,7 @@ impl Cave {
|
|||
}
|
||||
let mut stuck = true;
|
||||
for dx in [0, -1, 1] {
|
||||
let next = Pos2::new(drop.x() + dx, next_y);
|
||||
let next = Pos::new(drop.x() + dx, next_y);
|
||||
if !self.get(map, next) {
|
||||
drop = next;
|
||||
stuck = false;
|
||||
|
|
@ -252,7 +254,7 @@ mod test {
|
|||
#[test]
|
||||
fn test_parse() -> Result<()> {
|
||||
let input = "498,4 -> 498,6 -> 496,6";
|
||||
let expected = hashset! {Pos2::new(498, 4), Pos2::new(498, 5), Pos2::new(498, 6), Pos2::new(497, 6), Pos2::new(496, 6)};
|
||||
let expected = hashset! {Pos::new(498, 4), Pos::new(498, 5), Pos::new(498, 6), Pos::new(497, 6), Pos::new(496, 6)};
|
||||
let result = Cave::parse_one(input)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{
|
||||
parser::{eol_terminated, extract_result, ignore, trim0},
|
||||
pos2::Pos2,
|
||||
pos::Pos,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
|
|
@ -44,7 +44,7 @@ impl DayTrait for Day {
|
|||
.iter()
|
||||
.tuple_combinations()
|
||||
.filter_map(|(first, second)| first.cross(second))
|
||||
.filter(|pos| sensors.iter().all(|sensor| !sensor.contains(*pos)))
|
||||
.filter(|pos| sensors.iter().all(|sensor| !sensor.contains(pos)))
|
||||
.dedup()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ impl DayTrait for Day {
|
|||
}
|
||||
|
||||
impl Day {
|
||||
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos2<i64>>), SensorError> {
|
||||
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
|
||||
let data = extract_result(many0(eol_terminated(Sensor::parse)))(lines)?;
|
||||
|
||||
let mut sensors = HashSet::new();
|
||||
|
|
@ -70,7 +70,7 @@ impl Day {
|
|||
Ok((sensors, beacons))
|
||||
}
|
||||
|
||||
fn count_coverage_at(sensors: &HashSet<Sensor>, beacons: &HashSet<Pos2<i64>>, row: i64) -> i64 {
|
||||
fn count_coverage_at(sensors: &HashSet<Sensor>, beacons: &HashSet<Pos<i64>>, row: i64) -> i64 {
|
||||
let ranges = sensors
|
||||
.iter()
|
||||
.filter_map(|sensor| sensor.range_at(row))
|
||||
|
|
@ -117,7 +117,7 @@ impl From<Err<Error<&str>>> for SensorError {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct Line {
|
||||
start: Pos2<i64>,
|
||||
start: Pos<i64>,
|
||||
is_up: bool,
|
||||
steps: i64,
|
||||
}
|
||||
|
|
@ -139,16 +139,16 @@ impl Line {
|
|||
if one.pos.y() < two.pos.y() {
|
||||
is_up = true;
|
||||
if one.pos.y() + one.radius <= two.pos.y() {
|
||||
start = Pos2::new(one.pos.x(), one.pos.y() + one.radius + 1);
|
||||
start = Pos::new(one.pos.x(), one.pos.y() + one.radius + 1);
|
||||
} else {
|
||||
start = Pos2::new(two.pos.x() - two.radius - 1, two.pos.y());
|
||||
start = Pos::new(two.pos.x() - two.radius - 1, two.pos.y());
|
||||
}
|
||||
} else {
|
||||
is_up = false;
|
||||
if one.pos.y() - one.radius >= two.pos.y() {
|
||||
start = Pos2::new(one.pos.x(), one.pos.y() - one.radius - 1);
|
||||
start = Pos::new(one.pos.x(), one.pos.y() - one.radius - 1);
|
||||
} else {
|
||||
start = Pos2::new(two.pos.x() - two.radius - 1, two.pos.y());
|
||||
start = Pos::new(two.pos.x() - two.radius - 1, two.pos.y());
|
||||
}
|
||||
}
|
||||
let steps = two.pos.x().min(one.pos.x() + one.radius) - start.x();
|
||||
|
|
@ -160,7 +160,7 @@ impl Line {
|
|||
})
|
||||
}
|
||||
|
||||
fn cross(&self, other: &Line) -> Option<Pos2<i64>> {
|
||||
fn cross(&self, other: &Line) -> Option<Pos<i64>> {
|
||||
if self.is_up == other.is_up {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ impl Line {
|
|||
return None;
|
||||
}
|
||||
|
||||
let pos = top_down.start + Pos2::splat(r);
|
||||
let pos = top_down.start + Pos::splat(r);
|
||||
Some(pos)
|
||||
}
|
||||
}
|
||||
|
|
@ -227,7 +227,7 @@ impl Range {
|
|||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
||||
struct Sensor {
|
||||
pos: Pos2<i64>,
|
||||
pos: Pos<i64>,
|
||||
radius: i64,
|
||||
}
|
||||
|
||||
|
|
@ -239,21 +239,21 @@ impl Sensor {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_pos(input: &str) -> IResult<&str, Pos2<i64>> {
|
||||
fn parse_pos(input: &str) -> IResult<&str, Pos<i64>> {
|
||||
let (input, (x, y)) = separated_pair(
|
||||
Sensor::component("x"),
|
||||
trim0(char(',')),
|
||||
Sensor::component("y"),
|
||||
)(input)?;
|
||||
Ok((input, Pos2::new(x, y)))
|
||||
Ok((input, Pos::new(x, y)))
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos2<i64>)> {
|
||||
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos<i64>)> {
|
||||
let input = ignore(tag("Sensor at"))(input)?;
|
||||
let (input, pos) = trim0(Sensor::parse_pos)(input)?;
|
||||
let input = ignore(tag(": closest beacon is at"))(input)?;
|
||||
let (input, beacon) = trim0(Sensor::parse_pos)(input)?;
|
||||
let radius = pos.taxicab_between(beacon);
|
||||
let radius = pos.taxicab(&beacon);
|
||||
Ok((input, (Sensor { pos, radius }, beacon)))
|
||||
}
|
||||
|
||||
|
|
@ -268,12 +268,12 @@ impl Sensor {
|
|||
}
|
||||
|
||||
pub fn border_distance(&self, other: &Sensor) -> i64 {
|
||||
let distance = self.pos.taxicab_between(other.pos);
|
||||
let distance = self.pos.taxicab(&other.pos);
|
||||
distance - self.radius - other.radius
|
||||
}
|
||||
|
||||
pub fn contains(&self, pos: Pos2<i64>) -> bool {
|
||||
self.pos.taxicab_between(pos) <= self.radius
|
||||
pub fn contains(&self, pos: &Pos<i64>) -> bool {
|
||||
self.pos.taxicab(pos) <= self.radius
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,10 +287,10 @@ mod test {
|
|||
let input = "Sensor at x=2, y=18: closest beacon is at x=-2, y=15";
|
||||
let expected = (
|
||||
Sensor {
|
||||
pos: Pos2::new(2, 18),
|
||||
pos: Pos::new(2, 18),
|
||||
radius: 7,
|
||||
},
|
||||
Pos2::new(-2, 15),
|
||||
Pos::new(-2, 15),
|
||||
);
|
||||
let result = extract_result(Sensor::parse)(input)?;
|
||||
assert_eq!(result, expected);
|
||||
|
|
@ -301,7 +301,7 @@ mod test {
|
|||
#[test]
|
||||
fn test_width() {
|
||||
let sensor = Sensor {
|
||||
pos: Pos2::new(8, 7),
|
||||
pos: Pos::new(8, 7),
|
||||
radius: 9,
|
||||
};
|
||||
assert_eq!(sensor.range_at(17), None);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{
|
||||
name::Name,
|
||||
parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1},
|
||||
};
|
||||
use crate::common::parser::{eol_terminated, extract_result, ignore, trim0, trim1, trim_left1};
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
|
|
@ -98,7 +95,6 @@ impl Mul<Flow> for Time {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
struct Flow(i64);
|
||||
|
||||
impl Deref for Flow {
|
||||
type Target = i64;
|
||||
|
||||
|
|
@ -212,7 +208,7 @@ impl<'a> TryFrom<&'a str> for RawValve<'a> {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct Valve {
|
||||
id: Name,
|
||||
id: String,
|
||||
idx: Index,
|
||||
flow: Flow,
|
||||
distances: Vec<Time>,
|
||||
|
|
@ -249,7 +245,7 @@ impl Valve {
|
|||
distances.sort_unstable_by_key(|(id, _)| *id);
|
||||
|
||||
Ok(Valve {
|
||||
id: valve.id.into(),
|
||||
id: valve.id.to_owned(),
|
||||
idx,
|
||||
flow: valve.flow,
|
||||
distances: distances
|
||||
|
|
@ -261,7 +257,7 @@ impl Valve {
|
|||
|
||||
fn zero_flow(valve: &RawValve, idx: Index) -> Valve {
|
||||
Valve {
|
||||
id: valve.id.into(),
|
||||
id: valve.id.to_owned(),
|
||||
idx,
|
||||
flow: valve.flow,
|
||||
distances: vec![],
|
||||
|
|
@ -313,12 +309,11 @@ impl ValveSystem {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn get_valve_by_id<N: Into<Name>>(&self, id: N) -> Result<&Valve, ValveError> {
|
||||
let id = id.into();
|
||||
fn get_valve_by_id(&self, id: &str) -> Result<&Valve, ValveError> {
|
||||
self.valves
|
||||
.iter()
|
||||
.find(|valve| valve.id == id)
|
||||
.ok_or(ValveError::ValveNotFound(id.to_string()))
|
||||
.ok_or(ValveError::ValveNotFound(id.to_owned()))
|
||||
}
|
||||
|
||||
fn get_valve(&self, idx: Index) -> &Valve {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::{read_string, split_lines};
|
||||
use itertools::Itertools;
|
||||
use std::{cell::Cell, marker::PhantomData, ops::Index};
|
||||
|
||||
use crate::common::file::{read_string, split_lines};
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 17;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::file::split_lines;
|
||||
use itertools::Itertools;
|
||||
use std::{collections::HashSet, num::ParseIntError};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::common::file::split_lines;
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
|
||||
const DAY_NUMBER: usize = 18;
|
||||
|
||||
pub struct Day;
|
||||
|
|
@ -171,7 +173,7 @@ impl Blob {
|
|||
}
|
||||
|
||||
pub fn count_outside(&self) -> i64 {
|
||||
let Some(mut extent) = self.extent() else {
|
||||
let Some(mut extent)= self.extent() else {
|
||||
return 0;
|
||||
};
|
||||
let mut sides = 0;
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use nom::{error::Error, Err, IResult, Parser};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = todo!();
|
||||
|
|
@ -22,14 +21,8 @@ impl DayTrait for Day {
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
enum DayError {
|
||||
#[error("Not a valid description: {0}")]
|
||||
ParsingError(String),
|
||||
}
|
||||
|
||||
impl From<Err<Error<&str>>> for DayError {
|
||||
fn from(error: Err<Error<&str>>) -> Self {
|
||||
DayError::ParsingError(error.to_string())
|
||||
}
|
||||
#[error("Dummy")]
|
||||
Dummy,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -18,11 +18,6 @@ mod day17;
|
|||
mod day18;
|
||||
mod day19;
|
||||
mod day20;
|
||||
mod day21;
|
||||
mod day22;
|
||||
mod day23;
|
||||
mod day24;
|
||||
mod day25;
|
||||
mod template;
|
||||
|
||||
pub use template::DayTrait;
|
||||
|
|
@ -32,7 +27,7 @@ pub mod day_provider {
|
|||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
const MAX_DAY: usize = 25;
|
||||
const MAX_DAY: usize = 20;
|
||||
|
||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||
match day_num {
|
||||
|
|
@ -56,11 +51,6 @@ pub mod day_provider {
|
|||
18 => Ok(Box::new(day18::Day)),
|
||||
19 => Ok(Box::new(day19::Day)),
|
||||
20 => Ok(Box::new(day20::Day)),
|
||||
21 => Ok(Box::new(day21::Day)),
|
||||
22 => Ok(Box::new(day22::Day)),
|
||||
23 => Ok(Box::new(day23::Day)),
|
||||
24 => Ok(Box::new(day24::Day)),
|
||||
25 => Ok(Box::new(day25::Day)),
|
||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(get_many_mut)]
|
||||
|
||||
mod common;
|
||||
mod days;
|
||||
mod macros;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue