├── .gitignore
├── README.md
├── build.sbt
├── project
├── build.properties
└── plugins.sbt
└── src
├── main
├── resources
│ ├── 2015
│ │ ├── Day01.txt
│ │ ├── Day02.txt
│ │ ├── Day03.txt
│ │ ├── Day04.txt
│ │ ├── Day05.txt
│ │ ├── Day06.txt
│ │ ├── Day07.txt
│ │ ├── Day08.txt
│ │ ├── Day09.txt
│ │ ├── Day10.txt
│ │ ├── Day11.txt
│ │ ├── Day12.txt
│ │ ├── Day13.txt
│ │ ├── Day14.txt
│ │ ├── Day15.txt
│ │ ├── Day16.txt
│ │ ├── Day17.txt
│ │ ├── Day18.txt
│ │ ├── Day19.txt
│ │ ├── Day20.txt
│ │ ├── Day21.txt
│ │ ├── Day22.txt
│ │ ├── Day23.txt
│ │ ├── Day24.txt
│ │ └── Day25.txt
│ ├── 2017
│ │ ├── Day01.txt
│ │ ├── Day02.txt
│ │ ├── Day03.txt
│ │ ├── Day04.txt
│ │ ├── Day05.txt
│ │ ├── Day06.txt
│ │ ├── Day07.txt
│ │ ├── Day08.txt
│ │ ├── Day09.txt
│ │ ├── Day10.txt
│ │ ├── Day11.txt
│ │ ├── Day12.txt
│ │ ├── Day13.txt
│ │ ├── Day14.txt
│ │ ├── Day15.txt
│ │ ├── Day16.txt
│ │ ├── Day17.txt
│ │ ├── Day18.txt
│ │ ├── Day19.txt
│ │ ├── Day20.txt
│ │ ├── Day21.txt
│ │ ├── Day22.txt
│ │ ├── Day23.txt
│ │ ├── Day24.txt
│ │ └── Day25.txt
│ ├── 2018
│ │ ├── Day01.txt
│ │ ├── Day02.txt
│ │ ├── Day03.txt
│ │ ├── Day04.txt
│ │ ├── Day05.txt
│ │ ├── Day06.txt
│ │ ├── Day07.txt
│ │ ├── Day08.txt
│ │ ├── Day09.txt
│ │ ├── Day10.txt
│ │ ├── Day11.txt
│ │ ├── Day12.txt
│ │ ├── Day13.txt
│ │ ├── Day14.txt
│ │ ├── Day15.txt
│ │ ├── Day16.txt
│ │ ├── Day17.txt
│ │ ├── Day18.txt
│ │ ├── Day19.txt
│ │ └── Day24.txt
│ ├── 2019
│ │ ├── Day01.txt
│ │ ├── Day02.txt
│ │ ├── Day03.txt
│ │ ├── Day04.txt
│ │ ├── Day05.txt
│ │ └── Day06.txt
│ ├── 2020
│ │ ├── Day01.txt
│ │ ├── Day02.txt
│ │ └── Day07.txt
│ ├── 2021
│ │ ├── Day01.txt
│ │ ├── Day02.txt
│ │ ├── Day03.txt
│ │ ├── Day04.txt
│ │ ├── Day05.txt
│ │ ├── Day06.txt
│ │ ├── Day07.txt
│ │ ├── Day08.txt
│ │ ├── Day09.txt
│ │ ├── Day10.txt
│ │ ├── Day11.txt
│ │ ├── Day12.txt
│ │ ├── Day13.txt
│ │ └── Day14.txt
│ └── 2023
│ │ ├── Day01.txt
│ │ ├── Day02.txt
│ │ ├── Day03.txt
│ │ ├── Day04.txt
│ │ ├── Day05.txt
│ │ ├── Day06.txt
│ │ ├── Day07.txt
│ │ ├── Day08.txt
│ │ ├── Day09.txt
│ │ ├── Day10.txt
│ │ ├── Day11.txt
│ │ ├── Day12.txt
│ │ ├── Day13.txt
│ │ ├── Day14.txt
│ │ ├── Day15.txt
│ │ ├── Day16.txt
│ │ ├── Day17.txt
│ │ ├── Day18.txt
│ │ ├── Day19.txt
│ │ └── Day20.txt
└── scala
│ └── com
│ └── lmat
│ ├── adventofcode
│ ├── Puzzle.scala
│ ├── PuzzleRunner.scala
│ ├── year2015
│ │ ├── Day01.scala
│ │ ├── Day02.scala
│ │ ├── Day03.scala
│ │ ├── Day04.scala
│ │ ├── Day05.scala
│ │ ├── Day06.scala
│ │ ├── Day07.scala
│ │ ├── Day08.scala
│ │ ├── Day09.scala
│ │ ├── Day10.scala
│ │ ├── Day11.scala
│ │ ├── Day12.scala
│ │ ├── Day13.scala
│ │ ├── Day14.scala
│ │ ├── Day15.scala
│ │ ├── Day16.scala
│ │ ├── Day17.scala
│ │ ├── Day18.scala
│ │ ├── Day19.scala
│ │ ├── Day20.scala
│ │ ├── Day21.scala
│ │ ├── Day22.scala
│ │ ├── Day23.scala
│ │ ├── Day24.scala
│ │ └── Day25.scala
│ ├── year2017
│ │ ├── Day01.scala
│ │ ├── Day02.scala
│ │ ├── Day03.scala
│ │ ├── Day04.scala
│ │ ├── Day05.scala
│ │ ├── Day06.scala
│ │ ├── Day07.scala
│ │ ├── Day08.scala
│ │ ├── Day09.scala
│ │ ├── Day10.scala
│ │ ├── Day11.scala
│ │ ├── Day12.scala
│ │ ├── Day13.scala
│ │ ├── Day14.scala
│ │ ├── Day15.scala
│ │ ├── Day16.scala
│ │ ├── Day17.scala
│ │ ├── Day18.scala
│ │ ├── Day19.scala
│ │ ├── Day20.scala
│ │ ├── Day21.scala
│ │ ├── Day22.scala
│ │ ├── Day23.scala
│ │ ├── Day24.scala
│ │ └── Day25.scala
│ ├── year2018
│ │ ├── Day01.scala
│ │ ├── Day02.scala
│ │ ├── Day03.scala
│ │ ├── Day04.scala
│ │ ├── Day05.scala
│ │ ├── Day06.scala
│ │ ├── Day07.scala
│ │ ├── Day08.scala
│ │ ├── Day09.scala
│ │ ├── Day10.scala
│ │ ├── Day11.scala
│ │ ├── Day12.scala
│ │ ├── Day13.scala
│ │ ├── Day14.scala
│ │ ├── Day15.scala
│ │ ├── Day16.scala
│ │ ├── Day17.scala
│ │ ├── Day18.scala
│ │ ├── Day19.scala
│ │ └── Day24.scala
│ ├── year2019
│ │ ├── Day01.scala
│ │ ├── Day02.scala
│ │ ├── Day03.scala
│ │ ├── Day04.scala
│ │ ├── Day05.scala
│ │ └── Day06.scala
│ ├── year2020
│ │ ├── Day01.scala
│ │ ├── Day02.scala
│ │ └── Day07.scala
│ ├── year2021
│ │ ├── Day01.scala
│ │ ├── Day02.scala
│ │ ├── Day03.scala
│ │ ├── Day04.scala
│ │ ├── Day05.scala
│ │ ├── Day06.scala
│ │ ├── Day07.scala
│ │ ├── Day08.scala
│ │ ├── Day09.scala
│ │ ├── Day10.scala
│ │ ├── Day11.scala
│ │ ├── Day12.scala
│ │ ├── Day13.scala
│ │ └── Day14.scala
│ └── year2023
│ │ ├── Day01.scala
│ │ ├── Day02.scala
│ │ ├── Day03.scala
│ │ ├── Day04.scala
│ │ ├── Day05.scala
│ │ ├── Day06.scala
│ │ ├── Day07.scala
│ │ ├── Day08.scala
│ │ ├── Day09.scala
│ │ ├── Day10.scala
│ │ ├── Day11.scala
│ │ ├── Day12.scala
│ │ ├── Day13.scala
│ │ ├── Day14.scala
│ │ ├── Day15.scala
│ │ ├── Day16.scala
│ │ ├── Day17.scala
│ │ ├── Day18.scala
│ │ ├── Day19.scala
│ │ └── Day20.scala
│ └── util
│ ├── Files.scala
│ ├── Json.scala
│ ├── Logic.scala
│ ├── Maths.scala
│ ├── Matrix.scala
│ ├── Search.scala
│ ├── Sequences.scala
│ ├── Strings.scala
│ └── Traverse.scala
└── test
└── scala
└── com
└── lmat
├── adventofcode
├── year2015
│ ├── Day01Test.scala
│ ├── Day02Test.scala
│ ├── Day03Test.scala
│ ├── Day04Test.scala
│ ├── Day05Test.scala
│ ├── Day06Test.scala
│ ├── Day07Test.scala
│ ├── Day08Test.scala
│ ├── Day09Test.scala
│ ├── Day10Test.scala
│ ├── Day11Test.scala
│ ├── Day12Test.scala
│ ├── Day13Test.scala
│ ├── Day14Test.scala
│ ├── Day15Test.scala
│ ├── Day16Test.scala
│ ├── Day17Test.scala
│ ├── Day18Test.scala
│ ├── Day19Test.scala
│ ├── Day20Test.scala
│ ├── Day21Test.scala
│ ├── Day22Test.scala
│ ├── Day23Test.scala
│ ├── Day24Test.scala
│ ├── Day25Test.scala
│ └── Year2015Test.scala
├── year2017
│ ├── Day01Test.scala
│ ├── Day02Test.scala
│ ├── Day03Test.scala
│ ├── Day04Test.scala
│ ├── Day05Test.scala
│ ├── Day06Test.scala
│ ├── Day07Test.scala
│ ├── Day08Test.scala
│ ├── Day09Test.scala
│ ├── Day10Test.scala
│ ├── Day11Test.scala
│ ├── Day12Test.scala
│ ├── Day13Test.scala
│ ├── Day14Test.scala
│ ├── Day15Test.scala
│ ├── Day16Test.scala
│ ├── Day17Test.scala
│ ├── Day18Test.scala
│ ├── Day19Test.scala
│ ├── Day20Test.scala
│ ├── Day21Test.scala
│ ├── Day22Test.scala
│ ├── Day23Test.scala
│ ├── Day24Test.scala
│ ├── Day25Test.scala
│ └── Year2017Test.scala
├── year2018
│ ├── Day01Test.scala
│ ├── Day02Test.scala
│ ├── Day03Test.scala
│ ├── Day04Test.scala
│ ├── Day05Test.scala
│ ├── Day06Test.scala
│ ├── Day07Test.scala
│ ├── Day08Test.scala
│ ├── Day09Test.scala
│ ├── Day10Test.scala
│ ├── Day11Test.scala
│ ├── Day12Test.scala
│ ├── Day13Test.scala
│ ├── Day14Test.scala
│ ├── Day15Test.scala
│ ├── Day16Test.scala
│ ├── Day17Test.scala
│ ├── Day18Test.scala
│ ├── Day19Test.scala
│ ├── Day24Test.scala
│ └── Year2018Test.scala
├── year2019
│ ├── Day01Test.scala
│ ├── Day02Test.scala
│ ├── Day03Test.scala
│ ├── Day04Test.scala
│ ├── Day05Test.scala
│ ├── Day06Test.scala
│ └── Year2019Test.scala
├── year2020
│ ├── Day01Test.scala
│ ├── Day02Test.scala
│ ├── Day07Test.scala
│ └── Year2020Test.scala
├── year2021
│ ├── Day01Test.scala
│ ├── Day02Test.scala
│ ├── Day03Test.scala
│ ├── Day04Test.scala
│ ├── Day05Test.scala
│ ├── Day06Test.scala
│ ├── Day07Test.scala
│ ├── Day08Test.scala
│ ├── Day09Test.scala
│ ├── Day10Test.scala
│ ├── Day11Test.scala
│ ├── Day12Test.scala
│ ├── Day13Test.scala
│ ├── Day14Test.scala
│ └── Year2021Test.scala
└── year2023
│ ├── Day01Test.scala
│ ├── Day02Test.scala
│ ├── Day03Test.scala
│ ├── Day04Test.scala
│ ├── Day05Test.scala
│ ├── Day06Test.scala
│ ├── Day07Test.scala
│ ├── Day08Test.scala
│ ├── Day09Test.scala
│ ├── Day10Test.scala
│ ├── Day11Test.scala
│ ├── Day12Test.scala
│ ├── Day13Test.scala
│ ├── Day14Test.scala
│ ├── Day15Test.scala
│ ├── Day16Test.scala
│ ├── Day17Test.scala
│ ├── Day18Test.scala
│ ├── Day19Test.scala
│ ├── Day20Test.scala
│ └── Year2023Test.scala
└── util
├── JsonTest.scala
├── MathsTest.scala
├── MatrixTest.scala
├── SequencesTest.scala
├── StringsTest.scala
└── TraverseTest.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | .idea/
3 | .bsp/
4 | *.class
5 | *.iml
6 | *.ipr
7 | *.iws
8 | *.attach*
9 | out
10 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | lazy val root = (project in file("."))
2 | .settings(name := "Advent of Code Scala")
3 | .settings(moduleName := "advent-of-code-scala")
4 | .settings(version := "1.0")
5 | .settings(scalaVersion := "2.13.12")
6 | .settings(libraryDependencies ++= dependencies)
7 |
8 | lazy val dependencies = Seq(
9 | "org.scala-lang" % "scala-reflect" % "2.13.12",
10 | "org.scalatest" %% "scalatest" % "3.2.17" % "test"
11 | )
12 |
13 | scalacOptions ++= Seq("-deprecation", "-feature")
14 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 1.9.7
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4")
--------------------------------------------------------------------------------
/src/main/resources/2015/Day04.txt:
--------------------------------------------------------------------------------
1 | ckczppom
--------------------------------------------------------------------------------
/src/main/resources/2015/Day09.txt:
--------------------------------------------------------------------------------
1 | AlphaCentauri to Snowdin = 66
2 | AlphaCentauri to Tambi = 28
3 | AlphaCentauri to Faerun = 60
4 | AlphaCentauri to Norrath = 34
5 | AlphaCentauri to Straylight = 34
6 | AlphaCentauri to Tristram = 3
7 | AlphaCentauri to Arbre = 108
8 |
9 | Snowdin to Tambi = 22
10 | Snowdin to Faerun = 12
11 | Snowdin to Norrath = 91
12 | Snowdin to Straylight = 121
13 | Snowdin to Tristram = 111
14 | Snowdin to Arbre = 71
15 | Tambi to Faerun = 39
16 | Tambi to Norrath = 113
17 | Tambi to Straylight = 130
18 | Tambi to Tristram = 35
19 | Tambi to Arbre = 40
20 | Faerun to Norrath = 63
21 | Faerun to Straylight = 21
22 | Faerun to Tristram = 57
23 | Faerun to Arbre = 83
24 | Norrath to Straylight = 9
25 | Norrath to Tristram = 50
26 | Norrath to Arbre = 60
27 | Straylight to Tristram = 27
28 | Straylight to Arbre = 81
29 | Tristram to Arbre = 90
--------------------------------------------------------------------------------
/src/main/resources/2015/Day10.txt:
--------------------------------------------------------------------------------
1 | 1321131112
--------------------------------------------------------------------------------
/src/main/resources/2015/Day11.txt:
--------------------------------------------------------------------------------
1 | hxbxwxba
--------------------------------------------------------------------------------
/src/main/resources/2015/Day14.txt:
--------------------------------------------------------------------------------
1 | Dancer can fly 27 km/s for 5 seconds, but then must rest for 132 seconds.
2 | Cupid can fly 22 km/s for 2 seconds, but then must rest for 41 seconds.
3 | Rudolph can fly 11 km/s for 5 seconds, but then must rest for 48 seconds.
4 | Donner can fly 28 km/s for 5 seconds, but then must rest for 134 seconds.
5 | Dasher can fly 4 km/s for 16 seconds, but then must rest for 55 seconds.
6 | Blitzen can fly 14 km/s for 3 seconds, but then must rest for 38 seconds.
7 | Prancer can fly 3 km/s for 21 seconds, but then must rest for 40 seconds.
8 | Comet can fly 18 km/s for 6 seconds, but then must rest for 103 seconds.
9 | Vixen can fly 18 km/s for 5 seconds, but then must rest for 84 seconds.
--------------------------------------------------------------------------------
/src/main/resources/2015/Day15.txt:
--------------------------------------------------------------------------------
1 | Frosting: capacity 4, durability -2, flavor 0, texture 0, calories 5
2 | Candy: capacity 0, durability 5, flavor -1, texture 0, calories 8
3 | Butterscotch: capacity -1, durability 0, flavor 5, texture 0, calories 6
4 | Sugar: capacity 0, durability 0, flavor -2, texture 2, calories 1
--------------------------------------------------------------------------------
/src/main/resources/2015/Day17.txt:
--------------------------------------------------------------------------------
1 | 43
2 | 3
3 | 4
4 | 10
5 | 21
6 | 44
7 | 4
8 | 6
9 | 47
10 | 41
11 | 34
12 | 17
13 | 17
14 | 44
15 | 36
16 | 31
17 | 46
18 | 9
19 | 27
20 | 38
--------------------------------------------------------------------------------
/src/main/resources/2015/Day19.txt:
--------------------------------------------------------------------------------
1 | Al => ThF
2 | Al => ThRnFAr
3 | B => BCa
4 | B => TiB
5 | B => TiRnFAr
6 | Ca => CaCa
7 | Ca => PB
8 | Ca => PRnFAr
9 | Ca => SiRnFYFAr
10 | Ca => SiRnMgAr
11 | Ca => SiTh
12 | F => CaF
13 | F => PMg
14 | F => SiAl
15 | H => CRnAlAr
16 | H => CRnFYFYFAr
17 | H => CRnFYMgAr
18 | H => CRnMgYFAr
19 | H => HCa
20 | H => NRnFYFAr
21 | H => NRnMgAr
22 | H => NTh
23 | H => OB
24 | H => ORnFAr
25 | Mg => BF
26 | Mg => TiMg
27 | N => CRnFAr
28 | N => HSi
29 | O => CRnFYFAr
30 | O => CRnMgAr
31 | O => HP
32 | O => NRnFAr
33 | O => OTi
34 | P => CaP
35 | P => PTi
36 | P => SiRnFAr
37 | Si => CaSi
38 | Th => ThCa
39 | Ti => BP
40 | Ti => TiTi
41 | e => HF
42 | e => NAl
43 | e => OMg
44 |
45 | CRnSiRnCaPTiMgYCaPTiRnFArSiThFArCaSiThSiThPBCaCaSiRnSiRnTiTiMgArPBCaPMgYPTiRnFArFArCaSiRnBPMgArPRnCaPTiRnFArCaSiThCaCaFArPBCaCaPTiTiRnFArCaSiRnSiAlYSiThRnFArArCaSiRnBFArCaCaSiRnSiThCaCaCaFYCaPTiBCaSiThCaSiThPMgArSiRnCaPBFYCaCaFArCaCaCaCaSiThCaSiRnPRnFArPBSiThPRnFArSiRnMgArCaFYFArCaSiRnSiAlArTiTiTiTiTiTiTiRnPMgArPTiTiTiBSiRnSiAlArTiTiRnPMgArCaFYBPBPTiRnSiRnMgArSiThCaFArCaSiThFArPRnFArCaSiRnTiBSiThSiRnSiAlYCaFArPRnFArSiThCaFArCaCaSiThCaCaCaSiRnPRnCaFArFYPMgArCaPBCaPBSiRnFYPBCaFArCaSiAl
--------------------------------------------------------------------------------
/src/main/resources/2015/Day20.txt:
--------------------------------------------------------------------------------
1 | 36000000
--------------------------------------------------------------------------------
/src/main/resources/2015/Day21.txt:
--------------------------------------------------------------------------------
1 | Hit Points: 100
2 | Damage: 8
3 | Armor: 2
--------------------------------------------------------------------------------
/src/main/resources/2015/Day22.txt:
--------------------------------------------------------------------------------
1 | Hit Points: 55
2 | Damage: 8
--------------------------------------------------------------------------------
/src/main/resources/2015/Day23.txt:
--------------------------------------------------------------------------------
1 | jio a, +19
2 | inc a
3 | tpl a
4 | inc a
5 | tpl a
6 | inc a
7 | tpl a
8 | tpl a
9 | inc a
10 | inc a
11 | tpl a
12 | tpl a
13 | inc a
14 | inc a
15 | tpl a
16 | inc a
17 | inc a
18 | tpl a
19 | jmp +23
20 | tpl a
21 | tpl a
22 | inc a
23 | inc a
24 | tpl a
25 | inc a
26 | inc a
27 | tpl a
28 | inc a
29 | tpl a
30 | inc a
31 | tpl a
32 | inc a
33 | tpl a
34 | inc a
35 | inc a
36 | tpl a
37 | inc a
38 | inc a
39 | tpl a
40 | tpl a
41 | inc a
42 | jio a, +8
43 | inc b
44 | jie a, +4
45 | tpl a
46 | inc a
47 | jmp +2
48 | hlf a
49 | jmp -7
--------------------------------------------------------------------------------
/src/main/resources/2015/Day24.txt:
--------------------------------------------------------------------------------
1 | 1
2 | 2
3 | 3
4 | 7
5 | 11
6 | 13
7 | 17
8 | 19
9 | 23
10 | 31
11 | 37
12 | 41
13 | 43
14 | 47
15 | 53
16 | 59
17 | 61
18 | 67
19 | 71
20 | 73
21 | 79
22 | 83
23 | 89
24 | 97
25 | 101
26 | 103
27 | 107
28 | 109
29 | 113
--------------------------------------------------------------------------------
/src/main/resources/2015/Day25.txt:
--------------------------------------------------------------------------------
1 | To continue, please consult the code grid in the manual. Enter the code at row 2978, column 3083.
--------------------------------------------------------------------------------
/src/main/resources/2017/Day01.txt:
--------------------------------------------------------------------------------
1 | 8231753674683997878179259195565332579493378483264978184143341284379682788518559178822225126625428318115396632681141871952894291898364781898929292614792884883249356728741993224889167928232261325123447569829932951268292953928766755779761837993812528527484487298117739869189415599461746944992651752768158611996715467871381527675219481185217357632445748912726487669881876129192932995282777848496561259839781188719233951619188388532698519298142112853776942545211859134185231768952888462471642851588368445761489225786919778983848113833773768236969923939838755997989537648222217996381757542964844337285428654375499359997792679256881378967852376848812795761118139288152799921176874256377615952758268844139579622754965461884862647423491918913628848748756595463191585555385849335742224855473769411212376446591654846168189278959857681336724221434846946124915271196433144335482787432683848594487648477532498952572515118864475621828118274911298396748213136426357769991314661642612786847135485969889237193822718111269561741563479116832364485724716242176288642371849569664594194674763319687735723517614962575592111286177553435651952853878775431234327919595595658641534765455489561934548474291254387229751472883423413196845162752716925199866591883313638846474321161569892518574346226751366315311145777448781862222126923449311838564685882695889397531413937666673233451216968414288135984394249684886554812761191289485457945866524228415191549168557957633386991931186773843869999284468773866221976873998168818944399661463963658784821796272987155278195355579386768156718813624559264574836134419725187881514665834441359644955768658663278765363789664721736533517774292478192143934318399418188298753351815388561359528533778996296279366394386455544446922653976725113889842749182361253582433319351193862788433113852782596161148992233558144692913791714859516653421917841295749163469751479835492713392861519993791967927773114713888458982796514977717987598165486967786989991998142488631168697963816156374216224386193941566358543266646516247854435356941566492841213424915682394928959116411457967897614457497279472661229548612777155998358618945222326558176486944695689777438164612198225816646583996426313832539918
--------------------------------------------------------------------------------
/src/main/resources/2017/Day02.txt:
--------------------------------------------------------------------------------
1 | 1136 1129 184 452 788 1215 355 1109 224 1358 1278 176 1302 186 128 1148
2 | 242 53 252 62 40 55 265 283 38 157 259 226 322 48 324 299
3 | 2330 448 268 2703 1695 2010 3930 3923 179 3607 217 3632 1252 231 286 3689
4 | 89 92 903 156 924 364 80 992 599 998 751 827 110 969 979 734
5 | 100 304 797 81 249 1050 90 127 675 1038 154 715 79 1116 723 990
6 | 1377 353 3635 99 118 1030 3186 3385 1921 2821 492 3082 2295 139 125 2819
7 | 3102 213 2462 116 701 2985 265 165 248 680 3147 1362 1026 1447 106 2769
8 | 5294 295 6266 3966 2549 701 2581 6418 5617 292 5835 209 2109 3211 241 5753
9 | 158 955 995 51 89 875 38 793 969 63 440 202 245 58 965 74
10 | 62 47 1268 553 45 60 650 1247 1140 776 1286 200 604 399 42 572
11 | 267 395 171 261 79 66 428 371 257 284 65 25 374 70 389 51
12 | 3162 3236 1598 4680 2258 563 1389 3313 501 230 195 4107 224 225 4242 4581
13 | 807 918 51 1055 732 518 826 806 58 394 632 36 53 119 667 60
14 | 839 253 1680 108 349 1603 1724 172 140 167 181 38 1758 1577 748 1011
15 | 1165 1251 702 282 1178 834 211 1298 382 1339 67 914 1273 76 81 71
16 | 6151 5857 4865 437 6210 237 37 410 544 214 233 6532 2114 207 5643 6852
--------------------------------------------------------------------------------
/src/main/resources/2017/Day03.txt:
--------------------------------------------------------------------------------
1 | 347991
--------------------------------------------------------------------------------
/src/main/resources/2017/Day06.txt:
--------------------------------------------------------------------------------
1 | 4 10 4 1 8 4 9 14 5 1 14 15 0 15 3 5
--------------------------------------------------------------------------------
/src/main/resources/2017/Day10.txt:
--------------------------------------------------------------------------------
1 | 106,16,254,226,55,2,1,166,177,247,93,0,255,228,60,36
--------------------------------------------------------------------------------
/src/main/resources/2017/Day13.txt:
--------------------------------------------------------------------------------
1 | 0: 5
2 | 1: 2
3 | 2: 3
4 | 4: 4
5 | 6: 8
6 | 8: 4
7 | 10: 6
8 | 12: 6
9 | 14: 8
10 | 16: 6
11 | 18: 6
12 | 20: 12
13 | 22: 14
14 | 24: 8
15 | 26: 8
16 | 28: 9
17 | 30: 8
18 | 32: 8
19 | 34: 12
20 | 36: 10
21 | 38: 12
22 | 40: 12
23 | 44: 14
24 | 46: 12
25 | 48: 10
26 | 50: 12
27 | 52: 12
28 | 54: 12
29 | 56: 14
30 | 58: 12
31 | 60: 14
32 | 62: 14
33 | 64: 14
34 | 66: 14
35 | 68: 17
36 | 70: 12
37 | 72: 14
38 | 76: 14
39 | 78: 14
40 | 80: 14
41 | 82: 18
42 | 84: 14
43 | 88: 20
--------------------------------------------------------------------------------
/src/main/resources/2017/Day14.txt:
--------------------------------------------------------------------------------
1 | xlqgujun
--------------------------------------------------------------------------------
/src/main/resources/2017/Day15.txt:
--------------------------------------------------------------------------------
1 | Generator A starts with 883
2 | Generator B starts with 879
--------------------------------------------------------------------------------
/src/main/resources/2017/Day17.txt:
--------------------------------------------------------------------------------
1 | 370
--------------------------------------------------------------------------------
/src/main/resources/2017/Day18.txt:
--------------------------------------------------------------------------------
1 | set i 31
2 | set a 1
3 | mul p 17
4 | jgz p p
5 | mul a 2
6 | add i -1
7 | jgz i -2
8 | add a -1
9 | set i 127
10 | set p 622
11 | mul p 8505
12 | mod p a
13 | mul p 129749
14 | add p 12345
15 | mod p a
16 | set b p
17 | mod b 10000
18 | snd b
19 | add i -1
20 | jgz i -9
21 | jgz a 3
22 | rcv b
23 | jgz b -1
24 | set f 0
25 | set i 126
26 | rcv a
27 | rcv b
28 | set p a
29 | mul p -1
30 | add p b
31 | jgz p 4
32 | snd a
33 | set a b
34 | jgz 1 3
35 | snd b
36 | set f 1
37 | add i -1
38 | jgz i -11
39 | snd a
40 | jgz f -16
41 | jgz a -19
--------------------------------------------------------------------------------
/src/main/resources/2017/Day22.txt:
--------------------------------------------------------------------------------
1 | #.#.#.##.#.##.###.#.###.#
2 | .#..#.....#..#######.##.#
3 | ......###..##..###..#...#
4 | ##....#.#.#....#..#..#..#
5 | #..#....#.##.#.#..#..#.#.
6 | ..##..##.##..##...#...###
7 | ..#.#....#..####.##.##...
8 | ###...#.#...#.######...#.
9 | ..#####...###..#####.#.##
10 | ...#..#......####.##..#.#
11 | #...##..#.#####...#.##...
12 | ..#.#.###.##.##....##.###
13 | ##.##...###....#######.#.
14 | #.#...#.#..#.##..##..##.#
15 | .#...###...#..#..####....
16 | ####...#...##.####..#.#..
17 | ......#.....##.#.##....##
18 | ###.......####..##.#.##..
19 | ....###.....##.##..###.#.
20 | .##..##.#.###.###..#.###.
21 | ..#..##.######.##........
22 | #..#.#..#.###....##.##..#
23 | .##.#.#...######...##.##.
24 | ##..#..#..##.#.#..#..####
25 | #######.#.######.#.....##
--------------------------------------------------------------------------------
/src/main/resources/2017/Day23.txt:
--------------------------------------------------------------------------------
1 | set b 99
2 | set c b
3 | jnz a 2
4 | jnz 1 5
5 | mul b 100
6 | sub b -100000
7 | set c b
8 | sub c -17000
9 | set f 1
10 | set d 2
11 | set e 2
12 | set g d
13 | mul g e
14 | sub g b
15 | jnz g 2
16 | set f 0
17 | sub e -1
18 | set g e
19 | sub g b
20 | jnz g -8
21 | sub d -1
22 | set g d
23 | sub g b
24 | jnz g -13
25 | jnz f 2
26 | sub h -1
27 | set g b
28 | sub g c
29 | jnz g 2
30 | jnz 1 3
31 | sub b -17
32 | jnz 1 -23
--------------------------------------------------------------------------------
/src/main/resources/2017/Day24.txt:
--------------------------------------------------------------------------------
1 | 32/31
2 | 2/2
3 | 0/43
4 | 45/15
5 | 33/24
6 | 20/20
7 | 14/42
8 | 2/35
9 | 50/27
10 | 2/17
11 | 5/45
12 | 3/14
13 | 26/1
14 | 33/38
15 | 29/6
16 | 50/32
17 | 9/48
18 | 36/34
19 | 33/50
20 | 37/35
21 | 12/12
22 | 26/13
23 | 19/4
24 | 5/5
25 | 14/46
26 | 17/29
27 | 45/43
28 | 5/0
29 | 18/18
30 | 41/22
31 | 50/3
32 | 4/4
33 | 17/1
34 | 40/7
35 | 19/0
36 | 33/7
37 | 22/48
38 | 9/14
39 | 50/43
40 | 26/29
41 | 19/33
42 | 46/31
43 | 3/16
44 | 29/46
45 | 16/0
46 | 34/17
47 | 31/7
48 | 5/27
49 | 7/4
50 | 49/49
51 | 14/21
52 | 50/9
53 | 14/44
54 | 29/29
55 | 13/38
56 | 31/11
--------------------------------------------------------------------------------
/src/main/resources/2017/Day25.txt:
--------------------------------------------------------------------------------
1 | Begin in state A.
2 | Perform a diagnostic checksum after 12459852 steps.
3 |
4 | In state A:
5 | If the current value is 0:
6 | - Write the value 1.
7 | - Move one slot to the right.
8 | - Continue with state B.
9 | If the current value is 1:
10 | - Write the value 1.
11 | - Move one slot to the left.
12 | - Continue with state E.
13 |
14 | In state B:
15 | If the current value is 0:
16 | - Write the value 1.
17 | - Move one slot to the right.
18 | - Continue with state C.
19 | If the current value is 1:
20 | - Write the value 1.
21 | - Move one slot to the right.
22 | - Continue with state F.
23 |
24 | In state C:
25 | If the current value is 0:
26 | - Write the value 1.
27 | - Move one slot to the left.
28 | - Continue with state D.
29 | If the current value is 1:
30 | - Write the value 0.
31 | - Move one slot to the right.
32 | - Continue with state B.
33 |
34 | In state D:
35 | If the current value is 0:
36 | - Write the value 1.
37 | - Move one slot to the right.
38 | - Continue with state E.
39 | If the current value is 1:
40 | - Write the value 0.
41 | - Move one slot to the left.
42 | - Continue with state C.
43 |
44 | In state E:
45 | If the current value is 0:
46 | - Write the value 1.
47 | - Move one slot to the left.
48 | - Continue with state A.
49 | If the current value is 1:
50 | - Write the value 0.
51 | - Move one slot to the right.
52 | - Continue with state D.
53 |
54 | In state F:
55 | If the current value is 0:
56 | - Write the value 1.
57 | - Move one slot to the right.
58 | - Continue with state A.
59 | If the current value is 1:
60 | - Write the value 1.
61 | - Move one slot to the right.
62 | - Continue with state C.
--------------------------------------------------------------------------------
/src/main/resources/2018/Day06.txt:
--------------------------------------------------------------------------------
1 | 267, 196
2 | 76, 184
3 | 231, 301
4 | 241, 76
5 | 84, 210
6 | 186, 243
7 | 251, 316
8 | 265, 129
9 | 142, 124
10 | 107, 134
11 | 265, 191
12 | 216, 226
13 | 67, 188
14 | 256, 211
15 | 317, 166
16 | 110, 41
17 | 347, 332
18 | 129, 91
19 | 217, 327
20 | 104, 57
21 | 332, 171
22 | 257, 287
23 | 230, 105
24 | 131, 209
25 | 110, 282
26 | 263, 146
27 | 113, 217
28 | 193, 149
29 | 280, 71
30 | 357, 160
31 | 356, 43
32 | 321, 123
33 | 272, 70
34 | 171, 49
35 | 288, 196
36 | 156, 139
37 | 268, 163
38 | 188, 141
39 | 156, 182
40 | 199, 242
41 | 330, 47
42 | 89, 292
43 | 351, 329
44 | 292, 353
45 | 290, 158
46 | 167, 116
47 | 268, 235
48 | 124, 139
49 | 116, 119
50 | 142, 259
--------------------------------------------------------------------------------
/src/main/resources/2018/Day09.txt:
--------------------------------------------------------------------------------
1 | 471 players; last marble is worth 72026 points
--------------------------------------------------------------------------------
/src/main/resources/2018/Day11.txt:
--------------------------------------------------------------------------------
1 | 1718
--------------------------------------------------------------------------------
/src/main/resources/2018/Day12.txt:
--------------------------------------------------------------------------------
1 | initial state: ##...#......##......#.####.##.#..#..####.#.######.##..#.####...##....#.#.####.####.#..#.######.##...
2 |
3 | #.... => .
4 | #..## => #
5 | ....# => .
6 | ...#. => .
7 | ...## => #
8 | #.#.# => .
9 | .#... => #
10 | ##.#. => .
11 | ..#.# => .
12 | .##.# => #
13 | ###.# => #
14 | .#.## => .
15 | ..... => .
16 | ##### => #
17 | ###.. => .
18 | ##..# => #
19 | #.### => #
20 | #.#.. => .
21 | ..### => .
22 | ..#.. => .
23 | .#..# => #
24 | .##.. => #
25 | ##... => #
26 | .#.#. => #
27 | .###. => #
28 | #..#. => .
29 | ####. => .
30 | .#### => #
31 | #.##. => #
32 | ##.## => .
33 | ..##. => .
34 | #...# => #
--------------------------------------------------------------------------------
/src/main/resources/2018/Day14.txt:
--------------------------------------------------------------------------------
1 | 939601
--------------------------------------------------------------------------------
/src/main/resources/2018/Day15.txt:
--------------------------------------------------------------------------------
1 | ################################
2 | #########....#..#####.......####
3 | ###########G......###..##..#####
4 | ###########.....#.###......#####
5 | ###############.#...#.......####
6 | ###############..#....E......###
7 | ############.##...#...G....#####
8 | ############.##.....G..E...#####
9 | ###########G.##...GG......######
10 | #..####G##..G##..G.#......######
11 | #..........#............#.######
12 | #.......#....G.......G.##..#...#
13 | #.....G.......#####...####...#.#
14 | #.....G..#...#######..#####...E#
15 | #.##.....G..#########.#######..#
16 | #........G..#########.#######E##
17 | ####........#########.##########
18 | ##.#........#########.##########
19 | ##.G....G...#########.##########
20 | ##...........#######..##########
21 | #.G..#........#####...##########
22 | #......#.G.G..........##########
23 | ###.#................###########
24 | ###..................###.#######
25 | ####............E.....#....#####
26 | ####.####.......####....E.######
27 | ####..#####.....####......######
28 | #############..#####......######
29 | #####################EE..E######
30 | #####################..#.E######
31 | #####################.##########
32 | ################################
--------------------------------------------------------------------------------
/src/main/resources/2018/Day19.txt:
--------------------------------------------------------------------------------
1 | #ip 2
2 | addi 2 16 2
3 | seti 1 0 1
4 | seti 1 4 3
5 | mulr 1 3 4
6 | eqrr 4 5 4
7 | addr 4 2 2
8 | addi 2 1 2
9 | addr 1 0 0
10 | addi 3 1 3
11 | gtrr 3 5 4
12 | addr 2 4 2
13 | seti 2 5 2
14 | addi 1 1 1
15 | gtrr 1 5 4
16 | addr 4 2 2
17 | seti 1 1 2
18 | mulr 2 2 2
19 | addi 5 2 5
20 | mulr 5 5 5
21 | mulr 2 5 5
22 | muli 5 11 5
23 | addi 4 5 4
24 | mulr 4 2 4
25 | addi 4 9 4
26 | addr 5 4 5
27 | addr 2 0 2
28 | seti 0 0 2
29 | setr 2 3 4
30 | mulr 4 2 4
31 | addr 2 4 4
32 | mulr 2 4 4
33 | muli 4 14 4
34 | mulr 4 2 4
35 | addr 5 4 5
36 | seti 0 6 0
37 | seti 0 3 2
--------------------------------------------------------------------------------
/src/main/resources/2019/Day01.txt:
--------------------------------------------------------------------------------
1 | 98578
2 | 105016
3 | 93022
4 | 144768
5 | 80394
6 | 112379
7 | 121119
8 | 94660
9 | 126363
10 | 112893
11 | 102603
12 | 93967
13 | 77268
14 | 103649
15 | 70132
16 | 142499
17 | 143711
18 | 140554
19 | 104725
20 | 84738
21 | 70613
22 | 108746
23 | 111488
24 | 89944
25 | 67984
26 | 59613
27 | 80035
28 | 69350
29 | 134001
30 | 62115
31 | 104688
32 | 143033
33 | 109712
34 | 96194
35 | 90353
36 | 96899
37 | 131267
38 | 143909
39 | 96649
40 | 138803
41 | 140620
42 | 73931
43 | 118851
44 | 58910
45 | 92205
46 | 132615
47 | 83308
48 | 73807
49 | 146895
50 | 142622
51 | 56234
52 | 126672
53 | 79278
54 | 111589
55 | 57593
56 | 80856
57 | 76261
58 | 89204
59 | 110871
60 | 74731
61 | 68654
62 | 103148
63 | 89935
64 | 58596
65 | 89510
66 | 101248
67 | 86137
68 | 56176
69 | 78679
70 | 128987
71 | 73114
72 | 143844
73 | 69805
74 | 54820
75 | 99223
76 | 119668
77 | 79449
78 | 98890
79 | 64512
80 | 104946
81 | 126345
82 | 128346
83 | 112212
84 | 135582
85 | 108214
86 | 111077
87 | 75745
88 | 125934
89 | 52956
90 | 102036
91 | 108452
92 | 129232
93 | 97091
94 | 106975
95 | 92156
96 | 145892
97 | 66680
98 | 88452
99 | 75081
100 | 102811
--------------------------------------------------------------------------------
/src/main/resources/2019/Day02.txt:
--------------------------------------------------------------------------------
1 | 1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,9,1,19,1,19,5,23,1,9,23,27,2,27,6,31,1,5,31,35,2,9,35,39,2,6,39,43,2,43,13,47,2,13,47,51,1,10,51,55,1,9,55,59,1,6,59,63,2,63,9,67,1,67,6,71,1,71,13,75,1,6,75,79,1,9,79,83,2,9,83,87,1,87,6,91,1,91,13,95,2,6,95,99,1,10,99,103,2,103,9,107,1,6,107,111,1,10,111,115,2,6,115,119,1,5,119,123,1,123,13,127,1,127,5,131,1,6,131,135,2,135,13,139,1,139,2,143,1,143,10,0,99,2,0,14,0
--------------------------------------------------------------------------------
/src/main/resources/2019/Day04.txt:
--------------------------------------------------------------------------------
1 | 197487-673251
--------------------------------------------------------------------------------
/src/main/resources/2020/Day01.txt:
--------------------------------------------------------------------------------
1 | 1706
2 | 1466
3 | 1427
4 | 1744
5 | 1684
6 | 1386
7 | 2001
8 | 1750
9 | 1753
10 | 1770
11 | 1559
12 | 1616
13 | 1408
14 | 1860
15 | 1940
16 | 2002
17 | 1862
18 | 1918
19 | 1456
20 | 1209
21 | 1840
22 | 1462
23 | 1783
24 | 1644
25 | 1901
26 | 1791
27 | 1506
28 | 2005
29 | 1338
30 | 1383
31 | 1420
32 | 1631
33 | 1784
34 | 1897
35 | 1771
36 | 1588
37 | 1955
38 | 1937
39 | 1392
40 | 1396
41 | 1803
42 | 1429
43 | 1407
44 | 1698
45 | 1562
46 | 1913
47 | 1678
48 | 1198
49 | 1398
50 | 1703
51 | 1831
52 | 1489
53 | 1782
54 | 1864
55 | 1708
56 | 1397
57 | 1915
58 | 1953
59 | 1395
60 | 1610
61 | 1549
62 | 1564
63 | 1973
64 | 1931
65 | 2009
66 | 1980
67 | 1800
68 | 1443
69 | 1993
70 | 1900
71 | 1964
72 | 1581
73 | 1904
74 | 1665
75 | 1567
76 | 1057
77 | 1805
78 | 1402
79 | 1878
80 | 1729
81 | 1825
82 | 1682
83 | 1719
84 | 1469
85 | 1004
86 | 1591
87 | 1594
88 | 811
89 | 1523
90 | 1424
91 | 1756
92 | 373
93 | 1442
94 | 1718
95 | 1411
96 | 1892
97 | 1820
98 | 1977
99 | 1871
100 | 1890
101 | 1653
102 | 1372
103 | 1475
104 | 1476
105 | 1422
106 | 2004
107 | 1755
108 | 1676
109 | 639
110 | 1425
111 | 1853
112 | 1712
113 | 1525
114 | 1514
115 | 1455
116 | 1658
117 | 1963
118 | 1579
119 | 1861
120 | 1458
121 | 1474
122 | 1613
123 | 1681
124 | 1586
125 | 1441
126 | 1499
127 | 1865
128 | 1735
129 | 1989
130 | 1952
131 | 792
132 | 1669
133 | 1509
134 | 1481
135 | 1893
136 | 1445
137 | 1834
138 | 1779
139 | 1732
140 | 1826
141 | 1595
142 | 1829
143 | 449
144 | 1920
145 | 1707
146 | 1780
147 | 1935
148 | 1867
149 | 1769
150 | 1107
151 | 919
152 | 1382
153 | 1604
154 | 1875
155 | 1453
156 | 1496
157 | 1946
158 | 1659
159 | 1570
160 | 1692
161 | 1630
162 | 1638
163 | 1922
164 | 1691
165 | 1580
166 | 1880
167 | 1482
168 | 1762
169 | 1775
170 | 1376
171 | 1434
172 | 1856
173 | 1971
174 | 1646
175 | 1951
176 | 1416
177 | 1889
178 | 1773
179 | 1814
180 | 1471
181 | 1488
182 | 1736
183 | 1743
184 | 1459
185 | 1389
186 | 1498
187 | 1663
188 | 1611
189 | 1727
190 | 1699
191 | 1624
192 | 1511
193 | 1767
194 | 1754
195 | 1785
196 | 1491
197 | 1235
198 | 1510
199 | 1500
200 | 1485
--------------------------------------------------------------------------------
/src/main/resources/2021/Day06.txt:
--------------------------------------------------------------------------------
1 | 3,3,2,1,4,1,1,2,3,1,1,2,1,2,1,1,1,1,1,1,4,1,1,5,2,1,1,2,1,1,1,3,5,1,5,5,1,1,1,1,3,1,1,3,2,1,1,1,1,1,1,4,1,1,1,1,1,1,1,4,1,3,3,1,1,3,1,3,1,2,1,3,1,1,4,1,2,4,4,5,1,1,1,1,1,1,4,1,5,1,1,5,1,1,3,3,1,3,2,5,2,4,1,4,1,2,4,5,1,1,5,1,1,1,4,1,1,5,2,1,1,5,1,1,1,5,1,1,1,1,1,3,1,5,3,2,1,1,2,2,1,2,1,1,5,1,1,4,5,1,4,3,1,1,1,1,1,1,5,1,1,1,5,2,1,1,1,5,1,1,1,4,4,2,1,1,1,1,1,1,1,3,1,1,4,4,1,4,1,1,5,3,1,1,1,5,2,2,4,2,1,1,3,1,5,5,1,1,1,4,1,5,1,1,1,4,3,3,3,1,3,1,5,1,4,2,1,1,5,1,1,1,5,5,1,1,2,1,1,1,3,1,1,1,2,3,1,2,2,3,1,3,1,1,4,1,1,2,1,1,1,1,3,5,1,1,2,1,1,1,4,1,1,1,1,1,2,4,1,1,5,3,1,1,1,2,2,2,1,5,1,3,5,3,1,1,4,1,1,4
--------------------------------------------------------------------------------
/src/main/resources/2021/Day11.txt:
--------------------------------------------------------------------------------
1 | 7777838353
2 | 2217272478
3 | 3355318645
4 | 2242618113
5 | 7182468666
6 | 5441641111
7 | 4773862364
8 | 5717125521
9 | 7542127721
10 | 4576678341
--------------------------------------------------------------------------------
/src/main/resources/2021/Day12.txt:
--------------------------------------------------------------------------------
1 | XW-ed
2 | cc-tk
3 | eq-ed
4 | ns-eq
5 | cc-ed
6 | LA-kl
7 | II-tk
8 | LA-end
9 | end-II
10 | SQ-kl
11 | cc-kl
12 | XW-eq
13 | ed-LA
14 | XW-tk
15 | cc-II
16 | tk-LA
17 | eq-II
18 | SQ-start
19 | LA-start
20 | XW-end
21 | ed-tk
22 | eq-JR
23 | start-kl
24 | ed-II
25 | SQ-tk
--------------------------------------------------------------------------------
/src/main/resources/2021/Day14.txt:
--------------------------------------------------------------------------------
1 | PBVHVOCOCFFNBCNCCBHK
2 |
3 | FV -> C
4 | SS -> B
5 | SC -> B
6 | BP -> K
7 | VP -> S
8 | HK -> K
9 | FS -> F
10 | CC -> V
11 | VB -> P
12 | OP -> B
13 | FO -> N
14 | FH -> O
15 | VK -> N
16 | PV -> S
17 | HV -> O
18 | PF -> F
19 | HH -> F
20 | NK -> S
21 | NC -> S
22 | FC -> H
23 | FK -> K
24 | OO -> N
25 | HP -> C
26 | NN -> H
27 | BB -> H
28 | CN -> P
29 | PS -> N
30 | VF -> S
31 | CB -> B
32 | OH -> S
33 | CF -> C
34 | OK -> P
35 | CV -> V
36 | CS -> H
37 | KN -> B
38 | OV -> S
39 | HB -> C
40 | OS -> V
41 | PC -> B
42 | CK -> S
43 | PP -> K
44 | SN -> O
45 | VV -> C
46 | NS -> F
47 | PN -> K
48 | HS -> P
49 | VO -> B
50 | VC -> B
51 | NV -> P
52 | VS -> N
53 | FP -> F
54 | HO -> S
55 | KS -> O
56 | BN -> F
57 | VN -> P
58 | OC -> K
59 | SF -> P
60 | PO -> P
61 | SB -> O
62 | FN -> F
63 | OF -> F
64 | CP -> C
65 | HC -> O
66 | PH -> O
67 | BC -> O
68 | NO -> C
69 | BH -> C
70 | VH -> S
71 | KK -> O
72 | SV -> K
73 | KB -> K
74 | BS -> S
75 | HF -> B
76 | NH -> S
77 | PB -> N
78 | HN -> K
79 | SK -> B
80 | FB -> F
81 | KV -> S
82 | BF -> S
83 | ON -> S
84 | BV -> P
85 | KC -> S
86 | NB -> S
87 | NP -> B
88 | BK -> K
89 | NF -> C
90 | BO -> K
91 | KF -> B
92 | KH -> N
93 | SP -> O
94 | CO -> S
95 | KO -> V
96 | SO -> B
97 | CH -> C
98 | KP -> C
99 | FF -> K
100 | PK -> F
101 | OB -> H
102 | SH -> C
--------------------------------------------------------------------------------
/src/main/resources/2023/Day06.txt:
--------------------------------------------------------------------------------
1 | Time: 61 70 90 66
2 | Distance: 643 1184 1362 1041
--------------------------------------------------------------------------------
/src/main/resources/2023/Day20.txt:
--------------------------------------------------------------------------------
1 | %ls -> cs, jd
2 | %st -> jh, bj
3 | %hc -> tn
4 | %xs -> rr
5 | %pq -> tl, cs
6 | %rj -> km, ck
7 | %lb -> cg
8 | %zz -> dx, ch
9 | %kk -> cs, tc
10 | %hr -> ck, pl
11 | %pj -> cs, pr
12 | %pr -> zl, cs
13 | %tl -> vn, cs
14 | %tc -> cs, hn
15 | &cs -> hn, pj, qb, zl
16 | %lv -> hr
17 | %vn -> cs, ls
18 | &mp -> dr
19 | broadcaster -> ns, pj, xz, sg
20 | %gg -> jh, nx
21 | %cr -> jg, jh
22 | %ch -> dx
23 | &dx -> zj, xz, mp, zn, xs, hc
24 | %fl -> jh, ps
25 | %hn -> pq
26 | %qp -> dx, zj
27 | %bp -> zr
28 | %kl -> lb
29 | %pl -> ck, xq
30 | %rm -> zz, dx
31 | %rz -> jh, fl
32 | %pm -> cs, kk
33 | %zl -> pm
34 | %rk -> ck, kl
35 | &dr -> rx
36 | %cg -> lv, ck
37 | &qt -> dr
38 | %jg -> jh
39 | &qb -> dr
40 | &ck -> lb, lv, ns, kl, qt
41 | %zr -> st
42 | %dd -> dx, qp
43 | %kd -> rk, ck
44 | %xq -> rj, ck
45 | %sg -> rz, jh
46 | %zj -> hc
47 | %tn -> dx, xs
48 | %jd -> cs
49 | %rr -> rm, dx
50 | &jh -> ng, bp, zr, sg, bj
51 | %km -> ck
52 | %ps -> bp, jh
53 | %zn -> dd
54 | %bj -> gg
55 | %nx -> jh, cr
56 | %xz -> dx, zn
57 | &ng -> dr
58 | %ns -> ck, kd
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/Puzzle.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode
2 |
3 | /**
4 | * The most generic and versatile puzzle interface
5 | */
6 | trait Puzzle[R1, I1, P1, R2, I2, P2] {
7 | def parse1(resource: String): R1
8 | def parse2(resource: String): R2
9 |
10 | def preProcess1(raw: R1): I1
11 | def preProcess2(raw: R2): I2
12 |
13 | def part1(input: I1): P1
14 | def part2(input: I2): P2
15 |
16 | def solve(resource: String): (P1, P2) = {
17 | val result1 = part1(preProcess1(parse1(resource)))
18 | val result2 = part2(preProcess2(parse2(resource)))
19 | (result1, result2)
20 | }
21 | }
22 |
23 | /**
24 | * A puzzle with differently parsed inputs but no pre-processing
25 | * With no pre-processing required
26 | */
27 | trait SimpleMultiPuzzle[I1, P1, I2, P2] extends Puzzle[I1, I1, P1, I2, I2, P2] {
28 | override def preProcess1(raw: I1): I1 = raw
29 | override def preProcess2(raw: I2): I2 = raw
30 | }
31 |
32 | /**
33 | * A puzzle with a simple input that is applicable for both parts
34 | * The input is pre-processed as there is a computation heavy common step
35 | */
36 | trait CommonPuzzle[R, I, P1, P2] extends Puzzle[R, I, P1, R, I, P2] {
37 | def parse(resource: String): R
38 | def preProcess(raw: R): I
39 |
40 | override def parse1(resource: String): R = parse(resource)
41 | override def parse2(resource: String): R = parse(resource)
42 |
43 | override def preProcess1(raw: R): I = preProcess(raw)
44 | override def preProcess2(raw: R): I = preProcess(raw)
45 |
46 | override def solve(resource: String): (P1, P2) = {
47 | val input = preProcess(parse(resource))
48 | val result1 = part1(input)
49 | val result2 = part2(input)
50 | (result1, result2)
51 | }
52 | }
53 |
54 | /**
55 | * A Puzzle with a simple input that is applicable for both parts
56 | * With no pre-processing required
57 | */
58 | trait SimpleCommonPuzzle[I, P1, P2] extends CommonPuzzle[I, I, P1, P2] {
59 | override def preProcess(raw: I): I = raw
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/PuzzleRunner.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode
2 |
3 | import scala.util.Try
4 |
5 | object PuzzleRunner extends App {
6 | val year = args.headOption.map(_.toInt).getOrElse(2020)
7 | val day = args.lift(1).map(_.toInt).getOrElse(1)
8 | lazy val puzzleMap = buildPuzzleMap(2015, 2023)
9 | run(puzzleMap, year, day)
10 |
11 | def buildPuzzleMap(from: Int, to: Int): Map[(Int, Int), Puzzle[_, _, _, _, _, _]] = {
12 | for {
13 | year <- from to to
14 | day <- 1 to 25
15 | puzzle <- puzzleReference(year, day)
16 | } yield ((year, day), puzzle)
17 | }.toMap
18 |
19 | // Maybe this should be a macro instead of reflection
20 | def puzzleReference(year: Int, day: Int): Option[Puzzle[_, _, _, _, _, _]] = Try {
21 | import scala.reflect.runtime.{universe => ru}
22 | val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
23 | val path = s"com.lmat.adventofcode.year$year.Day${"%02d".format(day)}"
24 | val staticModule = runtimeMirror.staticModule(path)
25 | val reflectModule = runtimeMirror.reflectModule(staticModule)
26 | reflectModule.instance.asInstanceOf[Puzzle[_, _, _, _, _, _]]
27 | }.toOption
28 |
29 | def resource(year: Int, day: Int): String = s"$year/Day${"%02d".format(day)}.txt"
30 |
31 | def run(puzzleMap: Map[(Int, Int), Puzzle[_, _, _, _, _, _]], year: Int, day: Int): Unit =
32 | puzzleMap.get(year, day) match {
33 | case None => println(s"Puzzle for Day $day (Year $year) is not yet solved!")
34 | case Some(puzzle) =>
35 | println(s"Solving puzzle for Day $day (Year $year)")
36 |
37 | val res = resource(year, day)
38 | println(s"Loading input from $res")
39 |
40 | val (result1, result2) = puzzle.solve(res)
41 | println(s"Part 1: $result1")
42 | println(s"Part 2: $result2")
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day01.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day01 extends SimpleCommonPuzzle[Seq[Char], Int, Int] {
7 | override def parse(resource: String): Seq[Char] = readResource(resource).flatMap(_.toCharArray)
8 |
9 | override def part1(instructions: Seq[Char]): Int =
10 | instructions.foldLeft(0)(move)
11 |
12 | override def part2(instructions: Seq[Char]): Int =
13 | instructions.scanLeft(0)(move)
14 | .zipWithIndex
15 | .find { case (f, _) => f == -1 }
16 | .map(_._2).get
17 |
18 | def move(floor: Int, char: Char): Int = char match {
19 | case '(' => floor + 1
20 | case ')' => floor - 1
21 | case _ => floor
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day02.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.adventofcode.year2015.Day02Definitions._
5 | import com.lmat.util.Files.readResource
6 |
7 | object Day02Definitions {
8 | case class Box(length: Int, width:Int, height:Int)
9 | }
10 |
11 | object Day02 extends SimpleCommonPuzzle[Seq[Box], Int, Int] {
12 | override def parse(resource: String): Seq[Box] = readResource(resource).flatMap(parseBox)
13 |
14 | def parseBox(line:String): Option[Box] = {
15 | val box = "(.*)x(.*)x(.*)".r
16 | line match {
17 | case box(length, width, height) => Some(Box(length.toInt, width.toInt, height.toInt))
18 | case _ => None
19 | }
20 | }
21 |
22 | override def part1(boxes: Seq[Box]): Int =
23 | boxes.map(wrapping).sum
24 |
25 | def wrapping(box: Box): Int =
26 | area(box) + sideAreas(box).min
27 |
28 | def area(box: Box): Int =
29 | 2 * box.length * box.width +
30 | 2 * box.width * box.height +
31 | 2 * box.height * box.length
32 |
33 | def sideAreas(box: Box): Seq[Int] =
34 | (box.length * box.width) :: (box.width * box.height) :: (box.height * box.length) :: Nil
35 |
36 | override def part2(boxes: Seq[Box]): Int =
37 | boxes.map(ribbon).sum
38 |
39 | def ribbon(box: Box): Int =
40 | volume(box) + perimeters(box).min
41 |
42 | def volume(box: Box): Int =
43 | box.length * box.width * box.height
44 |
45 | def perimeters(box: Box): Seq[Int] =
46 | (box.length + box.width) * 2 :: (box.width + box.height) * 2 :: (box.height + box.length) * 2 :: Nil
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day03.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 | import scala.language.postfixOps
6 |
7 | object Day03 extends SimpleCommonPuzzle[Seq[Char], Int, Int] {
8 | override def parse(resource: String): Seq[Char] = readResource(resource).flatMap(_.toCharArray)
9 |
10 | case class Position(x: Int, y: Int)
11 |
12 | override def part1(directions: Seq[Char]): Int =
13 | visited(directions).distinct.size
14 |
15 | def visited(directions: Seq[Char]): Seq[Position] =
16 | directions.scanLeft(Position(0, 0))(move)
17 |
18 | def move(position: Position, char: Char): Position = char match {
19 | case '^' => position.copy(y = position.y + 1)
20 | case 'v' => position.copy(y = position.y - 1)
21 | case '>' => position.copy(x = position.x + 1)
22 | case '<' => position.copy(x = position.x - 1)
23 | case _ => position
24 | }
25 |
26 | override def part2(directions: Seq[Char]): Int = {
27 | val (original, robo) = directions.zipWithIndex.partition(_._2 % 2 == 0)
28 | visited(original.map(_._1)).toSet union visited(robo.map(_._1)).toSet size
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day04.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day04 extends SimpleCommonPuzzle[String, Int, Int]{
7 | override def parse(resource: String): String = readResource(resource).head
8 |
9 | override def part1(key: String): Int =
10 | findFirstHashStartingWith(key, "00000")
11 |
12 | override def part2(key: String): Int =
13 | findFirstHashStartingWith(key, "000000")
14 |
15 | def findFirstHashStartingWith(key: String, pattern: String): Int =
16 | LazyList.from(1).map(i => (i, md5(s"$key$i")))
17 | .find { case (_, hash) => hash.startsWith(pattern) }
18 | .map(_._1).get
19 |
20 | def md5(text: String) : String =
21 | java.security.MessageDigest.getInstance("MD5")
22 | .digest(text.getBytes())
23 | .map { "%02x".format(_) }
24 | .mkString
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day05.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day05 extends SimpleCommonPuzzle[Seq[String], Int, Int] {
7 | override def parse(resource: String): Seq[String] = readResource(resource)
8 |
9 | override def part1(strings: Seq[String]): Int = strings.count(nice)
10 |
11 | def nice(string: String): Boolean =
12 | countChars(string, Seq('a', 'e', 'i', 'o', 'u')) >= 3 &&
13 | hasDoubleLetter(string, 0) &&
14 | !containsAtLeastOne(string, Seq("ab", "cd", "pq", "xy"))
15 |
16 | def countChars(string: String, chars: Seq[Char]): Int =
17 | string.count(chars.contains)
18 |
19 | def hasDoubleLetter(string: String, skip: Int): Boolean =
20 | (string zip string.drop(skip + 1)).count{ case (a, b) => a == b } > 0
21 |
22 | def containsAtLeastOne(string: String, subStrings: Seq[String]): Boolean =
23 | subStrings.exists(sub => string.contains(sub))
24 |
25 | override def part2(strings: Seq[String]): Int = strings.count(nice2)
26 |
27 | def nice2(string: String): Boolean =
28 | hasDoubleTwice(string) && hasDoubleLetter(string, 1)
29 |
30 | def hasDoubleTwice(string: String): Boolean =
31 | string.toSeq.sliding(2).map(_.unwrap).zipWithIndex
32 | .exists { case (double, i) => string.updated(i, '\u0000').updated(i + 1, '\u0000').contains(double)}
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day08.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day08 extends SimpleCommonPuzzle[Seq[String], Int, Int] {
7 |
8 | override def parse(resource: String): Seq[String] = readResource(resource)
9 |
10 | override def part1(input: Seq[String]): Int = {
11 | input.map(_.length).sum - input.map(resolveEscapes).map(_.length).sum
12 | }
13 |
14 | def resolveEscapes(input:String): String = {
15 | def resolveHexEscape(input: String): String = {
16 | val hex = "(.*?)\\\\x([0-9,a-f]{2})(.*)".r
17 | input match {
18 | case hex(pre, num, post) => pre + Integer.parseInt(num, 16).toChar + resolveHexEscape(post)
19 | case _ => input
20 | }
21 | }
22 |
23 | val simplified = input
24 | .replaceAll("\\\\\"", "\"")
25 | .replaceAll("\\\\\\\\", "\\\\")
26 |
27 | resolveHexEscape(simplified).drop(1).dropRight(1)
28 | }
29 |
30 | override def part2(input: Seq[String]): Int =
31 | input.map(addEscapes).map(_.length).sum - input.map(_.length).sum
32 |
33 | def addEscapes(input:String): String = {
34 | val escaped =
35 | input
36 | .replaceAll("\\\\","\\\\\\\\")
37 | .replaceAll("\"","\\\\\"")
38 |
39 | "\"" + escaped + "\""
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day10.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 |
8 | object Day10 extends SimpleCommonPuzzle[String, Int, Int] {
9 |
10 | override def parse(resource: String): String = readResource(resource).head
11 |
12 | override def part1(digits: String): Int =
13 | LazyList.iterate(digits)(lookAndSay).map(_.length).drop(40).head
14 |
15 | def toDigits(number:String): Seq[Int] =
16 | number.map(_.asDigit)
17 |
18 | @tailrec
19 | def lookAndSayLength(digits: String, n:Int): Int =
20 | if(n == 0) digits.length
21 | else lookAndSayLength(lookAndSay(digits), n - 1)
22 |
23 | def lookAndSay(digits: String): String =
24 | lookAndSay(toDigits(digits).toVector).mkString
25 |
26 | /**
27 | * As we are doing a large number of appends it is important to use Vector here
28 | * instead of String or List
29 | */
30 | def lookAndSay(digits: Vector[Int]): Vector[Int] = {
31 | val (acc, num, count) = digits.foldLeft((Vector[Int](), 0, 0)) {
32 | case ((acc, num, count), digit) if digit == num => (acc, num, count + 1)
33 | case ((acc, num, _), digit) if num == 0 => (acc, digit, 1)
34 | case ((acc, num, count), digit) => (acc :+ count :+ num, digit, 1)
35 | }
36 | acc :+ count :+ num
37 | }
38 |
39 | override def part2(digits: String): Int =
40 | LazyList.iterate(digits)(lookAndSay).map(_.length).drop(50).head
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day11.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day11 extends SimpleCommonPuzzle[String, String, String] {
7 | override def parse(resource: String): String = readResource(resource).head
8 |
9 | override def part1(current: String): String = nextPassword(current)
10 |
11 | def nextPassword(current:String): String =
12 | LazyList.iterate(current)(increment).drop(1).find(isValid).get
13 |
14 | def increment(password: String): String = password.toCharArray.toSeq match {
15 | case start :+ 'z' => increment(start.mkString) :+ 'a'
16 | case start :+ last => (start :+ increment(last)).mkString
17 | }
18 |
19 | def increment(char: Char): Char =
20 | (char + 1).toChar
21 |
22 | def isValid(password:String): Boolean =
23 | runOfAtLeast(3)(password) && noneOf(Seq('i', 'o', 'l'))(password) && differentDoubles(2)(password)
24 |
25 | def runOfAtLeast(n: Int)(string: String): Boolean =
26 | string.toSeq.sliding(n).map(_.unwrap).exists(isRun)
27 |
28 | def isRun(string: String): Boolean =
29 | (string zip string.drop(1)).forall { case (l, r) => r - l == 1 }
30 |
31 | def noneOf(letters: Seq[Char])(string: String): Boolean =
32 | !letters.exists(string.contains(_))
33 |
34 | def differentDoubles(n:Int)(string: String): Boolean =
35 | (for (letter <- 'a' to 'z') yield Seq(letter, letter).mkString).count(string.contains(_)) >= n
36 |
37 | override def part2(current: String): String = nextPassword(nextPassword(current))
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day12.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 | import com.lmat.util._
6 |
7 | object Day12 extends SimpleCommonPuzzle[String, Int, Int] {
8 | override def parse(resource: String): String = readResource(resource).head
9 |
10 | override def part1(input: String): Int = extractAllNumbers(input).sum
11 |
12 | def extractAllNumbers(input: String): Seq[Int] =
13 | "[-0-9]+".r.findAllIn(input).toSeq.map(_.toInt)
14 |
15 | override def part2(input: String): Int =
16 | sumAllInts(Json.parse(input).get)
17 |
18 | def sumAllInts(json: Json): Int = json match {
19 | case JsonIntValue(value) => value
20 | case JsonStringValue(_) => 0
21 | case JsonArray(elements) => elements.map(sumAllInts).sum
22 | case JsonObject(fields) => if(fields.values.exists(isRed)) 0 else fields.values.map(sumAllInts).sum
23 | }
24 |
25 | def isRed(json: Json): Boolean = json match {
26 | case JsonStringValue(value) => value == "red"
27 | case _ => false
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day17.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.CommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.math.Ordering
7 |
8 | object Day17 extends CommonPuzzle[Seq[Int], Seq[Seq[Int]], Int, Int]{
9 | override def parse(resource: String): Seq[Int] = readResource(resource).map(_.toInt).toList
10 |
11 | override def preProcess(containers: Seq[Int]): Seq[Seq[Int]] =
12 | combinations(containers.sorted(Ordering[Int].reverse), 150)
13 |
14 | override def part1(combinations: Seq[Seq[Int]]): Int =
15 | combinations.size
16 |
17 | def combinations(containers: Seq[Int], target: Int): Seq[Seq[Int]] = {
18 | case class Branch(element: Int, remaining: Seq[Int], target: Int)
19 |
20 | val branches: Seq[Branch] =
21 | containers.scanRight(Seq[Int]())(_ +: _).filter(_.nonEmpty)
22 | .map { case h +: t => Branch(h, t, target - h) }
23 | .filter(b => b.element <= target)
24 | .filter(b => b.remaining.sum >= b.target)
25 |
26 | val (finished, unfinished) = branches.partition(_.target == 0)
27 |
28 | finished.map(b => Seq(b.element)) ++ unfinished.flatMap(branch => combinations(branch.remaining, branch.target).map(branch.element +: _))
29 | }
30 |
31 | override def part2(combinations: Seq[Seq[Int]]): Int =
32 | combinations
33 | .groupBy(_.size).view
34 | .mapValues(_.size)
35 | .minBy(_._1)
36 | ._2
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day20.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 | import com.lmat.util.Maths.divisors
6 |
7 | object Day20 extends SimpleCommonPuzzle[Int, Int, Int]{
8 | override def parse(resource: String): Int = Integer.parseInt(readResource(resource).head)
9 |
10 | /**
11 | * We can speed up the solution slightly by simplifying both sides by 10
12 | */
13 | override def part1(input: Int): Int =
14 | LazyList.from(1).find(n => divisors(n).sum >= (input / 10)).get
15 |
16 | override def part2(input: Int): Int =
17 | LazyList.from(1).find(n => divisors(n).filter(_ * 50 >= n).map(_ * 11).sum >= input).get
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day24.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day24 extends SimpleCommonPuzzle[Set[Int], Long, Long]{
7 | override def parse(resource: String): Set[Int] = readResource(resource).map(_.toInt).toSet
8 |
9 | override def part1(packages: Set[Int]): Long =
10 | separatePackages(packages, 3).map(entanglement).min
11 |
12 | override def part2(packages: Set[Int]): Long =
13 | separatePackages(packages, 4).map(entanglement).min
14 |
15 | /**
16 | * Generate an infinite Stream of package subsets
17 | * That have a sum that is nth of the sum of all the packages
18 | * Where the rest of the packages are divisible to n-1 groups that have the same size as the original package
19 | * And where the group is minimally sized
20 | */
21 | def separatePackages(packages: Set[Int], n: Int): LazyList[Set[Int]] = {
22 | val groupOneCandidates = candidates(packages, n).filter(candidate => check(packages -- candidate, n - 1, packages.sum / n))
23 | groupOneCandidates.takeWhile(_.size == groupOneCandidates.head.size)
24 | }
25 |
26 | /**
27 | * Generate an infinite Stream of package subsets
28 | * That have a sum that is nth of the sum of all the packages
29 | * In increasing size
30 | */
31 | def candidates(packages: Set[Int], n: Int): LazyList[Set[Int]] =
32 | LazyList.from(1).flatMap(i => packages.subsets(i).filter(_.sum == packages.sum / n))
33 |
34 | /**
35 | * Check if the packages are divisible to n groups with equal sums
36 | */
37 | def check(packages: Set[Int], n: Int, sum: Int): Boolean =
38 | if(n == 1 && packages.sum == sum) true
39 | else if( n == 1) false
40 | else candidates(packages, n).exists(candidate => check(packages -- candidate, n - 1, sum))
41 |
42 | /**
43 | * We need to promote our packages to Long as the product quickly scales out of the Int range
44 | */
45 | def entanglement(packages: Set[Int]): Long =
46 | packages.map(_.toLong).product
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2015/Day25.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.util.Try
7 |
8 | object Day25 extends SimpleCommonPuzzle[(Int, Int), Long, Unit]{
9 | override def parse(resource: String): (Int, Int) = readResource(resource).headOption.flatMap(parseIndex).get
10 |
11 | def parseIndex(row: String): Option[(Int, Int)] = {
12 | val pattern = ".*row (.*), column (.*).".r
13 |
14 | row match {
15 | case pattern(r, c) => Try(r.toInt).flatMap(rI => Try(c.toInt).map(cI => (rI, cI))).toOption
16 | case _ => None
17 | }
18 | }
19 |
20 | lazy val codes: LazyList[Long] =
21 | LazyList.iterate(20151125L)(num => (num * 252533L) % 33554393L)
22 |
23 | override def part1(index: (Int, Int)): Long = {
24 | val (row, column) = index
25 | codes(findIndex(row, column) - 1)
26 | }
27 |
28 | /**
29 | * From the given row and column we can easily get the code index back
30 | * (Code indexes start from 1 as per the puzzle description)
31 | *
32 | * row, column => base => index
33 | * 1, 1 => 1 => 1
34 | * 2, 1 => 2 => 2
35 | * 1, 2 => 2 => 3
36 | * 3, 1 => 3 => 4
37 | * 2, 2 => 3 => 5
38 | * 1, 3 => 3 => 6
39 | */
40 | def findIndex(row: Int, column: Int): Int ={
41 | val base = row + column - 1
42 | (0 until base).sum + column
43 | }
44 |
45 | override def part2(index: (Int, Int)): Unit = ()
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day01.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 | import com.lmat.util.Sequences.shiftRight
6 |
7 | object Day01 extends SimpleCommonPuzzle[String, Int, Int] {
8 | override def parse(resource: String): String =
9 | readResource(resource).head
10 |
11 | override def part1(captcha: String): Int =
12 | solveCaptcha(1)(captcha)
13 |
14 | override def part2(captcha: String): Int =
15 | solveCaptcha(captcha.length / 2)(captcha)
16 |
17 | def solveCaptcha(shiftAmount: Int)(captcha: String): Int = {
18 | val digits = captcha.toIndexedSeq.map(_.asDigit)
19 | val shifted = shiftRight(digits, shiftAmount)
20 | sumEqualDigits(digits, shifted)
21 | }
22 |
23 | def sumEqualDigits(digits: Seq[Int], digits2: Seq[Int]): Int =
24 | (digits zip digits2)
25 | .filter { case (a, b) => a == b }
26 | .map(_._1)
27 | .sum
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day02.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day02 extends SimpleCommonPuzzle[Seq[Seq[Int]], Int, Int] {
7 | type SpreadSheet = Seq[Seq[Int]]
8 |
9 | override def parse(resource: String): SpreadSheet =
10 | parseSpreadsheet(readResource(resource))
11 |
12 | def parseSpreadsheet(lines: Seq[String]): SpreadSheet =
13 | lines.map(row =>
14 | row.split("\\s+").toSeq
15 | .map(_.toInt))
16 |
17 | override def part1(spreadsheet: SpreadSheet): Int =
18 | checksum(spreadsheet, largestDifference)
19 |
20 | override def part2(spreadsheet: SpreadSheet): Int =
21 | checksum(spreadsheet, evenlyDivisible)
22 |
23 | def checksum(data: SpreadSheet, rowCheckSum: Seq[Int] => Int): Int =
24 | data.map(rowCheckSum).sum
25 |
26 | def largestDifference(row: Seq[Int]): Int =
27 | row.max - row.min
28 |
29 | /**
30 | * Generate all pairs
31 | * Filter when i>=j to only compare one way
32 | * Get all the ones with 0 modulo
33 | * Sum and subtract the size for the checks with the item itself
34 | */
35 | def evenlyDivisible(row: Seq[Int]): Int = {
36 | val result = for {
37 | i <- row
38 | j <- row if i >= j && i % j == 0
39 | } yield i / j
40 |
41 | result.sum - row.size
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day04.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day04 extends SimpleCommonPuzzle[Seq[Seq[String]], Int, Int]{
7 | type PassPhrase = Seq[String]
8 |
9 | override def parse(resource: String): Seq[PassPhrase] =
10 | readResource(resource).map(_.split("\\s+").toSeq)
11 |
12 | override def part1(passPhrases: Seq[PassPhrase]): Int =
13 | countValidPassphrases(passPhrases, hasOnlyUniqueWords)
14 |
15 | override def part2(passPhrases: Seq[PassPhrase]): Int =
16 | countValidPassphrases(passPhrases, hasNoAnagrams)
17 |
18 | def countValidPassphrases(passPhrases:Seq[PassPhrase], criteria: PassPhrase => Boolean): Int =
19 | passPhrases.count(criteria)
20 |
21 | def hasOnlyUniqueWords(words: PassPhrase): Boolean =
22 | words.size == words.distinct.size
23 |
24 | def hasNoAnagrams(words: PassPhrase): Boolean =
25 | words.size == words.map(_.toSeq.sorted.unwrap).distinct.size
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day05.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 |
8 | object Day05 extends SimpleCommonPuzzle[Vector[Int], Int, Int] {
9 | override def parse(resource: String): Vector[Int] =
10 | readResource(resource).map(_.toInt).toVector
11 |
12 | override def part1(offsets: Vector[Int]): Int =
13 | calculateSteps(offsets, next)
14 |
15 | override def part2(offsets: Vector[Int]): Int =
16 | calculateSteps(offsets, next2)
17 |
18 | case class State(offsets: Vector[Int], position: Int)
19 |
20 | /**
21 | * Representing the offsets as a Vector is essential here for performance reasons
22 | * Vectors have (amortized) constant update and random access both of which we do a very high number of times in this puzzle
23 | */
24 | def calculateSteps(offsets: Vector[Int], strategy: State => State): Int = {
25 | @tailrec
26 | def calculateSteps(state: State, strategy: State => State, steps: Int): Int = {
27 | if(hasEscaped(state)) steps
28 | else calculateSteps(strategy(state), strategy, steps + 1)
29 | }
30 |
31 | calculateSteps(State(offsets, 0), strategy, 0)
32 | }
33 |
34 | def hasEscaped(state: State): Boolean = state match {
35 | case State(offsets, position) => position >= offsets.size || position < 0
36 | }
37 |
38 | def next(state: State): State = state match {
39 | case State(offsets, position) =>
40 | val step = offsets(position)
41 | State(offsets.updated(position, step + 1), position + step)
42 | }
43 |
44 | def next2(state: State): State = state match {
45 | case State(offsets, position) =>
46 | val step = offsets(position)
47 | State(offsets.updated(position, if (step >= 3) step - 1 else step + 1), position + step)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day06.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day06Definitions.Result
4 | import com.lmat.adventofcode.CommonPuzzle
5 | import com.lmat.util.Files.readResource
6 |
7 | import scala.annotation.tailrec
8 |
9 | object Day06Definitions {
10 | case class Result(count: Int, size: Int)
11 | }
12 |
13 | object Day06 extends CommonPuzzle[Vector[Int], Result, Int, Int] {
14 | type Blocks = Vector[Int]
15 |
16 | override def parse(resource: String): Blocks =
17 | readResource(resource).head.split("\\s+").map(_.toInt).toVector
18 |
19 | override def preProcess(blocks: Blocks): Result = countRedistributionCycles(blocks)
20 |
21 | override def part1(result: Result): Int = result.count
22 |
23 | override def part2(result: Result): Int = result.size
24 |
25 | def countRedistributionCycles(blocks: Blocks): Result = {
26 | @tailrec
27 | def countCycles(seen: Vector[Blocks], current: Blocks, count: Int): Result =
28 | if (seen.contains(current)) Result(count, count - seen.indexOf(current))
29 | else countCycles(seen :+ current, redistribute(current), count + 1)
30 |
31 | countCycles(Vector(), blocks, 0)
32 | }
33 |
34 | /**
35 | * To avoid simulating the redistribution we calculate the end state for each bank
36 | * We check how many full rounds the redistribution does and who gets the rest
37 | */
38 | def redistribute(blocks: Blocks): Blocks = {
39 | val indexed = blocks.zipWithIndex
40 | val (max, maxIndex) = indexed.maxBy(_._1)
41 | val size = indexed.size
42 |
43 | val rounds = max / size
44 | val mod = max % size
45 | val modIndices = (maxIndex + 1 to maxIndex + mod).map(_ % size)
46 |
47 | indexed.map{
48 | case (_, i) if i == maxIndex && modIndices.contains(i) => rounds + 1
49 | case (_, i) if i == maxIndex => rounds
50 | case (v, i) if modIndices.contains(i) => rounds + v + 1
51 | case (v, _) => rounds + v
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day09.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 |
8 | object Day09 extends SimpleCommonPuzzle[String, Int, Int] {
9 | override def parse(resource: String): String = readResource(resource).head
10 |
11 | /**
12 | * Every groups score is equal to its nesting level (starting from 1)
13 | * The level increases with every '{' and decreases with every '}'
14 | * We need to remove all garbage first to avoid counting invalid braces
15 | */
16 | override def part1(stream: String): Int = {
17 | @tailrec
18 | def calculateScore(remaining: List[Char], level: Int, acc: Int): Int = remaining match {
19 | case List() => acc
20 | case '{' :: rest => calculateScore(rest, level + 1, acc + level)
21 | case '}' :: rest => calculateScore(rest, level - 1, acc)
22 | case _ :: rest => calculateScore(rest, level, acc)
23 | }
24 |
25 | val simplified = removeGarbage(stream)
26 | calculateScore(simplified.toList, 1, 0)
27 | }
28 |
29 | override def part2(stream: String): Int =
30 | "<.*?>".r.findAllIn(removeCancelled(stream)).map(_.length - 2).sum
31 |
32 | def removeGarbage(stream: String): String =
33 | removeCancelled(stream).replaceAll("<.*?>", "")
34 |
35 | def removeCancelled(stream: String): String =
36 | stream.replaceAll("!.", "")
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day11.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day11 extends SimpleCommonPuzzle[Seq[String], Int, Int] {
7 | override def parse(resource: String): Seq[String] =
8 | readResource(resource).head.split(",").toIndexedSeq
9 |
10 | case class CubeCoordinates(x: Int, y: Int, z: Int)
11 |
12 | /**
13 | * Hexagonal grids have well defined mathematics which makes this exercise fairly simple
14 | * 1. Find end coordinates by applying the steps
15 | * 2. Calculate manhattan distance from start to end coordinates
16 | *
17 | * Alternatively you can solve it by defining replace (e.g. ne, s => se) and cancel rules (e.g. n, s) to find out the minimum necessary steps taken to destination
18 | */
19 | override def part1(steps: Seq[String]): Int = {
20 | val start = CubeCoordinates(0, 0, 0)
21 | val endPosition = steps.foldLeft(start)(next)
22 | manhattanDistance(start, endPosition)
23 | }
24 |
25 | override def part2(steps: Seq[String]): Int = {
26 | val start = CubeCoordinates(0, 0, 0)
27 | val endPositions = steps.scanLeft(start)(next)
28 | endPositions.map(manhattanDistance(start, _)).max
29 | }
30 |
31 | def next(coordinates: CubeCoordinates, step: String): CubeCoordinates = step match {
32 | case "ne" => coordinates.copy(x = coordinates.x + 1, z = coordinates.z - 1)
33 | case "sw" => coordinates.copy(x = coordinates.x - 1, z = coordinates.z + 1)
34 |
35 | case "nw" => coordinates.copy(x = coordinates.x - 1, y = coordinates.y + 1)
36 | case "se" => coordinates.copy(x = coordinates.x + 1, y = coordinates.y - 1)
37 |
38 | case "n" => coordinates.copy(y = coordinates.y + 1, z = coordinates.z - 1)
39 | case "s" => coordinates.copy(y = coordinates.y - 1, z = coordinates.z + 1)
40 | }
41 |
42 | def manhattanDistance(a: CubeCoordinates, b: CubeCoordinates): Int =
43 | (Math.abs(a.x - b.x) + Math.abs(a.y - b.y) + Math.abs(a.z - b.z)) / 2
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day12.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 |
8 | object Day12 extends SimpleCommonPuzzle[Map[Int, Set[Int]], Int, Int] {
9 | type ProgramMap = Map[Int, Set[Int]]
10 |
11 | override def parse(resource: String): ProgramMap = parseProgramMap(readResource(resource))
12 |
13 | def parseProgramMap(rows: Seq[String]): ProgramMap = {
14 | case class Program(id: Int, connections: Set[Int])
15 |
16 | def parseProgram(line: String): Option[Program] = {
17 | val regex = "(.*) <-> (.*)".r
18 | line match {
19 | case regex(id, list) => Some(Program(id.toInt, list.split(", ").map(_.toInt).toSet))
20 | case _ => None
21 | }
22 | }
23 |
24 | rows.flatMap(parseProgram).groupBy(_.id).view.mapValues(_.head.connections).toMap
25 | }
26 |
27 | override def part1(programMap: ProgramMap): Int = connectedTo(programMap)(0).size
28 |
29 | def connectedTo(programMap: ProgramMap)(id: Int): Set[Int] = {
30 | @tailrec
31 | def connectedTo(connected: Set[Int], toCheck: Set[Int]): Set[Int] =
32 | if (toCheck.isEmpty) connected
33 | else connectedTo(connected + toCheck.head, (programMap(toCheck.head) -- connected) ++ toCheck.tail)
34 |
35 | connectedTo(Set(), Set(id))
36 | }
37 |
38 | override def part2(programMap: ProgramMap): Int = groups(programMap).size
39 |
40 | def groups(programMap: ProgramMap): Set[Set[Int]] =
41 | programMap.keySet.map(id => connectedTo(programMap)(id))
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day13.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day13 extends SimpleCommonPuzzle[Map[Int, Int], Int, Int] {
7 | override def parse(resource: String): Map[Int, Int] = parseLayers(readResource(resource))
8 |
9 | def parseLayers(rows: Seq[String]): Map[Int, Int] =
10 | rows.map(_.split(": ")).groupBy(_.head.toInt).view.mapValues(_.head(1).toInt).toMap
11 |
12 | override def part1(layers: Map[Int, Int]): Int = calculateSeverity(layers, 0)
13 |
14 | def calculateSeverity(layers: Map[Int, Int], delay: Int): Int =
15 | layers.filter { case (layer, layerSize) => fallsOnZero(layer, layerSize, delay) }
16 | .map { case (layer, layerSize) => layer * layerSize }
17 | .sum
18 |
19 | def fallsOnZero(layer: Int, layerSize: Int, delay: Int): Boolean =
20 | (delay + layer) % ((layerSize - 1) * 2) == 0
21 |
22 | override def part2(layers: Map[Int, Int]): Int =
23 | LazyList.from(0).find(delay => canEscape(layers, delay)).get
24 |
25 | def canEscape(layers: Map[Int, Int], delay: Int): Boolean =
26 | layers.forall { case (layer, layerSize) => !fallsOnZero(layer, layerSize, delay) }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2017/Day17.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day17 extends SimpleCommonPuzzle[Int, Int, Int] {
7 | override def parse(resource: String): Int = readResource(resource).head.toInt
8 |
9 | override def part1(steps: Int): Int = {
10 | val buffer =
11 | (0 until 2017)
12 | .foldLeft((State(0, 0), Seq(0))) { case ((state, b), _) =>
13 | val next = nextState(state, steps)
14 | (next, nextBuffer(b, next))
15 | }._2
16 | valueAfter(buffer, 2017)
17 | }
18 |
19 | case class State(position: Int, value: Int)
20 |
21 | def nextState(state: State, numberOfSteps: Int): State = {
22 | val nextValue = state.value + 1
23 | val nextPosition = ((state.position + numberOfSteps) % nextValue) + 1
24 | State(nextPosition, nextValue)
25 | }
26 |
27 | def nextBuffer(buffer: Seq[Int], nextState: State): Seq[Int] =
28 | buffer.dropRight(buffer.size - nextState.position) ++ Seq(nextState.value) ++ buffer.drop(nextState.position)
29 |
30 | def valueAfter(buffer: Seq[Int], value: Int): Int =
31 | buffer.lift(buffer.indexOf(2017) + 1).getOrElse(buffer.head)
32 |
33 | /**
34 | * Notice that 0 will always stay in position 0
35 | * We can use this insight to be much lighter on memory allocation and only keep track of the positions and the element in position 1
36 | */
37 | override def part2(steps: Int): Int =
38 | (0 until 50000000)
39 | .foldLeft((State(0, 0), 0)) { case ((state, positionOne), _) =>
40 | val next = nextState(state, steps)
41 | (next, nextPositionOne(positionOne, next))
42 | }._2
43 |
44 | def nextPositionOne(positionOne: Int, nextState: State): Int =
45 | if (nextState.position == 1) nextState.value else positionOne
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2018/Day01.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 | import com.lmat.util.Sequences.{cumulative, cycle, findFirstDuplicate}
6 |
7 | import scala.util.Try
8 |
9 | object Day01 extends SimpleCommonPuzzle[Seq[Int], Int, Int] {
10 | override def parse(resource: String): Seq[Int] =
11 | readResource(resource).flatMap(row => Try(row.toInt).toOption)
12 |
13 | override def part1(frequencies: Seq[Int]): Int =
14 | frequencies.sum
15 |
16 | override def part2(frequencies: Seq[Int]): Int =
17 | findFirstDuplicate(cumulative[Int](0, _ + _)(cycle(frequencies))).get
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2018/Day02.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 | import com.lmat.util.Sequences.countElements
6 |
7 | object Day02 extends SimpleCommonPuzzle[Seq[String], Int, String] {
8 | override def parse(resource: String): Seq[String] = readResource(resource)
9 |
10 | override def part1(boxIds: Seq[String]): Int =
11 | boxIds.count(hasCount(2)) * boxIds.count(hasCount(3))
12 |
13 | def hasCount(n: Int)(boxId: String): Boolean =
14 | countElements(boxId).values.toSet.contains(n)
15 |
16 | override def part2(boxIds: Seq[String]): String =
17 | generatePairs(boxIds)
18 | .find{ case (b1, b2) => difference(b1, b2) == 1 }
19 | .map { case (b1, b2) => commonLetters(b1, b2)}.get
20 |
21 | def generatePairs(boxIds: Seq[String]): LazyList[(String, String)] = boxIds match {
22 | case h +: t => t.map((h, _)).to(LazyList) ++ generatePairs(t)
23 | case _ => LazyList()
24 | }
25 |
26 | def difference(b1: String, b2: String): Int =
27 | (b1 zip b2).count { case (c1, c2) => c1 != c2 }
28 |
29 | def commonLetters(b1: String, b2: String): String =
30 | (b1 zip b2).collect{ case (c1, c2) if c1 == c2 => c1 }.mkString
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2018/Day05.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.CommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 |
8 | object Day05 extends CommonPuzzle[String, String, Int, Int] {
9 | override def parse(resource: String): String = readResource(resource).head
10 |
11 | /**
12 | * We can use the initial reduction step for the start of both parts to avoid duplicating work
13 | */
14 | override def preProcess(polymer: String): String = react(polymer)
15 |
16 | def react(polymer: String): String = {
17 | @tailrec
18 | def reactOnce(polymer: Vector[Char], built: Vector[Char]): Vector[Char] = polymer match {
19 | case p1 +: p2 +: rest if areReacting(p1, p2) => reactOnce(rest, built)
20 | case p1 +: p2 +: rest => reactOnce(p2 +: rest, built :+ p1)
21 | case p => built ++ p
22 | }
23 |
24 | @tailrec
25 | def iterate(polymer: Vector[Char]): Vector[Char] = {
26 | val next = reactOnce(polymer, Vector())
27 | if(next == polymer) polymer
28 | else iterate(next)
29 | }
30 |
31 | iterate(polymer.toCharArray.toVector).mkString
32 | }
33 |
34 | def areReacting(p1: Char, p2: Char): Boolean =
35 | p1 != p2 && p1.toLower == p2.toLower
36 |
37 | override def part1(reduced: String): Int =
38 | reduced.length
39 |
40 | /**
41 | * We can start from the reduced size polymer that we calculated in part 1 to speed up the process
42 | * Only check for letters that appear in the reduced size polymer as opposed to the whole abc
43 | */
44 | override def part2(reduced: String): Int =
45 | unitTypes(reduced).map(removeAll(reduced, _)).map(react).map(_.length).min
46 |
47 | def unitTypes(polymer: String): Set[Char] =
48 | polymer.toCharArray.map(_.toLower).toSet
49 |
50 | def removeAll(polymer: String, unit: Char): String =
51 | polymer.filterNot(c => c == unit || c == unit.toUpper)
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2018/Day08.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.CommonPuzzle
4 | import com.lmat.adventofcode.year2018.Day08Definitions._
5 | import com.lmat.util.Files.readResource
6 |
7 | object Day08Definitions {
8 | case class Node(children: Seq[Node], metaData: Seq[Int])
9 | }
10 |
11 | object Day08 extends CommonPuzzle[Seq[Int], Node, Int, Int] {
12 | override def parse(resource: String): Seq[Int] =
13 | readResource(resource).head.split(" ").toSeq.map(_.toInt)
14 |
15 | def buildTree(numbers: Seq[Int]): Node = {
16 | def buildNodes(count: Int, numbers: Seq[Int]): (Seq[Node], Seq[Int]) =
17 | if(count == 0) (Seq[Node](), numbers)
18 | else {
19 | val (n1, remaining) = buildNode(numbers)
20 | val (ns, rest) = buildNodes(count - 1, remaining)
21 | (n1 +: ns, rest)
22 | }
23 |
24 | def buildNode(numbers: Seq[Int]): (Node, Seq[Int]) = {
25 | val childCount +: metaDataCount +: current = numbers
26 | val (children, remaining) = buildNodes(childCount, current)
27 | val (metaData, rest) = remaining.splitAt(metaDataCount)
28 | (Node(children, metaData), rest)
29 | }
30 |
31 | buildNode(numbers)._1
32 | }
33 |
34 | override def preProcess(numbers: Seq[Int]): Node = buildTree(numbers)
35 |
36 | override def part1(tree: Node): Int = metaDataSum(tree)
37 |
38 | def metaDataSum(node: Node): Int =
39 | node.metaData.sum + node.children.map(metaDataSum).sum
40 |
41 | override def part2(tree: Node): Int = nodeValue(tree)
42 |
43 | def nodeValue(node: Node): Int =
44 | if (node.children.isEmpty) node.metaData.sum
45 | else node.metaData.map(m => node.children.lift(m - 1).map(nodeValue).getOrElse(0)).sum
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2018/Day11.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.util.Try
7 |
8 | object Day11 extends SimpleCommonPuzzle[Int, String, String] {
9 | override def parse(resource: String): Int =
10 | readResource(resource).headOption.flatMap(r => Try(r.toInt).toOption).get
11 |
12 | override def part1(serialNumber: Int): String = {
13 | val gridSize = 300
14 | val grid = generateGrid(serialNumber)(gridSize)
15 | val (x, y) = findMaxTotalPower(grid, gridSize)(3)._1
16 | s"$x,$y"
17 | }
18 |
19 | def findMaxTotalPower(grid: Map[(Int, Int), Int], gridSize: Int)(n: Int): ((Int, Int), Int) =
20 | (for {
21 | x <- 0 to (gridSize - n)
22 | y <- 0 to (gridSize - n)
23 | } yield ((x, y), calculateTotalPower(grid, n)(x,y))).maxBy(_._2)
24 |
25 | def calculateTotalPower(grid: Map[(Int, Int), Int], n: Int)(x: Int, y: Int): Int =
26 | (for {
27 | xA <- x until x + n
28 | yA <- y until y + n
29 | } yield grid((xA, yA))).sum
30 |
31 | def generateGrid(serialNumber: Int)(n: Int): Map[(Int, Int), Int] =
32 | (for {
33 | x <- 0 until n
34 | y <- 0 until n
35 | } yield ((x, y), calculatePowerLevel(serialNumber)(x, y))).toMap
36 |
37 | def calculatePowerLevel(serialNumber: Int)(x: Int, y: Int): Int = {
38 | val rackId = x + 10
39 | hudredsDigit((rackId * y + serialNumber) * rackId) - 5
40 | }
41 |
42 | def hudredsDigit(number: Int): Int =
43 | number.toString.toCharArray.map(_.asDigit).takeRight(3).headOption.getOrElse(0)
44 |
45 | override def part2(serialNumber: Int): String = {
46 | val gridSize = 300
47 | val grid = generateGrid(serialNumber)(gridSize)
48 |
49 | // We are terminating early here as grids larger than 20x20 have negative values in them with a high chance
50 | val (x, y, i) = (1 to 20).map(i => {
51 | val ((x, y), power) = findMaxTotalPower(grid, gridSize)(i)
52 | ((x, y, i), power)
53 | }).maxBy(_._2)._1
54 | s"$x,$y,$i"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2018/Day14.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.util.Try
7 |
8 | object Day14 extends SimpleCommonPuzzle[Int, String, Int] {
9 | override def parse(resource: String): Int = readResource(resource).headOption.flatMap(row => Try(row.toInt).toOption).get
10 |
11 | /**
12 | * If we can generate an infinite Stream of the recipes both parts become trivial
13 | */
14 | def recipeStream: LazyList[Int] = {
15 | type State = (Vector[Int], Int, Int)
16 |
17 | def next(state: State): State = {
18 | val (recipes, i1, i2) = state
19 | val recipe1 = recipes(i1)
20 | val recipe2 = recipes(i2)
21 |
22 | val extendedRecipes = recipes ++ toDigits(recipe1 + recipe2)
23 | (extendedRecipes, shiftRight(i1, recipe1 + 1, extendedRecipes.length), shiftRight(i2, recipe2 + 1, extendedRecipes.length))
24 | }
25 |
26 | LazyList.iterate((Vector(3, 7), 0, 1))(next).zipWithIndex.map { case ((recipes, _, _), index) => recipes(index) }
27 | }
28 |
29 | def toDigits(number: Int): Vector[Int] =
30 | number.toString.toCharArray.map(_.asDigit).toVector
31 |
32 | def shiftRight(i: Int, n: Int, length: Int): Int = {
33 | val shifted = i + n
34 | if(shifted < length) shifted else shifted % length
35 | }
36 |
37 | override def part1(number: Int): String =
38 | recipeStream.slice(number, number + 10).mkString
39 |
40 | override def part2(number: Int): Int =
41 | recipeStream.indexOfSlice(toDigits(number))
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2019/Day01.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2019
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 | import scala.util.Try
8 |
9 | object Day01 extends SimpleCommonPuzzle[Seq[Int], Int, Int] {
10 | override def parse(resource: String): Seq[Int] =
11 | readResource(resource).flatMap(row => Try(row.toInt).toOption)
12 |
13 | override def part1(moduleMasses: Seq[Int]): Int =
14 | moduleMasses.map(fuel).sum
15 |
16 | override def part2(moduleMasses: Seq[Int]): Int =
17 | moduleMasses.map(fuel2).sum
18 |
19 | def fuel(mass: Int): Int = (mass / 3) - 2
20 |
21 | def fuel2(mass: Int): Int =
22 | LazyList.iterate(mass)(fuel).takeWhile(_ > 0).drop(1).sum
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2019/Day02.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2019
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 | import scala.util.Try
8 |
9 | object Day02 extends SimpleCommonPuzzle[Vector[Int], Int, Int] {
10 | override def parse(resource: String): Vector[Int] =
11 | readResource(resource).flatMap(row => row.split(",").flatMap(raw => Try(raw.toInt).toOption)).toVector
12 |
13 | override def part1(intCode: Vector[Int]): Int = solveProgram(intCode)(12, 2)
14 |
15 | def solveProgram(intCode: Vector[Int])(noun: Int, verb: Int): Int =
16 | solve(intCode.updated(1, noun).updated(2, verb)).head
17 |
18 | def solve(intCode: Vector[Int]): Vector[Int] = {
19 |
20 | def update(code: Vector[Int], current: Int, operation: (Int, Int) => Int): Vector[Int] =
21 | code.updated(code(current + 3), operation(code(code(current + 1)), code(code(current + 2))))
22 |
23 | @tailrec
24 | def iterate(code: Vector[Int], current: Int): Vector[Int] = code(current) match {
25 | case 99 => code
26 | case 1 => iterate(update(code, current, _ + _), current + 4)
27 | case 2 => iterate(update(code, current, _ * _), current + 4)
28 | case _ => throw new IllegalArgumentException("")
29 | }
30 |
31 | iterate(intCode, 0)
32 | }
33 |
34 | override def part2(intCode: Vector[Int]): Int =
35 | generateInputs
36 | // .map { case (noun, verb) => println(s"Trying: $noun and $verb"); (noun, verb)} // If you are interested what generateInputs produce
37 | .find{ case (noun, verb) => trySolveProgram(intCode)(noun,verb).contains(19690720) }
38 | .map { case (noun, verb) => 100 * noun + verb }
39 | .get
40 |
41 | lazy val generateInputs: LazyList[(Int, Int)] =
42 | LazyList.from(0).flatMap(variations(_).to(LazyList))
43 |
44 | def variations(a: Int): Set[(Int, Int)] =
45 | (0 to a).flatMap(b => Seq((a, b), (b, a))).toSet
46 |
47 | def trySolveProgram(intCode: Vector[Int])(noun: Int, verb: Int): Option[Int] =
48 | Try(solveProgram(intCode)(noun, verb)).toOption
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2020/Day02.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2020
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.adventofcode.year2020.Day02Definitions._
5 | import com.lmat.util.Files.readResource
6 |
7 | object Day02Definitions {
8 | case class Policy(char: Char, min: Int, max: Int)
9 | }
10 |
11 | object Day02 extends SimpleCommonPuzzle[List[(Policy, String)], Int, Int] {
12 | override def parse(resource: String): List[(Policy, String)] =
13 | readResource(resource).flatMap(parsePolicyRow).toList
14 |
15 | def parsePolicyRow(row: String): Option[(Policy, String)] = {
16 | val pattern = s"(\\d+)-(\\d+) (.): (.*)".r
17 |
18 | row match {
19 | case pattern(minRaw, maxRaw, charRaw, password) => for {
20 | char <- charRaw.toCharArray.headOption
21 | min <- minRaw.toIntOption
22 | max <- maxRaw.toIntOption
23 | } yield (Policy(char, min, max), password)
24 | case _ => None
25 | }
26 | }
27 |
28 | override def part1(passwords: List[(Policy, String)]): Int =
29 | passwords.count { case (policy, pass) => isValid(policy)(pass) }
30 |
31 | override def part2(passwords: List[(Policy, String)]): Int =
32 | passwords.count { case (policy, pass) => isValid2(policy)(pass) }
33 |
34 | def isValid(policy: Policy)(password: String): Boolean = {
35 | val givenCharCount = password.count(_ == policy.char)
36 | policy.min <= givenCharCount && givenCharCount <= policy.max
37 | }
38 |
39 | def isValid2(policy: Policy)(password: String): Boolean = {
40 | val relevantChars = List(policy.min, policy.max).map(_ - 1).flatMap(password.lift)
41 | relevantChars.contains(policy.char) && relevantChars.toSet.size != 1
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2021/Day01.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day01 extends SimpleCommonPuzzle[Seq[Int], Int, Int] {
7 | override def parse(resource: String): Seq[Int] =
8 | readResource(resource).flatMap(_.toIntOption).toList
9 |
10 | override def part1(measurements: Seq[Int]): Int =
11 | countIfNext(measurements)(_ < _)
12 |
13 | override def part2(measurements: Seq[Int]): Int =
14 | countIfNext(windowedReduce(measurements)(3, _.sum))(_ < _)
15 |
16 | def countIfNext[A](seq: Seq[A])(f: (A, A) => Boolean): Int =
17 | seq.zip(seq.drop(1)).count(f.tupled)
18 |
19 | def windowedReduce[A](seq: Seq[A])(size: Int, f: Seq[A] => A): Seq[A] =
20 | seq.sliding(size).map(f).toSeq
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2021/Day06.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day06 extends SimpleCommonPuzzle[List[Int], Long, Long] {
7 | override def parse(resource: String): List[Int] =
8 | readResource(resource).flatMap(_.split(",").flatMap(_.toIntOption)).toList
9 |
10 | override def part1(fish: List[Int]): Long = simulateAt(7, 2)(fish, 80).values.sum
11 | override def part2(fish: List[Int]): Long = simulateAt(7, 2)(fish, 256).values.sum
12 |
13 | // Representing the lanternfish population by their count at each timer value is crucial for scaling
14 | def simulateAt(spawn: Int, extra: Int)(fish: List[Int], days: Int): Map[Int, Long] =
15 | LazyList.iterate(fish.groupBy(identity).view.mapValues(_.size.toLong).toMap)(next(spawn, extra)).take(days + 1).last
16 |
17 | def next(spawn: Int, extra: Int)(fish: Map[Int, Long]): Map[Int, Long] =
18 | fish.toList.map { case (timer, count) =>
19 | if (timer == 0) Map(spawn - 1 -> count, spawn + extra - 1 -> count)
20 | else Map(timer - 1 -> count)
21 | }.reduce(merge[Int])
22 |
23 | def merge[A](a: Map[A, Long], b: Map[A, Long]): Map[A, Long] =
24 | (a.keySet ++ b.keySet).map(key => (key, a.getOrElse(key, 0L) + b.getOrElse(key, 0L))).toMap
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2021/Day07.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day07 extends SimpleCommonPuzzle[List[Int], Int, Int]{
7 | override def parse(resource: String): List[Int] =
8 | readResource(resource).flatMap(_.split(",").flatMap(_.toIntOption)).toList
9 |
10 | override def part1(crabs: List[Int]): Int = align(crabs, identity)._2
11 | override def part2(crabs: List[Int]): Int = align(crabs, n => n * (n + 1) / 2)._2
12 |
13 | // Find the alignment that takes the minimum amount of fuel based on step cost
14 | def align(crabs: List[Int], stepCost: Int => Int): (Int, Int) =
15 | (for {position <- crabs.min to crabs.max}
16 | yield (position, crabs.map(crab => Math.abs(position - crab)).map(stepCost).sum))
17 | .minBy { case (_, fuel) => fuel }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2021/Day11.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | import scala.annotation.tailrec
7 |
8 | object Day11 extends SimpleCommonPuzzle[Map[(Int, Int), Int], Int, Int] {
9 | override def parse(resource: String): Map[(Int, Int), Int] =
10 | parseMap(readResource(resource))
11 |
12 | def parseMap(rows: Seq[String]): Map[(Int, Int), Int] =
13 | rows.map(_.toCharArray.map(_.asDigit).zipWithIndex).zipWithIndex
14 | .flatMap { case (row, y) => row.map { case (v, x) => ((x, y), v) } }.toMap
15 |
16 | override def part1(energyMap: Map[(Int, Int), Int]): Int =
17 | simulate(energyMap).slice(1, 101).map(_.count(_._2 == 0)).sum
18 |
19 | override def part2(energyMap: Map[(Int, Int), Int]): Int =
20 | simulate(energyMap).zipWithIndex.find(_._1.values.forall(_ == 0)).map(_._2).getOrElse(0)
21 |
22 | def simulate(start: Map[(Int, Int), Int]): LazyList[Map[(Int, Int), Int]] =
23 | LazyList.iterate(start)(step)
24 |
25 | def step(energyMap: Map[(Int, Int), Int]): Map[(Int, Int), Int] = {
26 | def flash(current: Map[(Int, Int), Int], x: Int, y: Int): Map[(Int, Int), Int] = {
27 | val flashed = current.updated((x, y), 0)
28 | val neighbours = adjacent(current)(x, y).filter(_._3 != 0)
29 | neighbours.foldLeft(flashed) { case (state, (xN, yN, vN)) => state.updated((xN, yN), vN + 1) }
30 | }
31 |
32 | @tailrec
33 | def loop(current: Map[(Int, Int), Int]): Map[(Int, Int), Int] = {
34 | val flashing = current.filter(_._2 > 9)
35 | if (flashing.isEmpty) current
36 | else loop(flashing.foldLeft(current) { case (state, ((x, y), _)) => flash(state, x, y) })
37 | }
38 |
39 | loop(energyMap.view.mapValues(_ + 1).toMap)
40 | }
41 |
42 | def adjacent(map: Map[(Int, Int), Int])(x: Int, y: Int): Set[(Int, Int, Int)] =
43 | Set((-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)).flatMap { case (xo, yo) => map.get((x + xo, y + yo)).map(v => (x + xo, y + yo, v)) }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2023/Day01.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day01 extends SimpleCommonPuzzle[List[String], Int, Int] {
7 | override def parse(resource: String): List[String] =
8 | readResource(resource).toList
9 |
10 | override def part1(lines: List[String]): Int =
11 | calibrate(firstDigit, lastDigit)(lines)
12 |
13 | override def part2(lines: List[String]): Int =
14 | calibrate(realFirstDigit, realLastDigit)(lines)
15 |
16 | def calibrate(firstDigit: String => Int, lastDigit: String => Int)(lines: List[String]): Int =
17 | lines.map(line => firstDigit(line) * 10 + lastDigit(line)).sum
18 |
19 | def firstDigit(line: String): Int =
20 | line.find(_.isDigit).map(_.asDigit).getOrElse(0)
21 |
22 | def lastDigit(line: String): Int =
23 | firstDigit(line.reverse)
24 |
25 | val digits: List[String] = List("one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
26 |
27 | def realFirstDigit(digits:List[String], line: String): Int = {
28 | val (numeric, numericPosition) = firstNumericDigit(line).getOrElse(0, Int.MaxValue)
29 | val (spelled, spelledPosition) = firstSpelledDigit(digits)(line).getOrElse(0, Int.MaxValue)
30 | if (numericPosition < spelledPosition) numeric else spelled
31 | }
32 |
33 | def realFirstDigit(line: String): Int =
34 | realFirstDigit(digits, line)
35 |
36 | def realLastDigit(line: String): Int =
37 | realFirstDigit(digits.map(_.reverse), line.reverse)
38 |
39 | def firstNumericDigit(line: String): Option[(Int, Int)] =
40 | line.zipWithIndex
41 | .find { case (c, _) => c.isDigit }
42 | .map { case (c, i) => (c.asDigit, i) }
43 |
44 | def firstSpelledDigit(digits: List[String])(line: String): Option[(Int, Int)] =
45 | digits.map(d => (d, line.indexOf(d)))
46 | .filter { case (_, i) => i >= 0 }
47 | .map { case (d, i) => (digits.indexOf(d) + 1, i) }
48 | .minByOption { case (_, i) => i }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2023/Day02.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.adventofcode.year2023.Day02Definitions._
5 | import com.lmat.util.Files.readResource
6 |
7 | object Day02Definitions {
8 | case class CubeSet(red: Int, green: Int, blue: Int)
9 |
10 | case class Game(id: Int, hands: List[CubeSet])
11 | }
12 |
13 | object Day02 extends SimpleCommonPuzzle[List[Game], Int, Int] {
14 | override def parse(resource: String): List[Game] =
15 | readResource(resource).flatMap(parseGame).toList
16 |
17 | def parseGame(row: String): Option[Game] = {
18 | val pattern = s"Game (\\d+): (.*)".r
19 |
20 | row match {
21 | case pattern(idRaw, hands) => idRaw.toIntOption.map(id => Game(id, hands.split("; ").map(parseHand).toList))
22 | case _ => None
23 | }
24 | }
25 |
26 | def parseHand(raw: String): CubeSet = {
27 | val cubesMap = raw.split(", ").flatMap(cubes => {
28 | val pattern = s"(\\d+) (.*)".r
29 | cubes match {
30 | case pattern(number, colour) => number.toIntOption.map(n => (colour, n))
31 | case _ => None
32 | }
33 | }).toMap
34 |
35 | CubeSet(cubesMap.getOrElse("red", 0), cubesMap.getOrElse("green", 0), cubesMap.getOrElse("blue", 0))
36 | }
37 |
38 | override def part1(games: List[Game]): Int =
39 | games.filter(isPossible(CubeSet(12, 13, 14), _)).map(_.id).sum
40 |
41 | def isPossible(bag: CubeSet, game: Game): Boolean =
42 | game.hands.forall(isPossible(bag, _))
43 |
44 | def isPossible(bag: CubeSet, hand: CubeSet): Boolean =
45 | bag.red >= hand.red && bag.green >= hand.green && bag.blue >= hand.blue
46 |
47 | override def part2(games: List[Game]): Int =
48 | games.map(minimalBag).map(power).sum
49 |
50 | def minimalBag(game: Game): CubeSet =
51 | CubeSet(game.hands.map(_.red).max, game.hands.map(_.green).max, game.hands.map(_.blue).max)
52 |
53 | def power(bag: CubeSet): Int =
54 | bag.red * bag.green * bag.blue
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2023/Day06.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.SimpleMultiPuzzle
4 | import com.lmat.adventofcode.year2023.Day06Definitions._
5 | import com.lmat.util.Files.readResource
6 |
7 | object Day06Definitions {
8 | case class Race(time: Long, distance: Long)
9 | }
10 |
11 | object Day06 extends SimpleMultiPuzzle[List[Race], Long, Race, Long] {
12 | override def parse1(resource: String): List[Race] =
13 | parseRaces(readResource(resource).toList)
14 |
15 | override def parse2(resource: String): Race =
16 | parseRace(readResource(resource).toList)
17 |
18 | def parseRaces(lines: List[String]): List[Race] = {
19 | val times = lines.head.split("[\\D]").flatMap(_.toLongOption)
20 | val distances = lines(1).split("[\\D]").flatMap(_.toLongOption)
21 | (times zip distances).map { case (t, d) => Race(t, d) }.toList
22 | }
23 |
24 | def parseRace(lines: List[String]): Race = {
25 | val time = lines.head.filter(_.isDigit).toLong
26 | val distance = lines(1).filter(_.isDigit).toLong
27 | Race(time, distance)
28 | }
29 |
30 | override def part1(races: List[Race]): Long =
31 | races.map(countRecords).product
32 |
33 | def countRecords(race: Race): Long =
34 | simulate(race.time).count {case (_, distance) => distance > race.distance}
35 |
36 | def simulate(time: Long): List[(Long, Long)] =
37 | (0L to time).map(hold => (hold, (time - hold) * hold)).toList
38 |
39 | override def part2(race: Race): Long = countRecordsFast(race)
40 |
41 | def countRecordsFast(race: Race): Long =
42 | race.time - LazyList.iterate(0L)(_ + 1L).takeWhile(hold => ((race.time - hold) * hold) <= race.distance).size * 2 + 1
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/adventofcode/year2023/Day09.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.SimpleCommonPuzzle
4 | import com.lmat.util.Files.readResource
5 |
6 | object Day09 extends SimpleCommonPuzzle[List[List[Int]], Long, Long] {
7 | override def parse(resource: String): List[List[Int]] =
8 | readResource(resource).toList.map(parseRow)
9 |
10 | def parseRow(row: String): List[Int] =
11 | row.split(" ").flatMap(_.toIntOption).toList
12 |
13 | override def part1(oasis: List[List[Int]]): Long =
14 | oasis.map(expand).map(history).sum
15 |
16 | def expand(start: List[Int]): List[List[Int]] =
17 | LazyList.iterate(start)(prev => prev.zip(prev.drop(1)).map { case (a, b) => b - a }).takeWhile(_.exists(_ != 0)).toList
18 |
19 | def history(history: List[List[Int]]): Int =
20 | history.reverse.map(_.last).sum
21 |
22 | override def part2(oasis: List[List[Int]]): Long =
23 | oasis.map(expand).map(history2).sum
24 |
25 | def history2(history: List[List[Int]]): Int =
26 | history.reverse.map(_.head).foldLeft(0)((s, c) => c - s)
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/util/Files.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | import scala.io.Source
4 |
5 | object Files {
6 |
7 | /**
8 | * Read the contents of a resource file given its relative path into a sequence of lines
9 | */
10 | def readResource(resource: String): Seq[String] =
11 | Source.fromInputStream(getClass.getClassLoader.getResourceAsStream(resource)).getLines().toSeq
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/util/Logic.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | object Logic {
4 | def and[A](fs: (A => Boolean)*): A => Boolean =
5 | fs.reduce[A => Boolean]{ case (f1, f2) => n => f1(n) && f2(n) }
6 |
7 | def or[A](fs: (A => Boolean)*): A => Boolean =
8 | fs.reduce[A => Boolean]{ case (f1, f2) => n => f1(n) || f2(n) }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/util/Maths.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | object Maths {
4 |
5 | /**
6 | * Simple primality test
7 | * Tests if a number is prime or not
8 | */
9 | def isPrime(n: Int): Boolean =
10 | if (n <= 1) false
11 | else (2 to math.sqrt(n).toInt).forall(n % _ != 0)
12 |
13 | /**
14 | * Tests if a number is composite or not
15 | */
16 | def isComposite(n: Int): Boolean = !isPrime(n)
17 |
18 | /**
19 | * All positive divisors
20 | * 60 => Set(1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60)
21 | */
22 | def divisors(n: Int): Set[Int] = {
23 | val half = (1 to math.sqrt(n).toInt).filter(n % _ == 0)
24 | (half ++ half.map(n / _)).toSet
25 | }
26 |
27 | def divisors(n: Long): Set[Long] = {
28 | val half = (1L to math.sqrt(n).toLong).filter(n % _ == 0)
29 | (half ++ half.map(n / _)).toSet
30 | }
31 |
32 | /**
33 | * Greatest common divisor
34 | */
35 | def gcd(a: Long, b: Long): Long = {
36 | val divA = divisors(a)
37 | val divB = divisors(b)
38 | divA.intersect(divB).max
39 | }
40 |
41 | /**
42 | * Least common multiple
43 | */
44 | def lcm(a: Long, b: Long): Long =
45 | Math.abs(a * b) / gcd(a, b)
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/util/Search.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | object Search {
4 |
5 | /**
6 | * Using the lazy nature of streams
7 | * We can safely iterate over all possible states (given a starting state)
8 | * - We can find all possible solution for puzzles (Given that the state space is small enough)
9 | * - We can find the shortest solution for puzzles (Given that our children function returns all child states sorted for our length criteria)
10 | */
11 | def streamSearch[A](initial: LazyList[A], children: A => LazyList[A]): LazyList[A] = {
12 | val more = initial.flatMap(children)
13 | if (more.isEmpty) initial
14 | else initial #::: streamSearch(more, children)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/util/Strings.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | object Strings {
4 | /**
5 | * Left pad a string to a length with the specified character
6 | */
7 | def leftPad(source: String)(pad: Char, length: Int): String =
8 | (pad.toString * (length - source.length)) + source
9 |
10 |
11 | /**
12 | * Find all indexesOf the query string in source
13 | * Return both the starting (inclusive) and ending indices (non-inclusive)
14 | */
15 | def indicesOf(raw: String, query: String): Seq[(Int, Int)] =
16 | raw.scanRight("")((c, s) => s"$c$s").dropRight(1).zipWithIndex
17 | .filter { case (test, _) => test.startsWith(query) }
18 | .map { case (_, index) => (index, index + query.length) }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/scala/com/lmat/util/Traverse.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | object Traverse {
4 | def sequenceOption[T](elements: Seq[Option[T]]): Option[Seq[T]] =
5 | if (elements.exists(_.isEmpty)) None
6 | else Some(elements.map(_.get))
7 | }
8 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day01Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day01._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day01Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val plans =
9 | Table(
10 | ("plan", "floor"),
11 | ("(())", 0),
12 | ("()()", 0),
13 | ("(((", 3),
14 | ("(()(()(", 3),
15 | ("))(((((", 3),
16 | ("())", -1),
17 | ("))(", -1),
18 | (")))", -3),
19 | (")())())", -3)
20 | )
21 |
22 | test("Day01 - Part 1") {
23 | forAll(plans) { (plan, floor) =>
24 | assert(part1(plan) == floor)
25 | }
26 | }
27 |
28 | val plans2 =
29 | Table(
30 | ("plan", "position"),
31 | (")", 1),
32 | ("()())", 5)
33 | )
34 |
35 | test("Day01 - Part 2") {
36 | forAll(plans2) { (plan, position) =>
37 | assert(part2(plan) == position)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day02Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day02._
4 | import com.lmat.adventofcode.year2015.Day02Definitions.Box
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day02Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val boxes =
10 | Table(
11 | ("box", "wrapping", "ribbon"),
12 | (Box(2, 3, 4), 58, 34),
13 | (Box(1, 1, 10), 43, 14)
14 | )
15 |
16 | test("Day02 - Part 1") {
17 | forAll(boxes) { (box, w, _) =>
18 | assert(wrapping(box) == w)
19 | }
20 | }
21 |
22 | test("Day02 - Part 2") {
23 | forAll(boxes) { (box, _, r) =>
24 | assert(ribbon(box) == r)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day03Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day03._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day03Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val directions =
9 | Table(
10 | ("directions", "simple", "with-robo"),
11 | (">", 2, 2),
12 | ("^v", 2, 3),
13 | ("^>v<", 4, 3),
14 | ("^v^v^v^v^v", 2, 11)
15 | )
16 |
17 | test("Day03 - Part 1") {
18 | forAll(directions) { (directions, houses, _) =>
19 | assert(part1(directions) == houses)
20 | }
21 | }
22 |
23 | test("Day03 - Part 2") {
24 | forAll(directions) { (directions, _, houses) =>
25 | assert(part2(directions) == houses)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day04Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day04.part1
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day04Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val directions =
9 | Table(
10 | ("key", "number"),
11 | ("abcdef", 609043),
12 | ("pqrstuv", 1048970),
13 | )
14 |
15 | test("Day04 - Part 1") {
16 | forAll(directions) { (key, number) =>
17 | assert(part1(key) == number)
18 | }
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day05Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day05.{nice, nice2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day05Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val strings =
9 | Table(
10 | ("string", "nice"),
11 | ("ugknbfddgicrmopn", true),
12 | ("aaa", true),
13 | ("jchzalrnumimnmhp", false),
14 | ("haegwjzuvuyypxyu", false),
15 | ("dvszwmarrgswjxmb", false)
16 | )
17 |
18 | test("Day05 - Part 1") {
19 | forAll(strings) { (string, isNice) =>
20 | assert(nice(string) == isNice)
21 | }
22 | }
23 |
24 | val strings2 =
25 | Table(
26 | ("string", "nice"),
27 | ("qjhvhtzxzqqjkmpb", true),
28 | ("xxyxx", true),
29 | ("uurcxstgmygtbstg", false),
30 | ("ieodomkazucvgmuy", false)
31 | )
32 |
33 | test("Day05 - Part 2") {
34 | forAll(strings2) { (string, isNice) =>
35 | assert(nice2(string) == isNice)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day06Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day06._
4 | import com.lmat.adventofcode.year2015.Day06Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day06Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val instructions = Seq(
10 | Instruction("turn on", Point(0, 0), Point(999, 999)),
11 | Instruction("toggle", Point(0, 0), Point(999, 0)),
12 | Instruction("turn off", Point(499, 499), Point(500, 500)))
13 |
14 | test("Day06 - Part 1") {
15 | assert(part1(instructions) == 998996)
16 | }
17 |
18 | val instructions2 = Seq(
19 | Instruction("turn on", Point(0, 0), Point(0, 0)),
20 | Instruction("toggle", Point(0, 0), Point(999, 999)))
21 |
22 | test("Day06 - Part 2") {
23 | assert(part2(instructions2) == 2000001) }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day07Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day07Definitions._
4 | import com.lmat.adventofcode.year2015.Day07._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day07Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val rawInstructions =
11 | """123 -> x
12 | |456 -> y
13 | |x AND y -> d
14 | |x OR y -> e
15 | |x LSHIFT 2 -> f
16 | |y RSHIFT 2 -> g
17 | |NOT x -> h
18 | |NOT y -> i
19 | """.stripMargin
20 |
21 | val instructions = Seq(
22 | Instruction(Identity("123"), "x"),
23 | Instruction(Identity("456"), "y"),
24 | Instruction(And("x", "y"), "d"),
25 | Instruction(Or("x", "y"), "e"),
26 | Instruction(LShift("x", 2), "f"),
27 | Instruction(RShift("y", 2), "g"),
28 | Instruction(Not("x"), "h"),
29 | Instruction(Not("y"), "i"))
30 |
31 | test("Day07 - Parse") {
32 | assert(rawInstructions.split("\n").toSeq.flatMap(parseInstruction) == instructions)
33 | }
34 |
35 | test("Day07 - Emulate") {
36 | val expected = Map(
37 | "d" -> 72,
38 | "e" -> 507,
39 | "f" -> 492,
40 | "g" -> 114,
41 | "h" -> 65412,
42 | "i" -> 65079,
43 | "x" -> 123,
44 | "y" -> 456)
45 |
46 | assert(emulate(instructions) == expected)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day08Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day08._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day08Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val raw =
10 | """""
11 | |"abc"
12 | |"aaa\"aaa"
13 | |"\x27"""".stripMargin
14 |
15 | val decoded =
16 | """
17 | |abc
18 | |aaa"aaa
19 | |'""".stripMargin
20 |
21 | val encoded =
22 | """"\"\""
23 | |"\"abc\""
24 | |"\"aaa\\\"aaa\""
25 | |"\"\\x27\""""".stripMargin
26 |
27 | test("Day08 - Resolve escapes") {
28 | assert(raw.split("\n").map(resolveEscapes) sameElements decoded.split("\n"))
29 | }
30 |
31 | test("Day08 - Add escapes") {
32 | assert(raw.split("\n").map(addEscapes) sameElements encoded.split("\n"))
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day09Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day09Definitions.Distance
4 | import com.lmat.adventofcode.year2015.Day09._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day09Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val distances = Seq(
11 | Distance("London", "Dublin", 464),
12 | Distance("London", "Belfast", 518),
13 | Distance("Dublin", "Belfast", 141))
14 |
15 | test("Day09 - Part 1") {
16 | assert(part1(distances) == 605)
17 | }
18 |
19 | test("Day09 - Part 2") {
20 | assert(part2(distances) == 982)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day10Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day10.lookAndSay
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day10Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val sequence = Seq(
10 | "1",
11 | "11",
12 | "21",
13 | "1211",
14 | "111221",
15 | "312211"
16 | )
17 |
18 | test("Day10 - Look And Say") {
19 | val actual = LazyList.iterate("1")(lookAndSay)
20 | assert(actual.take(6) == sequence)
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day11Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day11._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day11Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val sequence = Seq(
10 | "ax",
11 | "ay",
12 | "az",
13 | "ba",
14 | "bb",
15 | "bc"
16 | )
17 |
18 | test("Day11 - Increment") {
19 | assert(LazyList.iterate("ax")(increment).take(6) == sequence)
20 | }
21 |
22 | val passwords =
23 | Table(
24 | ("password", "valid"),
25 | ("hijklmmn", false),
26 | ("abbceffg", false),
27 | ("abbceabc", false),
28 | ("abcdffaa", true),
29 | ("ghjaabcc", true)
30 | )
31 |
32 | test("Day11 - Validity") {
33 | forAll(passwords) { (password, valid) =>
34 | assert(isValid(password) == valid)
35 | }
36 | }
37 |
38 | val passwords2 =
39 | Table(
40 | ("password", "next"),
41 | ("abcdefgh", "abcdffaa"),
42 | ("ghijklmn", "ghjaabcc")
43 | )
44 |
45 | test("Day11 - Next") {
46 | forAll(passwords2) { (password, next) =>
47 | assert(nextPassword(password) == next)
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day12Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day12._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day12Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val books =
10 | Table(
11 | ("books", "sum"),
12 | ("""[1,2,3]""", 6),
13 | ("""{"a":2,"b":4}""", 6),
14 | ("""[[[3]]]""", 3),
15 | ("""{"a":{"b":4},"c":-1}""", 3),
16 | ("""{"a":[-1,1]}""", 0),
17 | ("""[-1,{"a":1}]""", 0),
18 | ("""[]""", 0),
19 | ("""{}""", 0)
20 | )
21 |
22 | test("Day12 - Part1") {
23 | forAll(books) { (book, sum) =>
24 | assert(part1(book) == sum)
25 | }
26 | }
27 |
28 | val books2 =
29 | Table(
30 | ("books", "sum"),
31 | ("""[1,2,3]""", 6),
32 | ("""[1,{"c":"red","b":2},3]""", 4),
33 | ("""{"d":"red","e":[1,2,3,4],"f":5}""", 0),
34 | ("""[1,"red",5]""", 6)
35 | )
36 |
37 | test("Day12 - Part2") {
38 | forAll(books2) { (book, sum) =>
39 | assert(part2(book) == sum)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day15Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day15._
4 | import com.lmat.adventofcode.year2015.Day15Definitions.Ingredient
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day15Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val rawIngredients =
11 | """Butterscotch: capacity -1, durability -2, flavor 6, texture 3, calories 8
12 | |Cinnamon: capacity 2, durability 3, flavor -2, texture -1, calories 3
13 | """.stripMargin
14 |
15 | val butterscotch = Ingredient("Butterscotch", -1, -2, 6, 3, 8)
16 | val cinnamon = Ingredient("Cinnamon", 2, 3, -2, -1, 3)
17 | val ingredients = List(butterscotch, cinnamon)
18 |
19 | test("Day15 - Parse") {
20 | assert(rawIngredients.split("\n").flatMap(parseIngredient).toSeq == ingredients)
21 | }
22 |
23 | val scores =
24 | Table(
25 | ("ingredients", "score"),
26 | (Map(butterscotch -> 100, cinnamon -> 0), 0),
27 | (Map(butterscotch -> 0, cinnamon -> 100), 0),
28 | (Map(butterscotch -> 50, cinnamon -> 50), 50000000),
29 | (Map(butterscotch -> 44, cinnamon -> 56), 62842880),
30 | (Map(butterscotch -> 56, cinnamon -> 44), 19681280)
31 | )
32 |
33 | test("Day15 - Scoring") {
34 | forAll(scores) { (ingredients, s) =>
35 | assert(score(ingredients) == s)
36 | }
37 | }
38 |
39 | test("Day15 - Part 1") {
40 | assert(part1(preProcess(ingredients)) == 62842880)
41 | }
42 |
43 | test("Day15 - Part 2") {
44 | assert(part2(preProcess(ingredients)) == 57600000)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day16Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day16._
4 | import com.lmat.adventofcode.year2015.Day16Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day16Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val rawAunts =
10 | """Sue 1: goldfish: 6, trees: 9, akitas: 0
11 | |Sue 2: goldfish: 7, trees: 1, akitas: 0
12 | """.stripMargin
13 |
14 | val aunts = List(
15 | Aunt("Sue", 1, Map("goldfish" -> 6, "trees"-> 9, "akitas" -> 0)),
16 | Aunt("Sue", 2, Map("goldfish" -> 7, "trees"-> 1, "akitas" -> 0))
17 | )
18 |
19 | test("Day16 - Parse") {
20 | assert(rawAunts.split("\n").toSeq.flatMap(parseAunt) == aunts)
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day17Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day17._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day17Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val containers = List(20, 15, 10, 5, 5)
10 |
11 | val combinations25 = List(
12 | List(20, 5),
13 | List(20, 5),
14 | List(15, 10),
15 | List(15, 5, 5)
16 | )
17 |
18 | test("Day17 - Combinations") {
19 | assert(combinations(containers, 25) == combinations25)
20 | }
21 |
22 | test("Day17 - Part 1") {
23 | assert(part1(combinations25) == 4)
24 | }
25 |
26 | test("Day17 - Part 2") {
27 | assert(part2(combinations25) == 3)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day19Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day19._
4 | import com.lmat.adventofcode.year2015.Day19Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day19Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val rawGenetics =
10 | """H => HO
11 | |H => OH
12 | |O => HH
13 | |
14 | |HOH""".stripMargin
15 |
16 | val genetics = Genetics(
17 | "HOH",
18 | Set(
19 | Rule("H", "HO"),
20 | Rule("H", "OH"),
21 | Rule("O", "HH")))
22 |
23 | test("Day19 - Parse") {
24 | assert(parseGenetics(rawGenetics.split("\n").toSeq) == genetics)
25 | }
26 |
27 | test("Day19 - Next Molecules") {
28 | assert(nextMolecules(genetics.rules)(genetics.molecule) == Set("HOOH", "HOHO", "OHOH", "HHHH"))
29 | }
30 |
31 | test("Day19 - Part 1") {
32 | assert(part1(genetics) == 4)
33 | }
34 |
35 | val genetics2 =
36 | Genetics(
37 | "HOH",
38 | Set(
39 | Rule("e", "H"),
40 | Rule("e", "O"),
41 | Rule("H", "HO"),
42 | Rule("H", "OH"),
43 | Rule("O", "HH")))
44 |
45 | test("Day19 - Part 2") {
46 | assert(part2(genetics2) == 3)
47 | assert(part2(genetics2.copy(molecule = "HOHOHO")) == 6)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day20Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 | import com.lmat.adventofcode.year2015.Day20._
6 |
7 | class Day20Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | test("Day20 - Part 1") {
9 | assert(part1(100) == 6)
10 | assert(part1(1000) == 48)
11 | }
12 |
13 | test("Day20 - Part 2") {
14 | assert(part2(100) == 6)
15 | assert(part2(1000) == 36)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day21Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 | import com.lmat.adventofcode.year2015.Day21Definitions._
6 | import com.lmat.adventofcode.year2015.Day21._
7 |
8 |
9 | class Day21Test extends AnyFunSuite with TableDrivenPropertyChecks {
10 | val games =
11 | Table(
12 | ("player", "boss", "damage", "canWin"),
13 | (Character(8, 5, 5), Character(12, 7, 2), 3, true),
14 | (Character(8, 5, 4), Character(12, 7, 2), 3, false),
15 | (Character(8, 4, 5), Character(12, 7, 2), 2, false),
16 | (Character(12, 7, 2), Character(8, 5, 5), 2, true), // Player wins here as well because of starters advantage
17 | (Character(12, 6, 2), Character(8, 5, 5), 1, false),
18 | (Character(12, 7, 1), Character(8, 5, 5), 2, false),
19 | (Character(12, 5, 2), Character(8, 5, 5), 1, false),
20 | (Character(12, 4, 2), Character(8, 5, 5), 1, false),
21 | )
22 |
23 | test("Day21 - Damage") {
24 | forAll(games) { (player, boss, damage, _) =>
25 | assert(calculateDamage(player, boss) == damage)
26 | }
27 | }
28 |
29 | test("Day21 - Can Win") {
30 | forAll(games) { (player, boss, _, win) =>
31 | assert(canWin(player, boss) == win)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day23Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 |
6 | import com.lmat.adventofcode.year2015.Day23Definitions._
7 | import com.lmat.adventofcode.year2015.Day23._
8 |
9 | class Day23Test extends AnyFunSuite with TableDrivenPropertyChecks {
10 | val rawInstructions =
11 | """inc a
12 | |jio a, +2
13 | |tpl a
14 | |inc a""".stripMargin
15 |
16 | val instructions = Seq(
17 | Increment("a"),
18 | JumpIfOdd("a",2),
19 | Triple("a"),
20 | Increment("a")
21 | )
22 |
23 | test("Day23 - Parse") {
24 | assert(rawInstructions.split("\n").flatMap(parseInstruction).toSeq == instructions)
25 | }
26 |
27 | test("Day23 - Apply") {
28 | assert(applyInstructions(State(Map("a" -> 0, "b" -> 0), 0), instructions) == State(Map("a" -> 2, "b" -> 0),4))
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day24Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.year2015.Day24._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day24Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val packages = Set(1, 2, 3, 4, 5, 7, 8, 9, 10, 11)
9 |
10 | test("Day24 - Parts") {
11 | assert(separatePackages(packages, 3).map(entanglement).min == 99)
12 | assert(separatePackages(packages, 4).map(entanglement).min == 44)
13 | }
14 |
15 | test("Day24 - Groups") {
16 | assert(separatePackages(packages, 3).toList == List(Set(9, 11)))
17 | assert(separatePackages(packages, 4).toList == List(Set(5, 10), Set(7, 8), Set(11, 4)))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Day25Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 | import com.lmat.adventofcode.year2015.Day25._
6 |
7 | class Day25Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val codes =
10 | Table(
11 | ("row", "column", "index", "code"),
12 | (1, 1, 1, 20151125L),
13 | (2, 1, 2, 31916031L),
14 | (1, 2, 3, 18749137L),
15 | (3, 1, 4, 16080970L),
16 | (2, 2, 5, 21629792L),
17 | (1, 3, 6, 17289845L),
18 | )
19 |
20 | test("Day25 - Index") {
21 | forAll(codes) { (row, column, index, _) =>
22 | assert(findIndex(row, column) == index)
23 | }
24 | }
25 |
26 | test("Day25 - Part 1") {
27 | forAll(codes) { (row, column, _, code) =>
28 | assert(part1(row, column) == code)
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2015/Year2015Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2015
2 |
3 | import com.lmat.adventofcode.PuzzleRunner.{puzzleMap, resource}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Year2015Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val puzzles =
10 | Table(
11 | ("day", "part1", "part2"),
12 | (1, 232, 1783),
13 | (2, 1586300, 3737498),
14 | (3, 2572, 2631),
15 | (4, 117946, 3938038),
16 | (5, 255, 55),
17 | (6, 569999, 17836115),
18 | (7, 16076, 2797),
19 | (8, 1350, 2085),
20 | (9, 141, 736),
21 | (10, 492982, 6989950),
22 | (11, "hxbxxyzz", "hxcaabcc"),
23 | (12, 119433, 68466),
24 | (13, 709, 668),
25 | (14, 2640, 1102),
26 | (15, 18965440, 15862900),
27 | (16, 103, 405),
28 | (17, 1638, 17),
29 | (18, 1061, 1006),
30 | (19, 518, 200),
31 | (20, 831600, 884520),
32 | (21, 91, 158),
33 | (22, 953, 1289),
34 | (23, 184, 231),
35 | (24, 11846773891L, 80393059L),
36 | (25, 2650453, ())
37 | )
38 |
39 | forAll(puzzles) { (day, part1, part2) =>
40 | val year = 2015
41 |
42 | test(s"$year: Day $day") {
43 | val (res1, res2) = puzzleMap(year, day).solve(resource(year, day))
44 | assert(res1 == part1)
45 | assert(res2 == part2)
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day01Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day01.solveCaptcha
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day01Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val captchas =
9 | Table(
10 | ("captcha", "result"),
11 | ("1122", 3),
12 | ("1111", 4),
13 | ("1234", 0),
14 | ("91212129", 9),
15 | )
16 |
17 | test("Day01 - Part 1") {
18 | forAll(captchas) { (captcha, result) =>
19 | assert(solveCaptcha(1)(captcha) == result)
20 | }
21 | }
22 |
23 | val captchas2 =
24 | Table(
25 | ("captcha", "result"),
26 | ("1212", 6),
27 | ("1221", 0),
28 | ("123425", 4),
29 | ("123123", 12),
30 | ("12131415", 4)
31 | )
32 |
33 | test("Day01 - Part 2") {
34 | forAll(captchas2) { (captcha, result) =>
35 | assert(solveCaptcha(captcha.length / 2)(captcha) == result)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day02Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day02.{part1, part2, parseSpreadsheet}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day02Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val raws =
10 | Table(
11 | ("raw", "spreadsheet"),
12 | ("5 1 9 5\n7 5 3\n2 4 6 8", Seq(Seq(5, 1, 9, 5), Seq(7, 5, 3), Seq(2, 4, 6, 8))),
13 | ("5 9 2 8\n9 4 7 3\n3 8 6 5", Seq(Seq(5, 9, 2, 8), Seq(9, 4, 7, 3), Seq(3, 8, 6, 5)))
14 | )
15 |
16 | test("Parse") {
17 | forAll(raws) { (raw, spreadsheet) =>
18 | assert(parseSpreadsheet(raw.split("\n").toIndexedSeq) == spreadsheet)
19 | }
20 | }
21 |
22 | val spreadsheets =
23 | Table(
24 | ("spreadsheet", "checksum"),
25 | (Seq(Seq(5, 1, 9, 5), Seq(7, 5, 3), Seq(2, 4, 6, 8)), 18)
26 | )
27 |
28 | test("Day02 - Part 1") {
29 | forAll(spreadsheets) { (spreadsheet, check) =>
30 | assert(part1(spreadsheet) == check)
31 | }
32 | }
33 |
34 | val spreadsheets2 =
35 | Table(
36 | ("spreadsheet", "checksum"),
37 | (Seq(Seq(5, 9, 2, 8), Seq(9, 4, 7, 3), Seq(3, 8, 6, 5)), 9)
38 | )
39 |
40 | test("Day02 - Part 2") {
41 | forAll(spreadsheets2) { (spreadsheet, check) =>
42 | assert(part2(spreadsheet) == check)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day03Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day03.{part1, part2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day03Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val examples =
9 | Table(
10 | ("input", "steps"),
11 | (1, 0),
12 | (12, 3),
13 | (23, 2),
14 | (1024, 31),
15 | )
16 |
17 | test("Day03 - Part 1") {
18 | forAll(examples) { (input, steps) =>
19 | assert(part1(input) == steps)
20 | }
21 | }
22 |
23 | val examples2 =
24 | Table(
25 | ("input", "value"),
26 | (0, 1),
27 | (1, 2),
28 | (23, 25),
29 | (24, 25),
30 | )
31 |
32 | test("Day03 - Part 2") {
33 | forAll(examples2) { (input, value) =>
34 | assert(part2(input) == value)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day04Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day04.{hasNoAnagrams, hasOnlyUniqueWords}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day04Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val examples =
9 | Table(
10 | ("input", "isValid"),
11 | ("aa bb cc dd ee", true),
12 | ("aa bb cc dd aa", false),
13 | ("aa bb cc dd aaa", true)
14 | )
15 |
16 | test("Day04 - Part 1") {
17 | forAll(examples) { (input, isValid) =>
18 | assert(hasOnlyUniqueWords(input.split("\\s+").toSeq) == isValid)
19 | }
20 | }
21 |
22 | val examples2 =
23 | Table(
24 | ("input", "isValid"),
25 | ("abcde fghij", true),
26 | ("abcde xyz ecdab", false),
27 | ("a ab abc abd abf abj", true),
28 | ("iiii oiii ooii oooi oooo", true),
29 | ("oiii ioii iioi iiio", false)
30 | )
31 |
32 | test("Day04 - Part 2") {
33 | forAll(examples2) { (input, isValid) =>
34 | assert(hasNoAnagrams(input.split("\\s+").toSeq) == isValid)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day05Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day05.{part1, part2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day05Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val offsets =
10 | Table(
11 | ("offsets", "steps"),
12 | (Vector(0, 3, 0, 1, -3), 5)
13 | )
14 |
15 | test("Day05 - Part 1") {
16 | forAll(offsets) { (offsets, steps) =>
17 | assert(part1(offsets) == steps)
18 | }
19 | }
20 |
21 | val offsets2 =
22 | Table(
23 | ("offsets", "steps"),
24 | (Vector(0, 3, 0, 1, -3), 10)
25 | )
26 |
27 | test("Day05 - Part 2") {
28 | forAll(offsets2) { (offsets, steps) =>
29 | assert(part2(offsets) == steps)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day06Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day06.{part1, part2, preProcess, redistribute}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day06Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val examples =
9 | Table(
10 | ("Blocks", "Count", "Size"),
11 | (Vector(0,2,7,0), 5, 4)
12 | )
13 |
14 | test("Day06 - Part 1") {
15 | forAll(examples) { (blocks, steps, _) =>
16 | assert(part1(preProcess(blocks)) == steps)
17 | }
18 | }
19 |
20 | test("Day06 - Part 2") {
21 | forAll(examples) { (blocks, _, size) =>
22 | assert(part2(preProcess(blocks)) == size)
23 | }
24 | }
25 |
26 | val redistributeData =
27 | Table(
28 | ("Input", "result"),
29 | (Vector(0, 2, 7, 0), Vector(2, 4, 1, 2)),
30 | (Vector(2, 4, 1, 2), Vector(3, 1, 2, 3)),
31 | (Vector(3, 1, 2, 3), Vector(0, 2, 3, 4)),
32 | (Vector(0, 2, 3, 4), Vector(1, 3, 4, 1)),
33 | (Vector(1, 3, 4, 1), Vector(2, 4, 1, 2)),
34 | (Vector(0, 0, 2, 0), Vector(1, 0, 0, 1))
35 | )
36 |
37 | test("Redistribute") {
38 | forAll(redistributeData) { (input, result) =>
39 | assert(redistribute(input) == result)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day08Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day08Definitions._
4 | import com.lmat.adventofcode.year2017.Day08.{parseInstruction, part1, part2}
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day08Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val rawInstructions =
10 | """b inc 5 if a > 1
11 | |a inc 1 if b < 5
12 | |c dec -10 if a >= 1
13 | |c inc -20 if c == 10""".stripMargin
14 |
15 | val instructions = Seq(
16 | Instruction("b", "inc", 5, Condition("a", ">", 1)),
17 | Instruction("a", "inc", 1, Condition("b", "<", 5)),
18 | Instruction("c", "dec", -10, Condition("a", ">=", 1)),
19 | Instruction("c", "inc", -20, Condition("c", "==", 10))
20 | )
21 |
22 | test("Parse Instructions") {
23 | assert(rawInstructions.split("\n").toSeq.map(parseInstruction) == instructions)
24 | }
25 |
26 | test("Day08 - Part 1") {
27 | assert(part1(instructions) == 1)
28 | }
29 |
30 | test("Day08 - Part 2") {
31 | assert(part2(instructions) == 10)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day09Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day09.{part1, part2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day09Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val streams =
9 | Table(
10 | ("stream", "score"),
11 | ("{}", 1),
12 | ("{{{}}}", 6),
13 | ("{{},{}}", 5),
14 | ("{{{},{},{{}}}}", 16),
15 | ("{,,,}", 1),
16 | ("{{},{},{},{}}", 9),
17 | ("{{},{},{},{}}", 9),
18 | ("{{},{},{},{}}", 3),
19 | )
20 |
21 | test("Day09 - Part 1") {
22 | forAll(streams) { (stream, score) =>
23 | assert(part1(stream) == score)
24 | }
25 | }
26 |
27 | val garbage =
28 | Table(
29 | ("stream", "garbage"),
30 | ("<>", 0),
31 | ("", 17),
32 | ("<<<<>", 3),
33 | ("<{!>}>", 2),
34 | ("", 0),
35 | (">", 0),
36 | ("""<{o"i!a,<{i""", 10),
37 | )
38 |
39 | test("Day 09 - Part 2") {
40 | forAll(garbage) { (stream, garbage) =>
41 | assert(part2(stream) == garbage)
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day10Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day10._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day10Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | test("Day 10 - Part 1") {
10 | val hash = knotHashRound(initialState(5), Seq(3, 4, 1, 5))
11 | val expectedState = State(Seq(3, 4, 2, 1, 0), 4, 4)
12 |
13 | assert(hash == expectedState)
14 | assert(hash.circularList.take(2).product == 12)
15 | }
16 |
17 | val lengths =
18 | Table(
19 | ("source", "hash"),
20 | ("", "a2582a3a0e66e6e86e3812dcb672a272"),
21 | ("AoC 2017", "33efeb34ea91902bb2f59c9920caa6cd"),
22 | ("1,2,3", "3efbe78a8d82f29979031a4aa0b16a9d"),
23 | ("1,2,4", "63960835bcdc130f0b66d7ff4f6a5a8e"),
24 | )
25 |
26 | test("Day 10 - Part 2") {
27 | forAll(lengths) { (source, hash) =>
28 | assert(part2(calculateLengths(source)) == hash)
29 | }
30 | }
31 |
32 | test("Calculate Lengths") {
33 | assert(calculateLengths("1,2,3") == Seq(49, 44, 50, 44, 51, 17, 31, 73, 47, 23))
34 | }
35 |
36 | test("XOR") {
37 | assert(xor(Seq(65, 27, 9, 1, 4, 3, 40, 50, 91, 7, 6, 0, 2, 5, 68, 22)) == 64)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day11Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day11.{part1, part2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day11Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val directions =
10 | Table(
11 | ("Directions", "Distance", "Furthest"),
12 | ("ne,ne,ne", 3, 3),
13 | ("ne,ne,sw,sw", 0, 2),
14 | ("ne,ne,s,s", 2, 2),
15 | ("se,sw,se,sw,sw", 3, 3)
16 | )
17 |
18 | test("Day 11 - Part 1") {
19 | forAll(directions) { (direction, distance, _) =>
20 | assert(part1(direction.split(",").toIndexedSeq) == distance)
21 | }
22 | }
23 |
24 | test("Day 11 - Part 2") {
25 | forAll(directions) { (direction, _, furthest) =>
26 | assert(part2(direction.split(",").toIndexedSeq) == furthest)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day12Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day12.{ProgramMap, connectedTo, groups, parseProgramMap}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day12Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val input =
9 | """0 <-> 2
10 | |1 <-> 1
11 | |2 <-> 0, 3, 4
12 | |3 <-> 2, 4
13 | |4 <-> 2, 3, 6
14 | |5 <-> 6
15 | |6 <-> 4, 5""".stripMargin
16 |
17 | val programMap: ProgramMap = Map(
18 | 0 -> Set(2),
19 | 1 -> Set(1),
20 | 2 -> Set(0, 3, 4),
21 | 3 -> Set(2, 4),
22 | 5 -> Set(6),
23 | 4 -> Set(2, 3, 6),
24 | 6 -> Set(4, 5))
25 |
26 | test("Parse input") {
27 | assert(parseProgramMap(input.split("\n").toIndexedSeq) == programMap)
28 | }
29 |
30 | test("Day 12 - Part 1 - Connected to") {
31 | val result = connectedTo(programMap)(0)
32 | assert(result == Set(0, 5, 6, 2, 3, 4))
33 | assert(result.size == 6)
34 | }
35 |
36 | test("Day 12 - Part 2 - Groups") {
37 | val result = groups(programMap)
38 | assert(result == Set(Set(0, 5, 6, 2, 3, 4), Set(1)))
39 | assert(result.size == 2)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day13Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day13.{parseLayers, part1, part2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day13Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val input =
9 | """0: 3
10 | |1: 2
11 | |4: 4
12 | |6: 4""".stripMargin
13 |
14 | val layers = Map(
15 | 0 -> 3,
16 | 1 -> 2,
17 | 4 -> 4,
18 | 6 -> 4
19 | )
20 |
21 | test("Parse input") {
22 | assert(parseLayers(input.split("\n").toIndexedSeq) == layers)
23 | }
24 |
25 | val examples =
26 | Table(
27 | ("Layers", "Severity", "Delay"),
28 | (layers, 24, 10)
29 | )
30 |
31 | test("Day13 - Part 1") {
32 | forAll(examples) { (layers, severity, _) =>
33 | assert(part1(layers) == severity)
34 | }
35 | }
36 |
37 | test("Day13 - Part 2") {
38 | forAll(examples) { (layers, _, delay) =>
39 | assert(part2(layers) == delay)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day14Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day14.{preProcess, part1, part2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day14Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val examples =
9 | Table(
10 | ("Key", "Used", "Regions"),
11 | ("flqrgnkx", 8108, 1242)
12 | )
13 |
14 | test("Day14 - Part 1") {
15 | forAll(examples) { (key, used, _) =>
16 | assert(part1(preProcess(key)) == used)
17 | }
18 | }
19 |
20 | test("Day14 - Part 2") {
21 | forAll(examples) { (key, _, regions) =>
22 | assert(part2(preProcess(key)) == regions)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day15Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day15.{part1, part2}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day15Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val examples =
9 | Table(
10 | ("StartA", "StartB", "Count1", "Count2"),
11 | (65L, 8921L, 588L, 309L)
12 | )
13 |
14 | test("Day15 - Part 1") {
15 | forAll(examples) { (startA, startB, count, _) =>
16 | assert(part1((startA, startB)) == count)
17 | }
18 | }
19 |
20 | test("Day15 - Part 2") {
21 | forAll(examples) { (startA, startB, _, count) =>
22 | assert(part2((startA, startB)) == count)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day16Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day16Definitions._
4 | import com.lmat.adventofcode.year2017.Day16.{dance, parseMove}
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day16Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val rawMoves: String = "s1,x3/4,pe/b"
11 | val moves: Seq[Move] = Seq(Spin(1), Exchange(3, 4), Partner('e', 'b'))
12 |
13 | test("Parse moves") {
14 | assert(rawMoves.split(",").toSeq.flatMap(parseMove) == moves)
15 | }
16 |
17 | test("Dance") {
18 | assert(dance("abcde", moves) == "baedc")
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day17Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day17.{State, nextBuffer, nextState}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day17Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val testStates = Seq(
9 | (State(0, 0), List(0)),
10 | (State(1, 1), List(0, 1)),
11 | (State(1, 2), List(0, 2, 1)),
12 | (State(2, 3), List(0, 2, 3, 1)),
13 | (State(2, 4), List(0, 2, 4, 3, 1)),
14 | (State(1, 5), List(0, 5, 2, 4, 3, 1)),
15 | (State(5, 6), List(0, 5, 2, 4, 3, 6, 1)),
16 | (State(2, 7), List(0, 5, 7, 2, 4, 3, 6, 1)),
17 | (State(6, 8), List(0, 5, 7, 2, 4, 3, 8, 6, 1)),
18 | (State(1, 9), List(0, 9, 5, 7, 2, 4, 3, 8, 6, 1)))
19 |
20 | test("Test Spinlock states") {
21 | val states = (0 until 9).scanLeft((State(0, 0), Seq(0))){ case ((state, buffer), _) =>
22 | val next = nextState(state, 3)
23 | (next, nextBuffer(buffer, next))
24 | }
25 |
26 | assert(states == testStates)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day18Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day18._
4 | import com.lmat.adventofcode.year2017.Day18Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day18Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val rawInstructions1 =
11 | """set a 1
12 | |add a 2
13 | |mul a a
14 | |mod a 5
15 | |snd a
16 | |set a 0
17 | |rcv a
18 | |jgz a -1
19 | |set a 1
20 | |jgz a -2
21 | """.stripMargin
22 |
23 | val instructions1 = Seq(
24 | Set("a","1"),
25 | Add("a","2"),
26 | Multiply("a","a"),
27 | Modulo("a","5"),
28 | Sound("a"),
29 | Set("a","0"),
30 | Recover("a"),
31 | Jump("a","-1"),
32 | Set("a","1"),
33 | Jump("a","-2")
34 | )
35 |
36 | test("Parse 1") {
37 | assert(rawInstructions1.split("\n").flatMap(row => parseInstruction(row)).toSeq == instructions1)
38 | }
39 |
40 | test("Day18 - Part 1") {
41 | assert(part1(instructions1) == 4)
42 | }
43 |
44 | val rawInstructions2 =
45 | """snd 1
46 | |snd 2
47 | |snd p
48 | |rcv a
49 | |rcv b
50 | |rcv c
51 | |rcv d
52 | """.stripMargin
53 |
54 | val instructions2 = Seq(
55 | Send("1"),
56 | Send("2"),
57 | Send("p"),
58 | Receive("a"),
59 | Receive("b"),
60 | Receive("c"),
61 | Receive("d")
62 | )
63 |
64 | test("Parse 2") {
65 | assert(rawInstructions2.split("\n").flatMap(row => parseInstruction(row, true)).toSeq == instructions2)
66 | }
67 |
68 | test("Day18 - Part 2") {
69 | assert(part2(instructions2) == 3)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day20Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day20.{parseParticle, part1, part2}
4 | import com.lmat.adventofcode.year2017.Day20Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day20Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val rawParticles =
10 | """p=<3,0,0>, v=<2,0,0>, a=<-1,0,0>
11 | |p=<4,0,0>, v=<0,0,0>, a=<-2,0,0>
12 | """.stripMargin
13 |
14 | val particles = Seq(
15 | Particle(0, Coordinates(3, 0, 0), Coordinates(2, 0, 0), Coordinates(-1, 0, 0)),
16 | Particle(1, Coordinates(4, 0, 0), Coordinates(0, 0, 0), Coordinates(-2, 0, 0))
17 | )
18 |
19 | test("Parse 1") {
20 | assert(rawParticles.split("\n").zipWithIndex.toSeq.flatMap { case (row, i) => parseParticle(row, i) } == particles)
21 | }
22 |
23 | test("Day20 - Part 1") {
24 | assert(part1(particles) == 0)
25 | }
26 |
27 | val rawParticles2 =
28 | """p=<-6,0,0>, v=<3,0,0>, a=<0,0,0>
29 | |p=<-4,0,0>, v=<2,0,0>, a=<0,0,0>
30 | |p=<-2,0,0>, v=<1,0,0>, a=<0,0,0>
31 | |p=<3,0,0>, v=<1,0,0>, a=<0,0,0>
32 | """.stripMargin
33 |
34 | val particles2 = Seq(
35 | Particle(0, Coordinates(-6, 0, 0), Coordinates(3, 0, 0), Coordinates(0, 0, 0)),
36 | Particle(1, Coordinates(-4, 0, 0), Coordinates(2, 0, 0), Coordinates(0, 0, 0)),
37 | Particle(2, Coordinates(-2, 0, 0), Coordinates(1, 0, 0), Coordinates(0, 0, 0)),
38 | Particle(3, Coordinates(3, 0, 0), Coordinates(1, 0, 0), Coordinates(0, 0, 0))
39 | )
40 |
41 | test("Parse 2") {
42 | assert(rawParticles2.split("\n").zipWithIndex.toSeq.flatMap { case (row, i) => parseParticle(row, i) } == particles2)
43 | }
44 |
45 | test("Day20 - Part 2") {
46 | assert(part2(particles2) == 1)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day22Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day22._
4 | import com.lmat.adventofcode.year2017.Day22Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day22Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val rawGrid =
11 | """..#
12 | |#..
13 | |...""".stripMargin
14 |
15 | val grid: Map[Position, String] = Map(
16 | Position(-1, 1) -> "clean", Position(0, 1) -> "clean", Position(1, 1) -> "infected",
17 | Position(-1, 0) -> "infected", Position(0, 0) -> "clean", Position(1, 0) -> "clean",
18 | Position(-1, -1) -> "clean", Position(0, -1) -> "clean", Position(1, -1) -> "clean",
19 | )
20 |
21 | test("Parse") {
22 | assert(parseMap(rawGrid.split("\n").toIndexedSeq.map(_.toIndexedSeq)) == grid)
23 | }
24 |
25 | val viruses =
26 | Table(
27 | ("evolved", "bursts", "count"),
28 | (false, 7, 5),
29 | (false, 70, 41),
30 | (false, 10000, 5587),
31 | (true, 100, 26)
32 | )
33 |
34 | forAll(viruses) { (evolved, bursts, count) =>
35 | test(s"$count infected bursts out of $bursts if the virus is evolved=$evolved") {
36 | val result =
37 | if (evolved) countInfectiousBursts(grid, decideOnDirectionV2, decideNodeStateV2, bursts)
38 | else countInfectiousBursts(grid, decideOnDirection, decideNodeState, bursts)
39 | assert(count == result)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day23Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day23.parseInstruction
4 | import com.lmat.adventofcode.year2017.Day23Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day23Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val rawInstructions =
10 | """set b 99
11 | |set c b
12 | |jnz a 2
13 | |jnz 1 5
14 | |mul b 100
15 | |sub b -100000
16 | |set c b
17 | |sub c -17000
18 | |set f 1
19 | |set d 2
20 | |set e 2
21 | |set g d
22 | |mul g e
23 | |sub g b
24 | |jnz g 2
25 | |set f 0
26 | |sub e -1
27 | |set g e
28 | |sub g b
29 | |jnz g -8
30 | |sub d -1
31 | |set g d
32 | |sub g b
33 | |jnz g -13
34 | |jnz f 2
35 | |sub h -1
36 | |set g b
37 | |sub g c
38 | |jnz g 2
39 | |jnz 1 3
40 | |sub b -17
41 | |jnz 1 -23
42 | """.stripMargin
43 |
44 | val instructions = Seq(
45 | Set("b", "99"),
46 | Set("c", "b"),
47 | Jump("a", "2"),
48 | Jump("1", "5"),
49 | Multiply("b", "100"),
50 | Subtract("b", "-100000"),
51 | Set("c", "b"),
52 | Subtract("c", "-17000"),
53 | Set("f", "1"),
54 | Set("d", "2"),
55 | Set("e", "2"),
56 | Set("g", "d"),
57 | Multiply("g", "e"),
58 | Subtract("g", "b"),
59 | Jump("g", "2"),
60 | Set("f", "0"),
61 | Subtract("e", "-1"),
62 | Set("g", "e"),
63 | Subtract("g", "b"),
64 | Jump("g", "-8"),
65 | Subtract("d", "-1"),
66 | Set("g", "d"),
67 | Subtract("g", "b"),
68 | Jump("g", "-13"),
69 | Jump("f", "2"),
70 | Subtract("h", "-1"),
71 | Set("g", "b"),
72 | Subtract("g", "c"),
73 | Jump("g", "2"),
74 | Jump("1", "3"),
75 | Subtract("b", "-17"),
76 | Jump("1", "-23")
77 | )
78 |
79 | test("Parse") {
80 | assert(rawInstructions.split("\n").flatMap(row => parseInstruction(row)).toSeq == instructions)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Day25Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.year2017.Day25._
4 | import com.lmat.adventofcode.year2017.Day25Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day25Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val rawConfig =
10 | """Begin in state A.
11 | |Perform a diagnostic checksum after 6 steps.
12 | |
13 | |In state A:
14 | | If the current value is 0:
15 | | - Write the value 1.
16 | | - Move one slot to the right.
17 | | - Continue with state B.
18 | | If the current value is 1:
19 | | - Write the value 0.
20 | | - Move one slot to the left.
21 | | - Continue with state B.
22 | |
23 | |In state B:
24 | | If the current value is 0:
25 | | - Write the value 1.
26 | | - Move one slot to the left.
27 | | - Continue with state A.
28 | | If the current value is 1:
29 | | - Write the value 1.
30 | | - Move one slot to the right.
31 | | - Continue with state A.""".stripMargin
32 |
33 | val config = TuringMachineConfig("A", 6, Map(
34 | "A" -> State("A", Instruction(1, "right", "B"), Instruction(0, "left", "B")),
35 | "B" -> State("B", Instruction(1, "left", "A"), Instruction(1, "right", "A")))
36 | )
37 |
38 | test("Parse") {
39 | assert(parseInstructions(rawConfig.split("\n").toIndexedSeq) == config)
40 | }
41 |
42 | test("Day 25 - Part 1") {
43 | assert(part1(config) == 3)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2017/Year2017Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2017
2 |
3 | import com.lmat.adventofcode.PuzzleRunner.{puzzleMap, resource}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Year2017Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val puzzles =
10 | Table(
11 | ("day", "part1", "part2"),
12 | (1, 1390, 1232),
13 | (2, 37923, 263),
14 | (3, 480, 349975),
15 | (4, 325, 119),
16 | (5, 339351, 24315397),
17 | (6, 12841, 8038),
18 | (7, "airlri", 1206),
19 | (8, 6611, 6619),
20 | (9, 11089, 5288),
21 | (10, 11413, "7adfd64c2a03a4968cf708d1b7fd418d"),
22 | (11, 698, 1435),
23 | (12, 283, 195),
24 | (13, 1624, 3923436),
25 | (14, 8204, 1089),
26 | (15, 609, 253),
27 | (16, "doeaimlbnpjchfkg", "agndefjhibklmocp"),
28 | (17, 1244, 11162912),
29 | (18, 9423, 7620),
30 | (19, "SXPZDFJNRL", 18126),
31 | (20, 308, 504),
32 | (21, 162, 2264586),
33 | (22, 5411, 2511416),
34 | (23, 9409, 913),
35 | (24, 1511, 1471),
36 | (25, 4217, ())
37 | )
38 |
39 | forAll(puzzles) { (day, part1, part2) =>
40 | val year = 2017
41 |
42 | test(s"$year: Day $day") {
43 | val (res1, res2) = puzzleMap(year, day).solve(resource(year, day))
44 | assert(res1 == part1)
45 | assert(res2 == part2)
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day01Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day01._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day01Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val frequencies =
9 | Table(
10 | ("frequencies", "result"),
11 | (Seq(+1, +1, +1), 3),
12 | (Seq(+1, +1, -2), 0),
13 | (Seq(-1, -2, -3), -6),
14 | )
15 |
16 | test("Day01 - Part 1") {
17 | forAll(frequencies) { (frequencies, result) =>
18 | assert(part1(frequencies) == result)
19 | }
20 | }
21 |
22 | val frequencies2 =
23 | Table(
24 | ("frequencies", "result"),
25 | (Seq(+1, -1), 0),
26 | (Seq(+3, +3, +4, -2, -4), 10),
27 | (Seq(-6, +3, +8, +5, -6), 5),
28 | (Seq(+7, +7, -2, -7, -4), 14),
29 | )
30 |
31 | test("Day01 - Part 2") {
32 | forAll(frequencies2) { (frequencies, result) =>
33 | assert(part2(frequencies) == result)
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day02Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 | import com.lmat.adventofcode.year2018.Day02._
6 |
7 | class Day02Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val boxIDs = Seq(
10 | "abcdef",
11 | "bababc",
12 | "abbcde",
13 | "abcccd",
14 | "aabcdd",
15 | "abcdee",
16 | "ababab"
17 | )
18 |
19 | test("Day02 - Part 1") {
20 | assert(part1(boxIDs) == 12)
21 | }
22 |
23 | val boxIDs2 = Seq(
24 | "abcde",
25 | "fghij",
26 | "klmno",
27 | "pqrst",
28 | "fguij",
29 | "axcye",
30 | "wvxyz"
31 | )
32 |
33 | test("Day02 - Part 2") {
34 | assert(part2(boxIDs2) == "fgij")
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day03Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day03Definitions._
4 | import com.lmat.adventofcode.year2018.Day03._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day03Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 | val rawClaims =
10 | s"""#1 @ 1,3: 4x4
11 | |#2 @ 3,1: 4x4
12 | |#3 @ 5,5: 2x2""".stripMargin
13 |
14 | val claims = Seq(
15 | Claim(1, 1, 3, 4, 4),
16 | Claim(2, 3, 1, 4, 4),
17 | Claim(3, 5, 5, 2, 2))
18 |
19 | test("Day03 - Parse") {
20 | assert(rawClaims.split("\n").toSeq.flatMap(parseClaim) == claims)
21 | }
22 |
23 | val coverMap = Map(
24 | Pixel(3,1) -> 1, Pixel(4,1) -> 1, Pixel(5,1) -> 1, Pixel(6,1) -> 1,
25 | Pixel(3,2) -> 1, Pixel(4,2) -> 1, Pixel(5,2) -> 1, Pixel(6,2) -> 1,
26 | Pixel(1,3) -> 1, Pixel(2,3) -> 1, Pixel(3,3) -> 2, Pixel(4,3) -> 2, Pixel(5,3) -> 1, Pixel(6,3) -> 1,
27 | Pixel(1,4) -> 1, Pixel(2,4) -> 1, Pixel(3,4) -> 2, Pixel(4,4) -> 2, Pixel(5,4) -> 1, Pixel(6,4) -> 1,
28 | Pixel(1,5) -> 1, Pixel(2,5) -> 1, Pixel(3,5) -> 1, Pixel(4,5) -> 1, Pixel(5,5) -> 1, Pixel(6,5) -> 1,
29 | Pixel(1,6) -> 1, Pixel(2,6) -> 1, Pixel(3,6) -> 1, Pixel(4,6) -> 1, Pixel(5,6) -> 1, Pixel(6,6) -> 1)
30 |
31 | test("Day03 - Build Cover Map") {
32 | assert(buildCoverMap(claims) == coverMap)
33 | }
34 |
35 | test("Day03 - Part 1") {
36 | assert(part1(SuitPlan(claims, coverMap)) == 4)
37 | }
38 |
39 | test("Day03 - Part 2") {
40 | assert(part2(SuitPlan(claims, coverMap)) == 3)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day05Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day05._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day05Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val polymers =
9 | Table(
10 | ("polymer", "reacted", "part1"),
11 | ("aA", "", 0),
12 | ("abBA", "", 0),
13 | ("abAB", "abAB", 4),
14 | ("aabAAB", "aabAAB", 6),
15 | ("dabAcCaCBAcCcaDA", "dabCBAcaDA", 10),
16 | )
17 |
18 | test("Day05 - Reaction") {
19 | forAll(polymers) { (polymer, reacted, _) =>
20 | assert(react(polymer) == reacted)
21 | }
22 | }
23 |
24 | test("Day05 - Part 1") {
25 | forAll(polymers) { (polymer, _, result) =>
26 | assert(part1(preProcess(polymer)) == result)
27 | }
28 | }
29 |
30 | test("Day05 - Part 2") {
31 | assert(part2(preProcess("dabAcCaCBAcCcaDA")) == 4)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day06Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day06._
4 | import com.lmat.adventofcode.year2018.Day06Definitions._
5 |
6 | import org.scalatest.funsuite.AnyFunSuite
7 | import org.scalatest.prop.TableDrivenPropertyChecks
8 |
9 | class Day06Test extends AnyFunSuite with TableDrivenPropertyChecks {
10 |
11 | val rawCoordinates =
12 | """1, 1
13 | |1, 6
14 | |8, 3
15 | |3, 4
16 | |5, 5
17 | |8, 9""".stripMargin
18 |
19 | val coordinates = Seq(
20 | Coordinate(1, 1),
21 | Coordinate(1, 6),
22 | Coordinate(8, 3),
23 | Coordinate(3, 4),
24 | Coordinate(5, 5),
25 | Coordinate(8, 9))
26 |
27 | test("Day06 - Parse") {
28 | assert(rawCoordinates.split("\n").flatMap(parseCoordinate).toSeq == coordinates)
29 | }
30 |
31 | test("Day06 - Part 1") {
32 | assert(part1(coordinates) == 17)
33 | }
34 |
35 | test("Day06 - Part 2") {
36 | assert(countDistanceLessThan(32)(coordinates) == 16)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day07Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day07._
4 | import com.lmat.adventofcode.year2018.Day07Definitions._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day07Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val rawInstructions =
11 | """Step C must be finished before step A can begin.
12 | |Step C must be finished before step F can begin.
13 | |Step A must be finished before step B can begin.
14 | |Step A must be finished before step D can begin.
15 | |Step B must be finished before step E can begin.
16 | |Step D must be finished before step E can begin.
17 | |Step F must be finished before step E can begin.""".stripMargin
18 |
19 | val instructions = Seq(
20 | Instruction('C', 'A'),
21 | Instruction('C', 'F'),
22 | Instruction('A', 'B'),
23 | Instruction('A', 'D'),
24 | Instruction('B', 'E'),
25 | Instruction('D', 'E'),
26 | Instruction('F', 'E'))
27 |
28 | test("Day07 - Parse") {
29 | assert(rawInstructions.split("\n").flatMap(parseInstruction).toSeq == instructions)
30 | }
31 |
32 | test("Day07 - Part 1") {
33 | assert(part1(instructions) == "CABDFE")
34 | }
35 |
36 | test("Day07 - Time Map") {
37 | val timeM = timeMap(0)
38 | assert(timeM('A') == 1)
39 | assert(timeM('B') == 2)
40 | assert(timeM('Z') == 26)
41 | }
42 |
43 | test("Day07 - Part 2") {
44 | assert(completionTime(0, 2)(instructions) == 15)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day08Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day08Definitions._
4 | import com.lmat.adventofcode.year2018.Day08._
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.prop.TableDrivenPropertyChecks
7 |
8 | class Day08Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val numbers = Seq(2, 3, 0, 3, 10, 11, 12, 1, 1, 0, 1, 99, 2, 1, 1, 2)
11 | val tree =
12 | Node(Seq(
13 | Node(Seq(), Seq(10, 11, 12)),
14 | Node(Seq(
15 | Node(Seq(), Seq(99))),
16 | Seq(2))),
17 | Seq(1, 1, 2))
18 |
19 | test("Day08 - Build Tree") {
20 | assert(buildTree(numbers) == tree)
21 | }
22 |
23 | test("Day08 - Part 1") {
24 | assert(part1(tree) == 138)
25 | }
26 |
27 | test("Day08 - Part 2") {
28 | assert(part2(tree) == 66)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day09Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 | import com.lmat.adventofcode.year2018.Day09Definitions._
6 | import com.lmat.adventofcode.year2018.Day09._
7 |
8 |
9 | class Day09Test extends AnyFunSuite with TableDrivenPropertyChecks {
10 | val games =
11 | Table(
12 | ("gameDefinition", "maxScore"),
13 | (GameDefinition(9, 25), 32),
14 | (GameDefinition(10, 1618), 8317),
15 | (GameDefinition(13, 7999), 146373),
16 | (GameDefinition(17, 1104), 2764),
17 | (GameDefinition(21, 6111), 54718),
18 | (GameDefinition(30, 5807), 37305)
19 | )
20 |
21 | test("Day09 - Play Game") {
22 | forAll(games) { (gameDefinition, maxScore) =>
23 | assert(part1(gameDefinition) == maxScore)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day11Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day11._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day11Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val cells =
9 | Table(
10 | ("x", "y", "serial number", "power"),
11 | (3, 5, 8, 4),
12 | (122, 79, 57, -5),
13 | (217, 196, 39, 0),
14 | (101, 153, 71, 4),
15 | )
16 |
17 | test("Day11 - Calculate Power Level") {
18 | forAll(cells) { (x, y, serial, power) =>
19 | assert(calculatePowerLevel(serial)(x, y) == power)
20 | }
21 | }
22 |
23 | test("Day11 - Part 1") {
24 | assert(part1(42) == "21,61")
25 | }
26 |
27 | test("Day11 - Part 2") {
28 | assert(part2(18) == "90,269,16")
29 | assert(part2(42) == "232,251,12")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day14Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day14._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day14Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val recipes =
9 | Table(
10 | ("number", "score"),
11 | (5, "0124515891"),
12 | (18, "9251071085"),
13 | (2018, "5941429882")
14 | )
15 |
16 | test("Day14 - Part 1") {
17 | forAll(recipes) { (number, score) =>
18 | assert(part1(number) == score)
19 | }
20 | }
21 |
22 | val recipes2 =
23 | Table(
24 | ("number", "index"),
25 | (51589, 9),
26 | (92510, 18),
27 | (59414, 2018)
28 | )
29 |
30 | test("Day14 - Part 2") {
31 | forAll(recipes2) { (number, index) =>
32 | assert(part2(number) == index)
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day19Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.year2018.Day16Definitions.Instruction
4 | import com.lmat.adventofcode.year2018.Day19Definitions._
5 | import com.lmat.adventofcode.year2018.Day19._
6 | import org.scalatest.funsuite.AnyFunSuite
7 | import org.scalatest.prop.TableDrivenPropertyChecks
8 |
9 | class Day19Test extends AnyFunSuite with TableDrivenPropertyChecks {
10 |
11 | val rawProgram: String =
12 | """#ip 0
13 | |seti 5 0 1
14 | |seti 6 0 2
15 | |addi 0 1 0
16 | |addr 1 2 3
17 | |setr 1 0 0
18 | |seti 8 0 4
19 | |seti 9 0 5""".stripMargin
20 |
21 | val program = Program(0, List(
22 | Instruction("seti", 5, 0, 1),
23 | Instruction("seti", 6, 0, 2),
24 | Instruction("addi", 0, 1, 0),
25 | Instruction("addr", 1, 2, 3),
26 | Instruction("setr", 1, 0, 0),
27 | Instruction("seti", 8, 0, 4),
28 | Instruction("seti", 9, 0, 5)))
29 |
30 | test("Parse"){
31 | assert(parseProgram(rawProgram.split("\n").toIndexedSeq).get == program)
32 | }
33 |
34 | test("Part 1"){
35 | assert(part1(program) == 7)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Day24Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 | import com.lmat.adventofcode.year2018.Day24Definitions._
6 | import com.lmat.adventofcode.year2018.Day24._
7 |
8 | class Day24Test extends AnyFunSuite with TableDrivenPropertyChecks {
9 |
10 | val rawGroupConfiguration =
11 | """Immune System:
12 | |17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
13 | |989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
14 | |
15 | |Infection:
16 | |801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
17 | |4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4""".stripMargin
18 |
19 | val groupConfiguration = Map(
20 | ("immune", 0) -> Group(17, 5390, 4507, "fire", 2, Seq("radiation", "bludgeoning"), Seq()),
21 | ("immune", 1) -> Group(989, 1274, 25, "slashing", 3, Seq("bludgeoning", "slashing"), Seq("fire")),
22 | ("infect", 0) -> Group(801, 4706, 116, "bludgeoning", 1, Seq("radiation"), Seq()),
23 | ("infect", 1) -> Group(4485, 2961, 12, "slashing", 4, Seq("fire", "cold"), Seq("radiation"))
24 | )
25 |
26 | test("Day24 - Parse") {
27 | assert(parseGroupConfiguration(rawGroupConfiguration.split("\n").toIndexedSeq) == groupConfiguration)
28 | }
29 |
30 | test("Day24 - Part 1") {
31 | assert(part1(groupConfiguration) == 5216)
32 | }
33 |
34 | test("Day24 - Part 2") {
35 | assert(part2(groupConfiguration) == 51)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2018/Year2018Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2018
2 |
3 | import com.lmat.adventofcode.PuzzleRunner.{puzzleMap, resource}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Year2018Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val puzzles =
10 | Table(
11 | ("day", "part1", "part2"),
12 | (1, 540, 73056),
13 | (2, 5658, "nmgyjkpruszlbaqwficavxneo"),
14 | (3, 103482, 686),
15 | (4, 142515, 5370),
16 | (5, 11754, 4098),
17 | (6, 3569, 48978),
18 | (7, "CGKMUWXFAIHSYDNLJQTREOPZBV", 1046),
19 | (8, 37905, 33891),
20 | (9, 390093L, 3150377341L),
21 | (10, "JJXZHKFP", 10036),
22 | (11, "243,34", "90,214,15"),
23 | (12, 3421, 2550000001195L),
24 | (13, "109,23", "137,101"),
25 | (14, "5832873106", 20273708),
26 | (15, 243390, 59886),
27 | (16, 642, 481),
28 | (17, 31667, 25018),
29 | (18, 594712, 203138),
30 | (19, 1152, 12690000),
31 | (24, 19974, 4606)
32 | )
33 |
34 | forAll(puzzles) { (day, part1, part2) =>
35 | val year = 2018
36 |
37 | test(s"$year: Day $day") {
38 | val (res1, res2) = puzzleMap(year, day).solve(resource(year, day))
39 | assert(res1 == part1)
40 | assert(res2 == part2)
41 | }
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2019/Day01Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2019
2 |
3 | import com.lmat.adventofcode.year2019.Day01._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day01Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val masses =
9 | Table(
10 | ("mass", "result", "result2"),
11 | (12, 2, 2),
12 | (14, 2, 2),
13 | (1969, 654, 966),
14 | (100756, 33583, 50346)
15 | )
16 |
17 | test("Day01 - Part 1") {
18 | forAll(masses) { (mass, result, _) =>
19 | assert(fuel(mass) == result)
20 | }
21 | }
22 |
23 | test("Day01 - Part 2") {
24 | forAll(masses) { (mass, _, result) =>
25 | assert(fuel2(mass) == result)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2019/Day02Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2019
2 |
3 | import com.lmat.adventofcode.year2019.Day02._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day02Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val intCodes =
9 | Table(
10 | ("source", "result"),
11 | (Vector(1,0,0,0,99), Vector(2,0,0,0,99)),
12 | (Vector(2,3,0,3,99), Vector(2,3,0,6,99)),
13 | (Vector(2,4,4,5,99,0), Vector(2,4,4,5,99,9801)),
14 | (Vector(1,1,1,4,99,5,6,0,99), Vector(30,1,1,4,2,5,6,0,99)),
15 | )
16 |
17 | test("Day02 - Solve") {
18 | forAll(intCodes) { (source, result) =>
19 | assert(solve(source) == result)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2019/Day04Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2019
2 |
3 | import com.lmat.adventofcode.year2019.Day04._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Day04Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val numbers =
9 | Table(
10 | ("number", "monotonicallyIncreasing", "hasTwoAdjacent", "hasTwoStandAloneAdjacent"),
11 | (111111, true, true, false),
12 | (223450, false, true, true),
13 | (123789, true, false, false),
14 | (112233, true, true, true),
15 | (123444, true, true, false),
16 | (111122, true, true, true),
17 | )
18 |
19 | test("Day04 - Tests") {
20 | forAll(numbers) { (n, m, ad, ad2) =>
21 | val digits = toDigits(n)
22 | assert(monotonicallyIncreasing(digits) == m)
23 | assert(hasTwoAdjacent(digits) == ad)
24 | assert(hasTwoStandAloneAdjacent(digits) == ad2)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2019/Year2019Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2019
2 | import com.lmat.adventofcode.PuzzleRunner.{puzzleMap, resource}
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 |
6 | class Year2019Test extends AnyFunSuite with TableDrivenPropertyChecks {
7 |
8 | val puzzles =
9 | Table(
10 | ("day", "part1", "part2"),
11 | (1, 3315133, 4969831),
12 | (2, 6568671, 3951),
13 | (3, 1211, 101386),
14 | (4, 1640, 1126),
15 | (5, 5182797, 12077198),
16 | (6, 117672, 277)
17 | )
18 |
19 | forAll(puzzles) { (day, part1, part2) =>
20 | val year = 2019
21 |
22 | test(s"$year: Day $day") {
23 | val (res1, res2) = puzzleMap(year, day).solve(resource(year, day))
24 | assert(res1 == part1)
25 | assert(res2 == part2)
26 | }
27 | }
28 | }
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2020/Day01Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2020
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2020.Day01._
5 |
6 | class Day01Test extends AnyFunSuite {
7 | val expenses = Array(1721, 979, 366, 299, 675, 1456)
8 |
9 | test("sumsUpTo") {
10 | assert(sumsUpTo(expenses.sorted)(2020).get == (299, 1721))
11 | assert(sumsThreeUpTo(expenses.sorted)(2020).get == (366, 675, 979))
12 | }
13 |
14 | test("part1") {
15 | assert(part1(expenses) == 514579)
16 | }
17 |
18 | test("part2") {
19 | assert(part2(expenses) == 241861950)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2020/Day02Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2020
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2020.Day02._
5 | import com.lmat.adventofcode.year2020.Day02Definitions._
6 |
7 | class Day02Test extends AnyFunSuite {
8 | val passwordsRaw =
9 | """1-3 a: abcde
10 | |1-3 b: cdefg
11 | |2-9 c: ccccccccc""".stripMargin
12 |
13 | val passwords = List(
14 | (Policy('a', 1, 3), "abcde"),
15 | (Policy('b', 1, 3), "cdefg"),
16 | (Policy('c', 2, 9), "ccccccccc")
17 | )
18 |
19 | test("parse") {
20 | assert(passwordsRaw.split("\n").flatMap(parsePolicyRow).toList == passwords)
21 | }
22 |
23 | test("part1") {
24 | assert(part1(passwords) == 2)
25 | }
26 |
27 | test("part2") {
28 | assert(part2(passwords) == 1)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2020/Year2020Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2020
2 |
3 | import com.lmat.adventofcode.PuzzleRunner.{puzzleMap, resource}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Year2020Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val puzzles =
9 | Table(
10 | ("day", "part1", "part2"),
11 | (1, 980499, 200637446),
12 | (2, 483, 482),
13 | (7, 242, 176035)
14 | )
15 |
16 | forAll(puzzles) { (day, part1, part2) =>
17 | val year = 2020
18 |
19 | test(s"$year: Day $day") {
20 | val (res1, res2) = puzzleMap(year, day).solve(resource(year, day))
21 | assert(res1 == part1)
22 | assert(res2 == part2)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day01Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2021.Day01._
5 |
6 | class Day01Test extends AnyFunSuite {
7 | val measurements = Seq(199, 200, 208, 210, 200, 207, 240, 269, 260, 263)
8 |
9 | test("part1") {
10 | assert(part1(measurements) == 7)
11 | }
12 |
13 | test("part2") {
14 | assert(part2(measurements) == 5)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day02Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2021.Day02._
5 | import com.lmat.adventofcode.year2021.Day02Definitions._
6 |
7 | class Day02Test extends AnyFunSuite {
8 | val rawCommands =
9 | """forward 5
10 | |down 5
11 | |forward 8
12 | |up 3
13 | |down 8
14 | |forward 2""".stripMargin
15 |
16 | val commands = List(
17 | Forward(5),
18 | Down(5),
19 | Forward(8),
20 | Up(3),
21 | Down(8),
22 | Forward(2)
23 | )
24 |
25 | test("parse") {
26 | assert(rawCommands.split("\n").flatMap(parseCommand).toList == commands)
27 | }
28 |
29 | test("move1") {
30 | assert(commands.foldLeft(start)(move1) == Position(15, 10, 0))
31 | }
32 |
33 | test("part1") {
34 | assert(part1(commands) == 150)
35 | }
36 |
37 | test("move2") {
38 | assert(commands.foldLeft(start)(move2)== Position(15, 60, 10))
39 | }
40 |
41 | test("part2") {
42 | assert(part2(commands) == 900)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day03Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2021.Day03._
5 |
6 | class Day03Test extends AnyFunSuite {
7 | val diagnostics = List(
8 | "00100",
9 | "11110",
10 | "10110",
11 | "10111",
12 | "10101",
13 | "01111",
14 | "00111",
15 | "11100",
16 | "10000",
17 | "11001",
18 | "00010",
19 | "01010"
20 | )
21 |
22 | test("gammaRate") {
23 | assert(gammaRate(diagnostics) == "10110")
24 | }
25 |
26 | test("epsilonRate") {
27 | assert(epsilonRate(diagnostics) == "01001")
28 | }
29 |
30 | test("powerConsumption") {
31 | assert(powerConsumption(diagnostics) == 198)
32 | }
33 |
34 | test("part1") {
35 | assert(part1(diagnostics) == 198)
36 | }
37 |
38 | test("oxygenGeneratorRating") {
39 | assert(oxygenGeneratorRating(diagnostics) == "10111")
40 | }
41 |
42 | test("co2ScrubberRating") {
43 | assert(co2ScrubberRating(diagnostics) == "01010")
44 | }
45 |
46 | test("lifeSupportRating") {
47 | assert(lifeSupportRating(diagnostics) == 230)
48 | }
49 |
50 | test("part2") {
51 | assert(part2(diagnostics) == 230)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day05Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.year2021.Day05Definitions._
4 | import com.lmat.adventofcode.year2021.Day05._
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | class Day05Test extends AnyFunSuite {
8 | val raw =
9 | """0,9 -> 5,9
10 | |8,0 -> 0,8
11 | |9,4 -> 3,4
12 | |2,2 -> 2,1
13 | |7,0 -> 7,4
14 | |6,4 -> 2,0
15 | |0,9 -> 2,9
16 | |3,4 -> 1,4
17 | |0,0 -> 8,8
18 | |5,5 -> 8,2""".stripMargin
19 |
20 | val lines =
21 | List(
22 | Line(Coordinate(0, 9), Coordinate(5, 9)),
23 | Line(Coordinate(8, 0), Coordinate(0, 8)),
24 | Line(Coordinate(9, 4), Coordinate(3, 4)),
25 | Line(Coordinate(2, 2), Coordinate(2, 1)),
26 | Line(Coordinate(7, 0), Coordinate(7, 4)),
27 | Line(Coordinate(6, 4), Coordinate(2, 0)),
28 | Line(Coordinate(0, 9), Coordinate(2, 9)),
29 | Line(Coordinate(3, 4), Coordinate(1, 4)),
30 | Line(Coordinate(0, 0), Coordinate(8, 8)),
31 | Line(Coordinate(5, 5), Coordinate(8, 2))
32 | )
33 |
34 | test("parseLines") {
35 | assert(raw.split("\n").flatMap(parseLine).toList == lines)
36 | }
37 |
38 | test("part1") {
39 | assert(part1(lines) == 5)
40 | }
41 |
42 | test("part2") {
43 | assert(part2(lines) == 12)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day06Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.year2021.Day06._
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class Day06Test extends AnyFunSuite {
7 | test("simulate") {
8 | assert(simulateAt(7, 2)(List(3, 4, 3, 1, 2), 1) ==
9 | Map(0 -> 1, 1 -> 1, 2 -> 2, 3 -> 1))
10 | assert(simulateAt(7, 2)(List(3, 4, 3, 1, 2), 18) ==
11 | Map(0 -> 3, 1 -> 5, 2 -> 3, 3 -> 2, 4 -> 2, 5 -> 1, 6 -> 5, 7 -> 1, 8 -> 4))
12 | assert(simulateAt(7, 2)(List(3, 4, 3, 1, 2), 80) ==
13 | Map(0 -> 424, 1 -> 729, 2 -> 558, 3 -> 790, 4 -> 739, 5 -> 762, 6 -> 991, 7 -> 370, 8 -> 571))
14 | assert(simulateAt(7, 2)(List(3, 4, 3, 1, 2), 256) ==
15 | Map(0 -> 2376852196L, 1 -> 2731163883L, 2 -> 2897294544L, 3 -> 3164316379L, 4 -> 3541830408L, 5 -> 3681986557L, 6 -> 4275812629L, 7 -> 1985489551L, 8 -> 2329711392L))
16 | }
17 |
18 | test("part1") {
19 | assert(part1(List(3, 4, 3, 1, 2)) == 5934)
20 | }
21 |
22 | test("part2") {
23 | assert(part2(List(3, 4, 3, 1, 2)) == 26984457539L)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day07Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.year2021.Day07._
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class Day07Test extends AnyFunSuite {
7 | test("align") {
8 | assert(align(List(16, 1, 2, 0, 4, 2, 7, 1, 2, 14), identity) == (2, 37))
9 | assert(align(List(16, 1, 2, 0, 4, 2, 7, 1, 2, 14), steps => (1 to steps).sum) == (5, 168))
10 | assert(align(List(16, 1, 2, 0, 4, 2, 7, 1, 2, 14), n => n * (n + 1) / 2) == (5, 168))
11 | }
12 |
13 | test("part1") {
14 | assert(part1(List(16, 1, 2, 0, 4, 2, 7, 1, 2, 14)) == 37)
15 | }
16 |
17 | test("part2") {
18 | assert(part2(List(16, 1, 2, 0, 4, 2, 7, 1, 2, 14)) == 168)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day08Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.year2021.Day08._
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class Day08Test extends AnyFunSuite {
7 |
8 | val input =
9 | """be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
10 | |edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
11 | |fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
12 | |fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
13 | |aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
14 | |fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
15 | |dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
16 | |bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
17 | |egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
18 | |gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce""".stripMargin
19 |
20 | val shortInput = "acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf"
21 |
22 | test("deduction") {
23 | assert(deduction(parseLine(shortInput).get.inputs) == Map(
24 | Set('e', 'a', 'b', 'g', 'c', 'd') -> 0,
25 | Set('a', 'b') -> 1,
26 | Set('f', 'a', 'g', 'c', 'd') -> 2,
27 | Set('f', 'a', 'b', 'c', 'd') -> 3,
28 | Set('e', 'a', 'f', 'b') -> 4,
29 | Set('e', 'f', 'b', 'c', 'd') -> 5,
30 | Set('e', 'f', 'b', 'g', 'c', 'd') -> 6,
31 | Set('d', 'a', 'b') -> 7,
32 | Set('e', 'f', 'a', 'b', 'g', 'c', 'd') -> 8,
33 | Set('e', 'f', 'a', 'b', 'c', 'd') -> 9
34 | ))
35 | }
36 |
37 | test("part1") {
38 | assert(part1(input.split("\n").flatMap(parseLine).toList) == 26)
39 | }
40 |
41 | test("part2") {
42 | assert(part2(input.split("\n").flatMap(parseLine).toList) == 61229)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day09Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.year2021.Day09._
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class Day09Test extends AnyFunSuite {
7 | val raw =
8 | """2199943210
9 | |3987894921
10 | |9856789892
11 | |8767896789
12 | |9899965678""".stripMargin
13 |
14 | val heightmap = parseHeightMap(raw.split("\n"))
15 |
16 | test("lowPoints") {
17 | assert(lowPoints(heightmap) == Set((1, 0, 1), (9, 0, 0), (2, 2, 5), (6, 4, 5)))
18 | }
19 |
20 | test("basins") {
21 | assert(basins(heightmap) == Set(
22 | Set((3, 1, 7), (3, 3, 7), (1, 2, 8), (4, 1, 8), (2, 3, 6), (1, 4, 8), (4, 2, 7), (4, 3, 8), (2, 1, 8), (3, 2, 6), (2, 2, 5), (1, 3, 7), (0, 3, 8), (5, 2, 8)),
23 | Set((9, 4, 8), (7, 2, 8), (8, 4, 7), (6, 4, 5), (7, 3, 7), (5, 4, 6), (7, 4, 6), (6, 3, 6), (8, 3, 8)),
24 | Set((8, 1, 2), (7, 0, 2), (9, 2, 2), (9, 0, 0), (8, 0, 1), (6, 1, 4), (6, 0, 3), (9, 1, 1), (5, 0, 4)),
25 | Set((1, 0, 1), (0, 0, 2), (0, 1, 3))
26 | ))
27 | }
28 |
29 | test("part1") {
30 | assert(part1(heightmap) == 15)
31 | }
32 |
33 | test("part2") {
34 | assert(part2(heightmap) == 1134)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day10Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.year2021.Day10._
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class Day10Test extends AnyFunSuite {
7 | val lines =
8 | """[({(<(())[]>[[{[]{<()<>>
9 | |[(()[<>])]({[<{<<[]>>(
10 | |{([(<{}[<>[]}>{[]{[(<()>
11 | |(((({<>}<{<{<>}{[]{[]{}
12 | |[[<[([]))<([[{}[[()]]]
13 | |[{[{({}]{}}([{[{{{}}([]
14 | |{<[[]]>}<{[{[{[]{()[[[]
15 | |[<(<(<(<{}))><([]([]()
16 | |<{([([[(<>()){}]>(<<{{
17 | |<{([{{}}[<[[[<>{}]]]>[]]""".stripMargin
18 |
19 | test("corrupted") {
20 | assert(corrupted("{([(<{}[<>[]}>{[]{[(<()>").contains('}'))
21 | assert(corrupted("[[<[([]))<([[{}[[()]]]").contains(')'))
22 | assert(corrupted("[{[{({}]{}}([{[{{{}}([]").contains(']'))
23 | assert(corrupted("[<(<(<(<{}))><([]([]()").contains(')'))
24 | assert(corrupted("<{([([[(<>()){}]>(<<{{").contains('>'))
25 | }
26 |
27 | test("autoComplete") {
28 | assert(autoComplete("[({(<(())[]>[[{[]{<()<>>") == "}}]])})]")
29 | assert(autoComplete("[(()[<>])]({[<{<<[]>>(") == ")}>]})")
30 | assert(autoComplete("(((({<>}<{<{<>}{[]{[]{}") == "}}>}>))))")
31 | assert(autoComplete("{<[[]]>}<{[{[{[]{()[[[]") == "]]}}]}]}>")
32 | assert(autoComplete("<{([{{}}[<[[[<>{}]]]>[]]") == "])}>")
33 | }
34 |
35 | test("scoreIncomplete") {
36 | assert(scoreIncomplete("}}]])})]") == 288957)
37 | assert(scoreIncomplete(")}>]})") == 5566)
38 | assert(scoreIncomplete("}}>}>))))") == 1480781)
39 | assert(scoreIncomplete("]]}}]}]}>") == 995444)
40 | assert(scoreIncomplete("])}>") == 294)
41 | }
42 |
43 | test("part1") {
44 | assert(part1(lines.split("\n").toList) == 26397)
45 | }
46 |
47 | test("part2") {
48 | assert(part2(lines.split("\n").toList) == 288957)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Day13Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2021.Day13._
5 | import com.lmat.adventofcode.year2021.Day13Definitions._
6 |
7 | class Day13Test extends AnyFunSuite {
8 | val rawInstructions =
9 | """6,10
10 | |0,14
11 | |9,10
12 | |0,3
13 | |10,4
14 | |4,11
15 | |6,0
16 | |6,12
17 | |4,1
18 | |0,13
19 | |10,12
20 | |3,4
21 | |3,0
22 | |8,4
23 | |1,10
24 | |2,14
25 | |8,10
26 | |9,0
27 | |
28 | |fold along y=7
29 | |fold along x=5""".stripMargin
30 |
31 | val dots = Set(Dot(0, 3), Dot(6, 12), Dot(6, 10), Dot(8, 10), Dot(10, 12), Dot(9, 10), Dot(9, 0), Dot(10, 4), Dot(3, 4), Dot(1, 10), Dot(8, 4), Dot(4, 11), Dot(4, 1), Dot(3, 0), Dot(0, 13), Dot(6, 0), Dot(2, 14), Dot(0, 14))
32 | val folds = List(Fold("y", 7), Fold("x", 5))
33 |
34 | test("parseInstructions") {
35 | assert(parseInstructions(rawInstructions.split("\n")) == (dots, folds))
36 | }
37 |
38 | test("foldPaper") {
39 | println(draw(dots))
40 | val foldUp = foldPaper(dots, folds.head)
41 | println(draw(foldUp))
42 | val foldLeft = foldPaper(foldUp, folds(1))
43 | println(draw(foldLeft))
44 | }
45 |
46 | test("part1") {
47 | assert(part1(dots, folds) == 17)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2021/Year2021Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2021
2 |
3 | import com.lmat.adventofcode.PuzzleRunner.{puzzleMap, resource}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Year2021Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val puzzles =
9 | Table(
10 | ("day", "part1", "part2"),
11 | (1, 1292, 1262),
12 | (2, 1947824, 1813062561),
13 | (3, 2035764, 2817661),
14 | (4, 64084, 12833),
15 | (5, 4873, 19472),
16 | (6, 380758, 1710623015163L),
17 | (7, 351901, 101079875),
18 | (8, 514, 1012272),
19 | (9, 566, 891684),
20 | (10, 167379, 2776842859L),
21 | (11, 1721, 298),
22 | (12, 3679, 107395),
23 | (13, 621, "HKUJGAJZ"),
24 | (14, 3587, BigInt("3906445077999")),
25 | )
26 |
27 | forAll(puzzles) { (day, part1, part2) =>
28 | val year = 2021
29 |
30 | test(s"$year: Day $day") {
31 | val (res1, res2) = puzzleMap(year, day).solve(resource(year, day))
32 | assert(res1 == part1)
33 | assert(res2 == part2)
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day01Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.year2023.Day01._
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class Day01Test extends AnyFunSuite {
7 | val lines1 = List("1abc2", "pqr3stu8vwx", "a1b2c3d4e5f", "treb7uchet")
8 | val lines2 = List("two1nine", "eightwothree", "abcone2threexyz", "xtwone3four", "4nineeightseven2", "zoneight234", "7pqrstsixteen")
9 | test("part1") {
10 | assert(part1(lines1) == 142)
11 | }
12 |
13 | test("part2") {
14 | assert(part2(lines2) == 281)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day02Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.year2023.Day02Definitions._
4 | import com.lmat.adventofcode.year2023.Day02._
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | class Day02Test extends AnyFunSuite {
8 |
9 | val rawGames =
10 | """Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
11 | |Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
12 | |Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
13 | |Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
14 | |Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green""".stripMargin
15 |
16 | val games = List(
17 | Game(1, List(CubeSet(4, 0, 3), CubeSet(1, 2, 6), CubeSet(0, 2, 0))),
18 | Game(2, List(CubeSet(0, 2, 1), CubeSet(1, 3, 4), CubeSet(0, 1, 1))),
19 | Game(3, List(CubeSet(20, 8, 6), CubeSet(4, 13, 5), CubeSet(1, 5, 0))),
20 | Game(4, List(CubeSet(3, 1, 6), CubeSet(6, 3, 0), CubeSet(14, 3, 15))),
21 | Game(5, List(CubeSet(6, 3, 1), CubeSet(1, 2, 2)))
22 | )
23 |
24 | test("parse") {
25 | assert(rawGames.split("\n").flatMap(parseGame).toList == games)
26 | }
27 |
28 | test("part1") {
29 | assert(part1(games) == 8)
30 | }
31 |
32 | test("part2") {
33 | assert(part2(games) == 2286)
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day03Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.year2023.Day03Definitions._
4 | import com.lmat.adventofcode.year2023.Day03._
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | class Day03Test extends AnyFunSuite {
8 | val rawMap =
9 | """467..114..
10 | |...*......
11 | |..35..633.
12 | |......#...
13 | |617*......
14 | |.....+.58.
15 | |..592.....
16 | |......755.
17 | |...$.*....
18 | |.664.598..""".stripMargin
19 |
20 | val schematic = parse(rawMap.split("\n").toList)
21 | val parts = List(
22 | Part(598, List(Coordinate(7, 9), Coordinate(6, 9), Coordinate(5, 9))),
23 | Part(664, List(Coordinate(3, 9), Coordinate(2, 9), Coordinate(1, 9))),
24 | Part(755, List(Coordinate(8, 7), Coordinate(7, 7), Coordinate(6, 7))),
25 | Part(592, List(Coordinate(4, 6), Coordinate(3, 6), Coordinate(2, 6))),
26 | Part(58, List(Coordinate(8, 5), Coordinate(7, 5))),
27 | Part(617, List(Coordinate(2, 4), Coordinate(1, 4), Coordinate(0, 4))),
28 | Part(633, List(Coordinate(8, 2), Coordinate(7, 2), Coordinate(6, 2))),
29 | Part(35, List(Coordinate(3, 2), Coordinate(2, 2))),
30 | Part(114, List(Coordinate(7, 0), Coordinate(6, 0), Coordinate(5, 0))),
31 | Part(467, List(Coordinate(2, 0), Coordinate(1, 0), Coordinate(0, 0)))
32 | )
33 |
34 | val gears = List(
35 | Gear(Coordinate(3, 1), List(35, 467)),
36 | Gear(Coordinate(3, 4), List(617)),
37 | Gear(Coordinate(5, 8), List(598, 755))
38 | )
39 |
40 | test("collectParts") {
41 | assert(collectParts(schematic) == parts)
42 | }
43 |
44 | test("collectGears") {
45 | assert(collectGears(schematic) == gears)
46 | }
47 |
48 | test("part1") {
49 | assert(part1(schematic) == 4361)
50 | }
51 |
52 | test("part2") {
53 | assert(part2(schematic) == 467835)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day04Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day04Definitions._
5 | import com.lmat.adventofcode.year2023.Day04._
6 |
7 | class Day04Test extends AnyFunSuite {
8 |
9 | val rawGames =
10 | """Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
11 | |Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
12 | |Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
13 | |Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
14 | |Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
15 | |Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11""".stripMargin
16 |
17 | val games = List(
18 | Game(1, List(41, 48, 83, 86, 17), List(83, 86, 6, 31, 17, 9, 48, 53)),
19 | Game(2, List(13, 32, 20, 16, 61), List(61, 30, 68, 82, 17, 32, 24, 19)),
20 | Game(3, List(1, 21, 53, 59, 44), List(69, 82, 63, 72, 16, 21, 14, 1)),
21 | Game(4, List(41, 92, 73, 84, 69), List(59, 84, 76, 51, 58, 5, 54, 83)),
22 | Game(5, List(87, 83, 26, 28, 32), List(88, 30, 70, 12, 93, 22, 82, 36)),
23 | Game(6, List(31, 18, 13, 56, 72), List(74, 77, 10, 23, 35, 67, 36, 11))
24 | )
25 |
26 | test("parse") {
27 | assert(rawGames.split("\n").flatMap(parseGame).toList == games)
28 | }
29 |
30 | test("part1") {
31 | assert(part1(games) == 13)
32 | }
33 |
34 | test("simulate") {
35 | assert(simulate(start(games)).finished == Map(1 -> 1, 6 -> 1, 2 -> 2, 3 -> 4, 4 -> 8, 5 -> 14))
36 | }
37 |
38 | test("part2") {
39 | assert(part2(games) == 30)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day05Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day05Definitions._
5 | import com.lmat.adventofcode.year2023.Day05._
6 |
7 | class Day05Test extends AnyFunSuite {
8 | val raw =
9 | """seeds: 79 14 55 13
10 | |
11 | |seed-to-soil map:
12 | |50 98 2
13 | |52 50 48
14 | |
15 | |soil-to-fertilizer map:
16 | |0 15 37
17 | |37 52 2
18 | |39 0 15
19 | |
20 | |fertilizer-to-water map:
21 | |49 53 8
22 | |0 11 42
23 | |42 0 7
24 | |57 7 4
25 | |
26 | |water-to-light map:
27 | |88 18 7
28 | |18 25 70
29 | |
30 | |light-to-temperature map:
31 | |45 77 23
32 | |81 45 19
33 | |68 64 13
34 | |
35 | |temperature-to-humidity map:
36 | |0 69 1
37 | |1 0 69
38 | |
39 | |humidity-to-location map:
40 | |60 56 37
41 | |56 93 4""".stripMargin
42 |
43 | val almanac = Almanac(List(79, 14, 55, 13),
44 | Map(
45 | "humidity-to-location" -> List(Conversion(56, 93, 4), Conversion(60, 56, 37)),
46 | "light-to-temperature" -> List(Conversion(68, 64, 13), Conversion(81, 45, 19), Conversion(45, 77, 23)),
47 | "temperature-to-humidity" -> List(Conversion(1, 0, 69), Conversion(0, 69, 1)),
48 | "fertilizer-to-water" -> List(Conversion(57, 7, 4), Conversion(42, 0, 7), Conversion(0, 11, 42), Conversion(49, 53, 8)),
49 | "soil-to-fertilizer" -> List(Conversion(39, 0, 15), Conversion(37, 52, 2), Conversion(0, 15, 37)),
50 | "water-to-light" -> List(Conversion(18, 25, 70), Conversion(88, 18, 7)),
51 | "seed-to-soil" -> List(Conversion(52, 50, 48), Conversion(50, 98, 2)))
52 | )
53 |
54 | test("parse") {
55 | assert(parseAlmanac(raw.split("\n").toList) == almanac)
56 | }
57 |
58 | test("part1") {
59 | assert(part1(almanac) == 35)
60 | }
61 |
62 | test("part2") {
63 | assert(part2(almanac) == 46)
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day06Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day06Definitions._
5 | import com.lmat.adventofcode.year2023.Day06._
6 |
7 | class Day06Test extends AnyFunSuite {
8 | val raw =
9 | """Time: 7 15 30
10 | |Distance: 9 40 200""".stripMargin
11 |
12 | val parsed = List(Race(7, 9), Race(15, 40), Race(30, 200))
13 | val parsed2 = Race(71530, 940200)
14 |
15 |
16 | test("parse") {
17 | assert(parseRaces(raw.split("\n").toList) == parsed)
18 | assert(parseRace(raw.split("\n").toList) == parsed2)
19 | }
20 |
21 | test("part1") {
22 | assert(part1(parsed) == 288)
23 | }
24 |
25 | test("part2") {
26 | assert(part2(parsed2) == 71503)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day07Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day07Definitions._
5 | import com.lmat.adventofcode.year2023.Day07._
6 | class Day07Test extends AnyFunSuite {
7 | val raw =
8 | """32T3K 765
9 | |T55J5 684
10 | |KK677 28
11 | |KTJJT 220
12 | |QQQJA 483""".stripMargin
13 |
14 | val parsed = List(
15 | Hand(List('3', '2', 'T', '3', 'K'),765),
16 | Hand(List('T', '5', '5', 'J', '5'),684),
17 | Hand(List('K', 'K', '6', '7', '7'),28),
18 | Hand(List('K', 'T', 'J', 'J', 'T'),220),
19 | Hand(List('Q', 'Q', 'Q', 'J', 'A'),483)
20 | )
21 |
22 | test("parse") {
23 | assert(raw.split("\n").toList.map(parseHand) == parsed)
24 | }
25 |
26 | test("part1") {
27 | assert(part1(parsed) == 6440)
28 | }
29 |
30 | test("evaluate") {
31 | assert(parsed.map(hand => evaluate(hand.cards)) == List(
32 | "One pair",
33 | "Three of a kind",
34 | "Two pairs",
35 | "Two pairs",
36 | "Three of a kind")
37 | )
38 | }
39 |
40 | test("evaluate2") {
41 | assert(parsed.map(hand => evaluate2(hand.cards)) == List(
42 | "One pair",
43 | "Four of a kind",
44 | "Two pairs",
45 | "Four of a kind",
46 | "Four of a kind")
47 | )
48 | }
49 |
50 | test("part2") {
51 | assert(part2(parsed) == 5905)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day09Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day09._
5 | class Day09Test extends AnyFunSuite {
6 | val raw =
7 | """0 3 6 9 12 15
8 | |1 3 6 10 15 21
9 | |10 13 16 21 30 45""".stripMargin
10 |
11 | val oasis = List(
12 | List(0, 3, 6, 9, 12, 15),
13 | List(1, 3, 6, 10, 15, 21),
14 | List(10, 13, 16, 21, 30, 45)
15 | )
16 |
17 | test("parse") {
18 | assert(raw.split("\n").toList.map(parseRow) == oasis)
19 | }
20 |
21 | test("part1") {
22 | assert(part1(oasis) == 114)
23 | }
24 |
25 | test("part2") {
26 | assert(part2(oasis) == 2)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day11Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day11._
5 |
6 | class Day11Test extends AnyFunSuite {
7 |
8 | val raw1 =
9 | """...#......
10 | |.......#..
11 | |#.........
12 | |..........
13 | |......#...
14 | |.#........
15 | |.........#
16 | |..........
17 | |.......#..
18 | |#...#.....""".stripMargin
19 |
20 | val raw2 =
21 | """....#........
22 | |.........#...
23 | |#............
24 | |.............
25 | |.............
26 | |........#....
27 | |.#...........
28 | |............#
29 | |.............
30 | |.............
31 | |.........#...
32 | |#....#.......""".stripMargin
33 |
34 | val universe1 = parseUniverse(raw1.split("\n").toList)
35 | val expanded1 = parseUniverse(raw2.split("\n").toList)
36 |
37 |
38 | test("expand") {
39 | val rows = List(3, 7)
40 | val columns = List(2, 5, 8)
41 |
42 | assert(emptyRows(universe1) == rows)
43 | assert(emptyColumns(universe1) == columns)
44 |
45 | assert(expand(galaxies(universe1), rows, columns, 2) == galaxies(expanded1))
46 | }
47 |
48 | test("part1") {
49 | assert(part1(universe1) == 374)
50 | }
51 |
52 | test("galaxyDistances") {
53 | assert(galaxyDistances(universe1, 10) == 1030)
54 | assert(galaxyDistances(universe1, 100) == 8410)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day15Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day15._
5 | import com.lmat.adventofcode.year2023.Day15Definitions._
6 |
7 | class Day15Test extends AnyFunSuite {
8 | val raw = "rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7"
9 | val parsed = raw.split(",").toList
10 | val operations = List(Equal("rn", 1), Dash("cm"), Equal("qp", 3), Equal("cm", 2), Dash("qp"), Equal("pc", 4), Equal("ot", 9), Equal("ab", 5), Dash("pc"), Equal("pc", 6), Equal("ot", 7))
11 |
12 | test("parse") {
13 | assert(parsed.flatMap(parseOperation) == operations)
14 | }
15 |
16 | test("hash") {
17 | assert(parsed.map(hash) == List(30, 253, 97, 47, 14, 180, 9, 197, 48, 214, 231))
18 | }
19 |
20 | test("simulate") {
21 | assert(simulate(operations).filter(_._2.nonEmpty) == Map(
22 | 0 -> Vector(("rn", 1), ("cm", 2)),
23 | 3 -> Vector(("ot", 7), ("ab", 5), ("pc", 6))))
24 | }
25 |
26 | test("part1") {
27 | assert(part1(parsed) == 1320)
28 | }
29 |
30 | test("part2") {
31 | assert(part2(operations) == 145)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day16Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day16._
5 | import com.lmat.adventofcode.year2023.Day16Definitions._
6 |
7 | class Day16Test extends AnyFunSuite {
8 |
9 | val raw =
10 | """.|...\....
11 | ||.-.\.....
12 | |.....|-...
13 | |........|.
14 | |..........
15 | |.........\
16 | |..../.\\..
17 | |.-.-/..|..
18 | |.|....-|.\
19 | |..//.|....""".stripMargin
20 |
21 | val contraption = parseContraption(raw.split("\n").toList)
22 |
23 | test("parse") {
24 | assert(print(contraption) == raw)
25 | }
26 |
27 | test("energize") {
28 | assert(energize(contraption, Coordinate(0,0), East).toList.sorted == List(
29 | Coordinate(0, 0), Coordinate(1, 0), Coordinate(2, 0), Coordinate(3, 0), Coordinate(4, 0), Coordinate(5, 0),
30 | Coordinate(1, 1), Coordinate(5, 1),
31 | Coordinate(1, 2), Coordinate(5, 2), Coordinate(6, 2), Coordinate(7, 2), Coordinate(8, 2), Coordinate(9, 2),
32 | Coordinate(1, 3), Coordinate(5, 3), Coordinate(6, 3),
33 | Coordinate(1, 4), Coordinate(5, 4), Coordinate(6, 4),
34 | Coordinate(1, 5), Coordinate(5, 5), Coordinate(6, 5),
35 | Coordinate(1, 6), Coordinate(4, 6), Coordinate(5, 6), Coordinate(6, 6), Coordinate(7, 6),
36 | Coordinate(0, 7), Coordinate(1, 7), Coordinate(2, 7), Coordinate(3, 7), Coordinate(4, 7), Coordinate(5, 7), Coordinate(6, 7), Coordinate(7, 7),
37 | Coordinate(1, 8), Coordinate(2, 8), Coordinate(3, 8), Coordinate(4, 8), Coordinate(5, 8), Coordinate(6, 8), Coordinate(7, 8),
38 | Coordinate(1, 9), Coordinate(5, 9), Coordinate(7, 9)))
39 | }
40 |
41 | test("part1") {
42 | assert(part1(contraption) == 46)
43 | }
44 |
45 | test("part2") {
46 | assert(part2(contraption) == 51)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day17Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day17._
5 | import com.lmat.adventofcode.year2023.Day17Definitions._
6 |
7 | class Day17Test extends AnyFunSuite {
8 |
9 | val raw =
10 | """2413432311323
11 | |3215453535623
12 | |3255245654254
13 | |3446585845452
14 | |4546657867536
15 | |1438598798454
16 | |4457876987766
17 | |3637877979653
18 | |4654967986887
19 | |4564679986453
20 | |1224686865563
21 | |2546548887735
22 | |4322674655533""".stripMargin
23 |
24 | val raw2 =
25 | """111111111111
26 | |999999999991
27 | |999999999991
28 | |999999999991
29 | |999999999991""".stripMargin
30 |
31 | val cityMap = parseCityMap(raw.split("\n").toList)
32 | val cityMap2 = parseCityMap(raw2.split("\n").toList)
33 |
34 | test("parse") {
35 | assert(print(cityMap) == raw)
36 | assert(print(cityMap2) == raw2)
37 | }
38 |
39 | test("part1") {
40 | assert(part1(cityMap) == 102)
41 | }
42 |
43 | test("part2") {
44 | assert(part2(cityMap) == 94)
45 | assert(part2(cityMap2) == 71)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Day18Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import com.lmat.adventofcode.year2023.Day18Definitions._
5 | import com.lmat.adventofcode.year2023.Day18._
6 | class Day18Test extends AnyFunSuite {
7 | val raw =
8 | """R 6 (#70c710)
9 | |D 5 (#0dc571)
10 | |L 2 (#5713f0)
11 | |D 2 (#d2c081)
12 | |R 2 (#59c680)
13 | |D 2 (#411b91)
14 | |L 5 (#8ceee2)
15 | |U 2 (#caa173)
16 | |L 1 (#1b58a2)
17 | |U 2 (#caa171)
18 | |R 2 (#7807d2)
19 | |U 3 (#a77fa3)
20 | |L 2 (#015232)
21 | |U 2 (#7a21e3)""".stripMargin
22 |
23 | val digPlan = List(
24 | Plan(East, 6),
25 | Plan(South, 5),
26 | Plan(West, 2),
27 | Plan(South, 2),
28 | Plan(East, 2),
29 | Plan(South, 2),
30 | Plan(West, 5),
31 | Plan(North, 2),
32 | Plan(West, 1),
33 | Plan(North, 2),
34 | Plan(East, 2),
35 | Plan(North, 3),
36 | Plan(West, 2),
37 | Plan(North, 2)
38 | )
39 |
40 | val digPlan2 = List(
41 | Plan(East, 461937),
42 | Plan(South, 56407),
43 | Plan(East, 356671),
44 | Plan(South, 863240),
45 | Plan(East, 367720),
46 | Plan(South, 266681),
47 | Plan(West, 577262),
48 | Plan(North, 829975),
49 | Plan(West, 112010),
50 | Plan(South, 829975),
51 | Plan(West, 491645),
52 | Plan(North, 686074),
53 | Plan(West, 5411),
54 | Plan(North, 500254)
55 | )
56 |
57 | test("parse"){
58 | assert(raw.split("\n").flatMap(parsePlan).toList == digPlan)
59 | assert(raw.split("\n").flatMap(parsePlan2).toList == digPlan2)
60 | }
61 |
62 | test("part1") {
63 | assert(part1(digPlan) == 62)
64 | }
65 |
66 | test("part2") {
67 | assert(part2(digPlan2) == 952408144115L)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/adventofcode/year2023/Year2023Test.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.adventofcode.year2023
2 |
3 | import com.lmat.adventofcode.PuzzleRunner.{puzzleMap, resource}
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class Year2023Test extends AnyFunSuite with TableDrivenPropertyChecks {
8 | val puzzles =
9 | Table(
10 | ("day", "part1", "part2"),
11 | (1, 55108, 56324),
12 | (2, 2369, 66363),
13 | (3, 536576, 75741499),
14 | (4, 22193, 5625994),
15 | (5, 993500720L, 4917124L),
16 | (6, 293046, 35150181),
17 | (7, 249638405L, 249776650L),
18 | (8, 12169, 12030780859469L),
19 | (9, 1708206096L, 1050),
20 | (10, 6882, 491),
21 | (11, 9312968, 597714117556L),
22 | (12, 6871, 2043098029844L),
23 | (13, 37718, 40995),
24 | (14, 106990, 100531),
25 | (15, 516070, 244981),
26 | (16, 8901, 9064),
27 | (17, 928, 1104),
28 | (18, 35991, 54058824661845L),
29 | (19, 330820, 123972546935551L),
30 | (20, 919383692, 247702167614647L),
31 | )
32 |
33 | forAll(puzzles) { (day, part1, part2) =>
34 | val year = 2023
35 |
36 | test(s"$year: Day $day") {
37 | val (res1, res2) = puzzleMap(year, day).solve(resource(year, day))
38 | assert(res1 == part1)
39 | assert(res2 == part2)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/util/JsonTest.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 |
6 | class JsonTest extends AnyFunSuite with TableDrivenPropertyChecks {
7 |
8 | val examples =
9 | Table(
10 | ("raw", "json"),
11 | ("""123""", JsonIntValue(123)),
12 | ("""-123""", JsonIntValue(-123)),
13 | (""""magic"""", JsonStringValue("magic")),
14 | ("""[]""", JsonArray(Seq())),
15 | ("""{}""", JsonObject(Map())),
16 |
17 | ("""[1,2,3]""", JsonArray(Seq(JsonIntValue(1), JsonIntValue(2), JsonIntValue(3)))),
18 | ("""[1,"two",3]""", JsonArray(Seq(JsonIntValue(1), JsonStringValue("two"), JsonIntValue(3)))),
19 | ("""[[[3]]]""", JsonArray(Seq(JsonArray(Seq(JsonArray(Seq(JsonIntValue(3)))))))),
20 | ("""[[[3],"five"],[4]]""", JsonArray(Seq(JsonArray(Seq(JsonArray(Seq(JsonIntValue(3))), JsonStringValue("five"))), JsonArray(Seq(JsonIntValue(4)))))),
21 |
22 | ("""{"a":2,"b":4}""", JsonObject(Map("a" -> JsonIntValue(2), "b" -> JsonIntValue(4)))),
23 | ("""{"a":{"b":4},"c":-1}""", JsonObject(Map("a" -> JsonObject(Map("b" -> JsonIntValue(4))), "c" -> JsonIntValue(-1)))),
24 | ("""{"a":[-1,1]}""", JsonObject(Map("a" -> JsonArray(Seq(JsonIntValue(-1), JsonIntValue(1)))))),
25 | ("""[-1,{"a":1}]""", JsonArray(Seq(JsonIntValue(-1), JsonObject(Map("a" -> JsonIntValue(1)))))),
26 | )
27 |
28 | forAll(examples) { (raw, json) =>
29 | test(s"Parsing: $raw") {
30 | assert(Json.parse(raw).get == json)
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/util/MathsTest.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 | import com.lmat.util.Maths._
6 |
7 | class MathsTest extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val numbers =
10 | Table(
11 | ("number", "prime", "composite"),
12 | (0, false, true),
13 | (1, false, true),
14 | (2, true, false),
15 | (3, true, false),
16 | (4, false, true),
17 | (5, true, false),
18 | (6, false, true),
19 | (7, true, false),
20 | (8, false, true),
21 | (9, false, true),
22 | (10, false, true),
23 | (15485863, true, false),
24 | (15485861, false, true)
25 | )
26 |
27 | forAll(numbers) { (number, prime, _) =>
28 | test(s"$number is prime=$prime") {
29 | assert(isPrime(number) == prime)
30 | }
31 | }
32 |
33 | forAll(numbers) { (number, _, composite) =>
34 | test(s"$number is composite=$composite") {
35 | assert(isComposite(number) == composite)
36 | }
37 | }
38 |
39 | val divisorsTable =
40 | Table(
41 | ("number", "divisors"),
42 | (1, Set(1)),
43 | (2, Set(1, 2)),
44 | (4, Set(1, 2, 4)),
45 | (5, Set(1, 5)),
46 | (6, Set(1, 2, 3, 6)),
47 | (7, Set(1, 7)),
48 | (8, Set(1, 2, 4, 8)),
49 | (9, Set(1, 3, 9)),
50 | (10, Set(1, 2, 5, 10)),
51 | (15485863, Set(1, 15485863)),
52 | (15485861, Set(1, 17, 503, 1811, 8551, 30787, 910933, 15485861)),
53 | )
54 |
55 | forAll(divisorsTable) { (number, divisorsResult) =>
56 | test(s"Divisors of $number") {
57 | assert(divisors(number) == divisorsResult)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/util/MatrixTest.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | import com.lmat.util.Matrix._
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class MatrixTest extends AnyFunSuite {
7 | val matrix: Matrix[Int] = Matrix(Vector(
8 | Vector(0, 1, 2, 3),
9 | Vector(4, 5, 6, 7),
10 | Vector(8, 9, 10, 11),
11 | Vector(12, 13, 14, 15)
12 | ))
13 |
14 | val smallMatrices = Matrix(Vector(
15 | Vector(
16 | Matrix(Vector(
17 | Vector(0, 1),
18 | Vector(4, 5))),
19 | Matrix(Vector(
20 | Vector(2, 3),
21 | Vector(6, 7)))),
22 | Vector(
23 | Matrix(Vector(
24 | Vector(8, 9),
25 | Vector(12, 13))),
26 | Matrix(Vector(
27 | Vector(10, 11),
28 | Vector(14, 15))))
29 | ))
30 |
31 | test("Flips") {
32 | assert(flipHorizontal(matrix) == Matrix(Vector(
33 | Vector(3, 2, 1, 0),
34 | Vector(7, 6, 5, 4),
35 | Vector(11, 10, 9, 8),
36 | Vector(15, 14, 13, 12)
37 | )))
38 |
39 | assert(flipVertical(matrix) == Matrix(Vector(
40 | Vector(12, 13, 14, 15),
41 | Vector(8, 9, 10, 11),
42 | Vector(4, 5, 6, 7),
43 | Vector(0, 1, 2, 3)
44 | )))
45 | }
46 |
47 | test("Rotations") {
48 | assert(rotateLeft(matrix) == Matrix(Vector(
49 | Vector(3, 7, 11, 15),
50 | Vector(2, 6, 10, 14),
51 | Vector(1, 5, 9, 13),
52 | Vector(0, 4, 8, 12)
53 | )))
54 |
55 | assert(rotateRight(matrix) == Matrix(Vector(
56 | Vector(12, 8, 4, 0),
57 | Vector(13, 9, 5, 1),
58 | Vector(14, 10, 6, 2),
59 | Vector(15, 11, 7, 3)
60 | )))
61 | }
62 |
63 | test("Break -> Merge") {
64 | assert(break(matrix, 2, 2) == smallMatrices)
65 | assert(merge(smallMatrices) == matrix)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/util/StringsTest.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | import com.lmat.util.Strings._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.prop.TableDrivenPropertyChecks
6 |
7 | class StringsTest extends AnyFunSuite with TableDrivenPropertyChecks {
8 |
9 | val stringsForPadding =
10 | Table(
11 | ("Source", "Pad", "Length", "Result"),
12 | ("", '0', 0, ""),
13 | ("", '0', 1, "0"),
14 | ("0", '0', 0, "0"),
15 | ("0", '0', 1, "0"),
16 | ("0", '0', -1, "0"),
17 | ("123", '0', 1, "123"),
18 | ("123", '0', 4, "0123"),
19 | ("123", '0', 10, "0000000123"),
20 | )
21 |
22 | forAll(stringsForPadding) { (source, pad, length, result) =>
23 | test(s"Left Pad $source with $pad to $length") {
24 | assert(leftPad(source)(pad, length) == result)
25 | }
26 | }
27 |
28 | val stringsForIndices =
29 | Table(
30 | ("Source", "Query", "Indices"),
31 | ("", "", Seq()),
32 | ("", "aa", Seq()),
33 | ("aa", "", Seq((0,0), (1,1))),
34 | ("aaa", "aaa", Seq((0, 3))),
35 | ("aaa", "aa", Seq((0, 2), (1, 3))),
36 | ("aaa", "a", Seq((0, 1), (1, 2), (2, 3))),
37 | ("abbaabba", "aa", Seq((3, 5))),
38 | )
39 |
40 | forAll(stringsForIndices) { (source, query, indices) =>
41 | test(s"IndicesOf $query in $source") {
42 | assert(indicesOf(source, query) == indices)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/scala/com/lmat/util/TraverseTest.scala:
--------------------------------------------------------------------------------
1 | package com.lmat.util
2 |
3 | import org.scalatest.funsuite.AnyFunSuite
4 | import org.scalatest.prop.TableDrivenPropertyChecks
5 |
6 | class TraverseTest extends AnyFunSuite with TableDrivenPropertyChecks {
7 | val examples =
8 | Table(
9 | ("elements", "result"),
10 | (Seq(), Some(Seq())),
11 | (Seq(None), None),
12 | (Seq(Some(2)), Some(Seq(2))),
13 | (Seq(Some(2), Some(3), Some(1)), Some(Seq(2, 3, 1))),
14 | (Seq(Some(2), Some(3), None), None),
15 | )
16 |
17 | forAll(examples) { (elements, result) =>
18 | test(s"Sequencing: $elements") {
19 | assert(Traverse.sequenceOption(elements) == result)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------