├── .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 | --------------------------------------------------------------------------------