├── py ├── aoc2022 │ ├── __init__.py │ ├── day2.py │ ├── day25.py │ ├── main.py │ ├── day1.py │ ├── day4.py │ ├── day6.py │ ├── day3.py │ ├── day20.py │ ├── day5.py │ ├── day14.py │ ├── day9.py │ ├── day12.py │ ├── day7.py │ ├── day18.py │ ├── day24.py │ ├── day8.py │ ├── day13.py │ ├── day17.py │ └── day23.py ├── .gitignore ├── tests │ └── test_benchmark.py ├── README.md ├── LICENSE └── pyproject.toml ├── .gitignore ├── rs ├── .gitignore ├── src │ ├── lib.rs │ ├── day25.rs │ ├── day4.rs │ ├── day2.rs │ ├── day1.rs │ ├── day6.rs │ ├── util.rs │ ├── day3.rs │ ├── day20.rs │ ├── day5.rs │ └── day18.rs ├── README.md ├── Cargo.toml └── build.rs ├── kt ├── src │ ├── jvmTest │ │ ├── resources │ │ │ ├── day2.txt │ │ │ ├── day6.txt │ │ │ ├── day6a.txt │ │ │ ├── day6b.txt │ │ │ ├── day6c.txt │ │ │ ├── day6d.txt │ │ │ ├── day20.txt │ │ │ ├── day17.txt │ │ │ ├── day8.txt │ │ │ ├── day9.txt │ │ │ ├── day12.txt │ │ │ ├── day14.txt │ │ │ ├── day9a.txt │ │ │ ├── day4.txt │ │ │ ├── day24.txt │ │ │ ├── day23.txt │ │ │ ├── day1.txt │ │ │ ├── day25.txt │ │ │ ├── day18.txt │ │ │ ├── day3.txt │ │ │ ├── day5.txt │ │ │ ├── day22.txt │ │ │ ├── day21.txt │ │ │ ├── day13.txt │ │ │ ├── day7.txt │ │ │ ├── day19.txt │ │ │ ├── day16.txt │ │ │ ├── day11.txt │ │ │ ├── day15.txt │ │ │ └── day10.txt │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── ephemient │ │ │ └── aoc2022 │ │ │ └── Test.kt │ ├── jvmMain │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── ephemient │ │ │ └── aoc2022 │ │ │ ├── PriorityQueue.kt │ │ │ └── Main.kt │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── ephemient │ │ │ └── aoc2022 │ │ │ ├── Day.kt │ │ │ ├── Main.kt │ │ │ ├── PriorityQueue.kt │ │ │ ├── IntPair.kt │ │ │ ├── Day6.kt │ │ │ ├── Day2.kt │ │ │ ├── Day25.kt │ │ │ ├── Day4.kt │ │ │ ├── Day3.kt │ │ │ ├── Day7.kt │ │ │ ├── Day1.kt │ │ │ ├── Day20.kt │ │ │ ├── Day9.kt │ │ │ ├── Day5.kt │ │ │ ├── Day14.kt │ │ │ ├── Day12.kt │ │ │ ├── Day18.kt │ │ │ ├── Day24.kt │ │ │ ├── Day10.kt │ │ │ └── Day15.kt │ ├── commonTest │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── ephemient │ │ │ └── aoc2022 │ │ │ ├── Day25Test.kt │ │ │ ├── Test.kt │ │ │ ├── Day2Test.kt │ │ │ ├── Day3Test.kt │ │ │ ├── Day4Test.kt │ │ │ ├── Day8Test.kt │ │ │ ├── Day12Test.kt │ │ │ ├── Day14Test.kt │ │ │ ├── Day18Test.kt │ │ │ ├── Day1Test.kt │ │ │ ├── Day23Test.kt │ │ │ ├── Day24Test.kt │ │ │ ├── Day5Test.kt │ │ │ ├── Day15Test.kt │ │ │ ├── Day16Test.kt │ │ │ ├── Day21Test.kt │ │ │ ├── Day22Test.kt │ │ │ ├── Day7Test.kt │ │ │ ├── Day11Test.kt │ │ │ ├── Day20Test.kt │ │ │ ├── Day17Test.kt │ │ │ ├── Day9Test.kt │ │ │ ├── Day19Test.kt │ │ │ ├── Day13Test.kt │ │ │ ├── Day10Test.kt │ │ │ └── Day6Test.kt │ ├── nativeMain │ │ └── kotlin │ │ │ └── com │ │ │ └── github │ │ │ └── ephemient │ │ │ └── aoc2022 │ │ │ ├── PriorityQueue.kt │ │ │ └── Main.kt │ └── nativeTest │ │ └── kotlin │ │ └── com │ │ └── github │ │ └── ephemient │ │ └── aoc2022 │ │ └── Test.kt ├── detekt.yml ├── .gitignore ├── gradle │ ├── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ └── libs.versions.toml ├── gradle.properties ├── processor │ ├── src │ │ └── main │ │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.devtools.ksp.processing.SymbolProcessorProvider │ └── build.gradle.kts ├── buildSrc │ ├── build.gradle.kts │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── jetbrains │ │ └── kotlin │ │ └── gradle │ │ └── targets │ │ └── jvm │ │ └── tasks │ │ └── KotlinJvmTest.kt ├── graalvm │ └── src │ │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── ephemient │ │ └── aoc2022 │ │ └── graalvm │ │ └── ImageInfoTest.java ├── settings.gradle.kts └── README.md ├── hs ├── cabal.project ├── test │ ├── Main.hs │ ├── Day2Spec.hs │ ├── Day20Spec.hs │ ├── Day17Spec.hs │ ├── Day14Spec.hs │ ├── Day8Spec.hs │ ├── Day25Spec.hs │ ├── Day12Spec.hs │ ├── Day4Spec.hs │ ├── Day24Spec.hs │ ├── Day23Spec.hs │ ├── Day1Spec.hs │ ├── Day3Spec.hs │ ├── Day18Spec.hs │ ├── Day5Spec.hs │ ├── Day9Spec.hs │ ├── Day22Spec.hs │ ├── Day21Spec.hs │ ├── Day7Spec.hs │ ├── Day19Spec.hs │ ├── Day6Spec.hs │ ├── Day13Spec.hs │ ├── Day16Spec.hs │ ├── Day15Spec.hs │ └── Day11Spec.hs ├── .gitignore ├── src │ ├── Common.hs │ ├── Day25.hs │ ├── Day1.hs │ ├── Day6.hs │ ├── Day2.hs │ ├── Day9 │ │ └── TH.hs │ ├── Day3.hs │ ├── Day4.hs │ ├── Day8.hs │ ├── Day7.hs │ ├── Day9.hs │ ├── Day5.hs │ ├── Day10.hs │ ├── Day13.hs │ ├── Day12.hs │ ├── Day18.hs │ ├── Day13Fast.hs │ ├── Day24.hs │ ├── Day23.hs │ ├── Day20.hs │ ├── Day14.hs │ ├── Day21.hs │ └── Day15.hs ├── app │ ├── cbits │ │ └── main.c │ └── Main.hs ├── bench │ └── cbits │ │ └── main.c ├── README.md └── LICENSE ├── .gitattributes └── .github ├── dependabot.yml └── workflows ├── py.yml ├── py-bench.yml ├── hs.yml ├── rs.yml ├── hs-bench.yml ├── kt.yml ├── get-inputs.yml └── rs-bench.yml /py/aoc2022/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /day*.txt 2 | -------------------------------------------------------------------------------- /rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day2.txt: -------------------------------------------------------------------------------- 1 | A Y 2 | B X 3 | C Z 4 | -------------------------------------------------------------------------------- /hs/cabal.project: -------------------------------------------------------------------------------- 1 | packages: . 2 | with-compiler: ghc-9.2 3 | -------------------------------------------------------------------------------- /hs/test/Main.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -F -pgmF hspec-discover #-} 2 | -------------------------------------------------------------------------------- /kt/detekt.yml: -------------------------------------------------------------------------------- 1 | style: 2 | MagicNumber: 3 | active: false 4 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day6.txt: -------------------------------------------------------------------------------- 1 | mjqjpqmgbljsphdztnvjfqwrcgsmlb 2 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day6a.txt: -------------------------------------------------------------------------------- 1 | bvwbjplbgvbhsrlpgdmjqwftvncz 2 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day6b.txt: -------------------------------------------------------------------------------- 1 | nppdvjthqldpwncqszvftbrmjlhg 2 | -------------------------------------------------------------------------------- /py/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | dist/ 3 | aoc2022/day*.txt 4 | *~ 5 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day6c.txt: -------------------------------------------------------------------------------- 1 | nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg 2 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day6d.txt: -------------------------------------------------------------------------------- 1 | zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw 2 | -------------------------------------------------------------------------------- /kt/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | build/ 4 | local.properties 5 | *~ 6 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day20.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | -3 4 | 3 5 | -2 6 | 0 7 | 4 8 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day17.txt: -------------------------------------------------------------------------------- 1 | >>><<><>><<<>><>>><<<>>><<<><<<>><>><<>> 2 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day8.txt: -------------------------------------------------------------------------------- 1 | 30373 2 | 25512 3 | 65332 4 | 33549 5 | 35390 6 | -------------------------------------------------------------------------------- /hs/.gitignore: -------------------------------------------------------------------------------- 1 | day*.txt 2 | cabal.project.local 3 | dist-* 4 | .ghci 5 | .ghcid 6 | *~ 7 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day9.txt: -------------------------------------------------------------------------------- 1 | R 4 2 | U 4 3 | L 3 4 | D 1 5 | R 4 6 | D 1 7 | L 5 8 | R 2 9 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day12.txt: -------------------------------------------------------------------------------- 1 | Sabqponm 2 | abcryxxl 3 | accszExk 4 | acctuvwj 5 | abdefghi 6 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day14.txt: -------------------------------------------------------------------------------- 1 | 498,4 -> 498,6 -> 496,6 2 | 503,4 -> 502,4 -> 502,9 -> 494,9 3 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day9a.txt: -------------------------------------------------------------------------------- 1 | R 5 2 | U 8 3 | L 8 4 | D 3 5 | R 17 6 | D 10 7 | L 25 8 | U 20 9 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day4.txt: -------------------------------------------------------------------------------- 1 | 2-4,6-8 2 | 2-3,4-5 3 | 5-7,7-9 4 | 2-8,3-7 5 | 6-6,4-6 6 | 2-6,4-8 7 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day24.txt: -------------------------------------------------------------------------------- 1 | #.###### 2 | #>>.<^<# 3 | #.<..<<# 4 | #>v.><># 5 | #<^v^^># 6 | ######.# 7 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day23.txt: -------------------------------------------------------------------------------- 1 | ....#.. 2 | ..###.# 3 | #...#.# 4 | .#...## 5 | #.###.. 6 | ##.#.## 7 | .#..#.. 8 | -------------------------------------------------------------------------------- /kt/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephemient/aoc2022/HEAD/kt/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /kt/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.jvmargs=-Xmx512m -XX:+UseParallelGC 3 | org.gradle.java.installations.fromEnv=GRAALVM_HOME 4 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day1.txt: -------------------------------------------------------------------------------- 1 | 1000 2 | 2000 3 | 3000 4 | 5 | 4000 6 | 7 | 5000 8 | 6000 9 | 10 | 7000 11 | 8000 12 | 9000 13 | 14 | 10000 15 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day25.txt: -------------------------------------------------------------------------------- 1 | 1=-0-2 2 | 12111 3 | 2=0= 4 | 21 5 | 2=01 6 | 111 7 | 20012 8 | 112 9 | 1=-1= 10 | 1-12 11 | 12 12 | 1= 13 | 122 14 | -------------------------------------------------------------------------------- /kt/processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider: -------------------------------------------------------------------------------- 1 | com.github.ephemient.aoc2022.processor.MainProcessor$Provider 2 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day18.txt: -------------------------------------------------------------------------------- 1 | 2,2,2 2 | 1,2,2 3 | 3,2,2 4 | 2,1,2 5 | 2,3,2 6 | 2,2,1 7 | 2,2,3 8 | 2,2,4 9 | 2,2,6 10 | 1,2,5 11 | 3,2,5 12 | 2,1,5 13 | 2,3,5 14 | -------------------------------------------------------------------------------- /kt/src/jvmMain/kotlin/com/github/ephemient/aoc2022/PriorityQueue.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | actual typealias PriorityQueue = java.util.PriorityQueue 4 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day3.txt: -------------------------------------------------------------------------------- 1 | vJrwpWtwJgWrhcsFMMfFFhFp 2 | jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL 3 | PmmdzqPrVvPwwTWBwg 4 | wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn 5 | ttgJtRGJQctTZtZT 6 | CrZsJsPPZsGzwwsLwLmpwMDw 7 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day5.txt: -------------------------------------------------------------------------------- 1 | [D] 2 | [N] [C] 3 | [Z] [M] [P] 4 | 1 2 3 5 | 6 | move 1 from 2 to 1 7 | move 3 from 1 to 3 8 | move 2 from 2 to 1 9 | move 1 from 1 to 2 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # generated by Gradle Wrapper 2 | kt/gradlew linguist-generated 3 | kt/gradlew.bat linguist-generated 4 | 5 | # copied from Kotlin Gradle Plugin 6 | kt/buildSrc/src/main/kotlin/org/jetbrains/kotlin/gradle/**/*.kt linguist-vendored 7 | -------------------------------------------------------------------------------- /kt/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `embedded-kotlin` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | implementation(gradleApi()) 11 | implementation(kotlin("gradle-plugin", libs.versions.kotlin.get())) 12 | } 13 | -------------------------------------------------------------------------------- /kt/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Target(AnnotationTarget.CLASS) 4 | @Retention(AnnotationRetention.SOURCE) 5 | annotation class Day { 6 | @Target(AnnotationTarget.FUNCTION) 7 | annotation class Part 8 | } 9 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day22.txt: -------------------------------------------------------------------------------- 1 | ...# 2 | .#.. 3 | #... 4 | .... 5 | ...#.......# 6 | ........#... 7 | ..#....#.... 8 | ..........#. 9 | ...#.... 10 | .....#.. 11 | .#...... 12 | ......#. 13 | 14 | 10R5L5R10L4R5L5 15 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day21.txt: -------------------------------------------------------------------------------- 1 | root: pppw + sjmn 2 | dbpl: 5 3 | cczh: sllz + lgvd 4 | zczc: 2 5 | ptdq: humn - dvpt 6 | dvpt: 3 7 | lfqf: 4 8 | humn: 5 9 | ljgn: 2 10 | sjmn: drzm * dbpl 11 | sllz: 4 12 | pppw: cczh / lfqf 13 | lgvd: ljgn * ptdq 14 | drzm: hmdt - zczc 15 | hmdt: 32 16 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Main.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | expect fun getInput(day: Int): List 4 | 5 | expect fun assert(condition: () -> Boolean) 6 | 7 | expect fun assert(condition: () -> Boolean, lazyMessage: () -> Any) 8 | 9 | expect fun trace(message: String) 10 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day25Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day25Test { 7 | @Test 8 | fun part1() { 9 | assertEquals("2=-1=0", Day25(getTestInput(25)).part1()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day13.txt: -------------------------------------------------------------------------------- 1 | [1,1,3,1,1] 2 | [1,1,5,1,1] 3 | 4 | [[1],[2,3,4]] 5 | [[1],4] 6 | 7 | [9] 8 | [[8,7,6]] 9 | 10 | [[4,4],4,4] 11 | [[4,4],4,4,4] 12 | 13 | [7,7,7,7] 14 | [7,7,7] 15 | 16 | [] 17 | [3] 18 | 19 | [[[]]] 20 | [[]] 21 | 22 | [1,[2,[3,[4,[5,6,7]]]],8,9] 23 | [1,[2,[3,[4,[5,6,0]]]],8,9] 24 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day7.txt: -------------------------------------------------------------------------------- 1 | $ cd / 2 | $ ls 3 | dir a 4 | 14848514 b.txt 5 | 8504156 c.dat 6 | dir d 7 | $ cd a 8 | $ ls 9 | dir e 10 | 29116 f 11 | 2557 g 12 | 62596 h.lst 13 | $ cd e 14 | $ ls 15 | 584 i 16 | $ cd .. 17 | $ cd .. 18 | $ cd d 19 | $ ls 20 | 4060174 j 21 | 8033020 d.log 22 | 5626152 d.ext 23 | 7214296 k 24 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/PriorityQueue.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | expect class PriorityQueue(comparator: Comparator) { 4 | fun isEmpty(): Boolean 5 | 6 | fun add(element: E): Boolean 7 | 8 | @Throws(NoSuchElementException::class) 9 | fun remove(): E 10 | } 11 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | expect fun getTestInput(day: Int, extra: String = ""): List 4 | 5 | @OptIn(ExperimentalMultiplatform::class) 6 | @OptionalExpectation 7 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) 8 | expect annotation class SlowTest() 9 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day19.txt: -------------------------------------------------------------------------------- 1 | Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian. 2 | Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian. 3 | -------------------------------------------------------------------------------- /kt/buildSrc/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | 7 | versionCatalogs { 8 | create("libs") { 9 | from(files("../gradle/libs.versions.toml")) 10 | } 11 | } 12 | } 13 | 14 | rootProject.name = "buildSrc" 15 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day2Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day2Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(15, Day2(getTestInput(2)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(12, Day2(getTestInput(2)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day3Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day3Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(157, Day3(getTestInput(3)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(70, Day3(getTestInput(3)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day4Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day4Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(2, Day4(getTestInput(4)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(4, Day4(getTestInput(4)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day8Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day8Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(21, Day8(getTestInput(8)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(8, Day8(getTestInput(8)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day12Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day12Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(31, Day12(getTestInput(12)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(29, Day12(getTestInput(12)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day14Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day14Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(24, Day14(getTestInput(14)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(93, Day14(getTestInput(14)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day18Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day18Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(64, Day18(getTestInput(18)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(58, Day18(getTestInput(18)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day1Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day1Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(24000, Day1(getTestInput(1)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(45000, Day1(getTestInput(1)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day23Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day23Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(110, Day23(getTestInput(23)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(20, Day23(getTestInput(23)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day24Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day24Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(18, Day24(getTestInput(24)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(54, Day24(getTestInput(24)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day5Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day5Test { 7 | @Test 8 | fun part1() { 9 | assertEquals("CMZ", Day5(getTestInput(5)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals("MCD", Day5(getTestInput(5)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day15Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day15Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(26, Day15(getTestInput(15)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(56000011, Day15(getTestInput(15)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day16Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day16Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(1651, Day16(getTestInput(16)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(1707, Day16(getTestInput(16)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day21Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day21Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(152, Day21(getTestInput(21)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(301, Day21(getTestInput(21)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day22Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day22Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(6032, Day22(getTestInput(22)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(5031, Day22(getTestInput(22)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day7Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day7Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(95437, Day7(getTestInput(7)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(24933642, Day7(getTestInput(7)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/graalvm/src/test/java/com/github/ephemient/aoc2022/graalvm/ImageInfoTest.java: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022.graalvm; 2 | 3 | import org.graalvm.nativeimage.ImageInfo; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assumptions.assumeTrue; 7 | 8 | public class ImageInfoTest { 9 | @Test 10 | public void isGraalImage() { 11 | assumeTrue(ImageInfo.inImageCode()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day11Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day11Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(10605, Day11(getTestInput(11)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(2713310158, Day11(getTestInput(11)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day20Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day20Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(3, Day20(getTestInput(20)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(1623178306, Day20(getTestInput(20)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day17Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day17Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(3068, Day17(getTestInput(17)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(1514285714288, Day17(getTestInput(17)).part2()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/IntPair.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | data class IntPair(val first: Int, val second: Int) { 4 | override fun toString(): String = "($first,$second)" 5 | } 6 | 7 | infix fun Int.to(other: Int): IntPair = IntPair(this, other) 8 | 9 | data class IntTriple(val first: Int, val second: Int, val third: Int) { 10 | override fun toString(): String = "($first,$second,$third)" 11 | } 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: /kt 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: pip 8 | directory: /py 9 | schedule: 10 | interval: daily 11 | - package-ecosystem: cargo 12 | directory: /rs 13 | schedule: 14 | interval: daily 15 | - package-ecosystem: bundler 16 | directory: / 17 | target-branch: gh-docs 18 | schedule: 19 | interval: daily 20 | -------------------------------------------------------------------------------- /hs/src/Common.hs: -------------------------------------------------------------------------------- 1 | module Common (count, readEntire) where 2 | 3 | import Data.Foldable (toList) 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (null) 6 | import Data.Text.Read (Reader) 7 | 8 | count :: (Foldable t) => (a -> Bool) -> t a -> Int 9 | count p = length . filter p . toList 10 | 11 | readEntire :: Reader a -> Text -> Either String a 12 | readEntire reader input = do 13 | (a, t) <- reader input 14 | if T.null t then Right a else Left "incomplete read" 15 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day9Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day9Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(13, Day9(getTestInput(9)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(1, Day9(getTestInput(9)).part2()) 15 | assertEquals(36, Day9(getTestInput(9, "a")).part2()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day6.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day6(lines: List) { 5 | private val input = lines.single() 6 | 7 | @Day.Part 8 | fun part1(): Int? = solve(4) 9 | 10 | @Day.Part 11 | fun part2(): Int? = solve(14) 12 | 13 | private fun solve(n: Int): Int? { 14 | val index = input.windowedSequence(n).indexOfFirst { it.toSet().size == n } 15 | return if (index < 0) null else index + n 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod day1; 2 | pub mod day10; 3 | pub mod day11; 4 | pub mod day12; 5 | pub mod day13; 6 | pub mod day13_fast; 7 | pub mod day14; 8 | pub mod day15; 9 | pub mod day16; 10 | pub mod day17; 11 | pub mod day18; 12 | pub mod day19; 13 | pub mod day2; 14 | pub mod day20; 15 | pub mod day21; 16 | pub mod day22; 17 | pub mod day23; 18 | pub mod day24; 19 | pub mod day25; 20 | pub mod day3; 21 | pub mod day4; 22 | pub mod day5; 23 | pub mod day6; 24 | pub mod day7; 25 | pub mod day8; 26 | pub mod day9; 27 | pub mod util; 28 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day2.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day2(lines: List) { 5 | private val inputs = lines.map { it[0] - 'A' + 1 to it[2] - 'X' + 1 } 6 | 7 | @Day.Part 8 | fun part1(): Int = inputs.sumOf { (first, second) -> score(first, second) } 9 | 10 | @Day.Part 11 | fun part2(): Int = inputs.sumOf { (first, second) -> score(first, 1 + (first + second).mod(3)) } 12 | } 13 | 14 | private fun score(first: Int, second: Int): Int = (1 + second - first).mod(3) * 3 + second 15 | -------------------------------------------------------------------------------- /hs/test/Day2Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day2Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day2 (day2a, day2b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "A Y" 12 | , "B X" 13 | , "C Z" 14 | ] 15 | 16 | spec :: Spec 17 | spec = do 18 | describe "part 1" $ do 19 | it "examples" $ do 20 | day2a example `shouldBe` 15 21 | describe "part 2" $ do 22 | it "examples" $ do 23 | day2b example `shouldBe` 12 24 | -------------------------------------------------------------------------------- /hs/test/Day20Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day20Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day20 (day20a, day20b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines ["1", "2", "-3", "3", "-2", "0", "4"] 11 | 12 | spec :: Spec 13 | spec = do 14 | describe "part 1" $ do 15 | it "examples" $ do 16 | day20a example `shouldBe` Right 3 17 | describe "part 2" $ do 18 | it "examples" $ do 19 | day20b example `shouldBe` Right 1623178306 20 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day19Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlinx.coroutines.ExperimentalCoroutinesApi 4 | import kotlinx.coroutines.test.runTest 5 | import kotlin.test.Test 6 | import kotlin.test.assertEquals 7 | 8 | @OptIn(ExperimentalCoroutinesApi::class) 9 | class Day19Test { 10 | @Test 11 | fun part1() = runTest { 12 | assertEquals(33, Day19(getTestInput(19)).part1()) 13 | } 14 | 15 | @Test 16 | @SlowTest 17 | fun part2() = runTest { 18 | assertEquals(3472, Day19(getTestInput(19)).part2()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day16.txt: -------------------------------------------------------------------------------- 1 | Valve AA has flow rate=0; tunnels lead to valves DD, II, BB 2 | Valve BB has flow rate=13; tunnels lead to valves CC, AA 3 | Valve CC has flow rate=2; tunnels lead to valves DD, BB 4 | Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE 5 | Valve EE has flow rate=3; tunnels lead to valves FF, DD 6 | Valve FF has flow rate=0; tunnels lead to valves EE, GG 7 | Valve GG has flow rate=0; tunnels lead to valves FF, HH 8 | Valve HH has flow rate=22; tunnel leads to valve GG 9 | Valve II has flow rate=0; tunnels lead to valves AA, JJ 10 | Valve JJ has flow rate=21; tunnel leads to valve II 11 | -------------------------------------------------------------------------------- /rs/README.md: -------------------------------------------------------------------------------- 1 | # [Advent of Code 2022](https://adventofcode.com/2022) 2 | ### my answers in [Rust](https://www.rust-lang.org/) ![Rust CI](https://github.com/ephemient/aoc2022/workflows/Rust%20CI/badge.svg) 3 | 4 | This project builds with [Cargo](https://docs.rust-lang.org/cargo). 5 | 6 | Run the test suite: 7 | 8 | ```sh 9 | cargo test 10 | ``` 11 | 12 | Run the [Criterion.rs](https://github.com/bheisler/criterion.rs) benchmarks: 13 | 14 | ```sh 15 | cargo install cargo-criterion 16 | cargo criterion 17 | ``` 18 | 19 | Print solutions for the inputs provided in local data files: 20 | 21 | ```sh 22 | cargo run 23 | ``` 24 | -------------------------------------------------------------------------------- /hs/test/Day17Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day17Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day17 (day17) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ ">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>" 12 | ] 13 | 14 | spec :: Spec 15 | spec = do 16 | describe "part 1" $ do 17 | it "examples" $ do 18 | day17 2022 example `shouldBe` 3068 19 | describe "part 2" $ do 20 | it "examples" $ do 21 | day17 1000000000000 example `shouldBe` 1514285714288 22 | -------------------------------------------------------------------------------- /hs/test/Day14Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day14Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day14 (day14) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "498,4 -> 498,6 -> 496,6" 12 | , "503,4 -> 502,4 -> 502,9 -> 494,9" 13 | ] 14 | 15 | spec :: Spec 16 | spec = do 17 | describe "part 1" $ do 18 | it "examples" $ do 19 | fst (day14 example) `shouldBe` 24 20 | describe "part 2" $ do 21 | it "examples" $ do 22 | snd (day14 example) `shouldBe` 93 23 | -------------------------------------------------------------------------------- /hs/test/Day8Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day8Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day8 (day8a, day8b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "30373" 12 | , "25512" 13 | , "65332" 14 | , "33549" 15 | , "35390" 16 | ] 17 | 18 | spec :: Spec 19 | spec = do 20 | describe "part 1" $ do 21 | it "examples" $ do 22 | day8a example `shouldBe` 21 23 | describe "part 2" $ do 24 | it "examples" $ do 25 | day8b example `shouldBe` 8 26 | -------------------------------------------------------------------------------- /hs/test/Day25Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day25Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day25 (day25) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "1=-0-2" 12 | , "12111" 13 | , "2=0=" 14 | , "21" 15 | , "2=01" 16 | , "111" 17 | , "20012" 18 | , "112" 19 | , "1=-1=" 20 | , "1-12" 21 | , "12" 22 | , "1=" 23 | , "122" 24 | ] 25 | 26 | spec :: Spec 27 | spec = do 28 | describe "part 1" $ do 29 | it "examples" $ do 30 | day25 example `shouldBe` "2=-1=0" 31 | -------------------------------------------------------------------------------- /kt/src/jvmTest/kotlin/com/github/ephemient/aoc2022/Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import java.io.File 4 | import java.io.IOException 5 | 6 | actual fun getTestInput(day: Int, extra: String): List { 7 | val fileName = "day$day$extra.txt" 8 | val inputStream = try { 9 | System.getenv("aoc2022_test_datadir")?.ifEmpty { null }?.let { File(File(it), fileName).inputStream() } 10 | } catch (_: IOException) { 11 | null 12 | } ?: checkNotNull(ClassLoader.getSystemClassLoader().getResourceAsStream(fileName)) { "No data for day $day$extra" } 13 | return inputStream.bufferedReader().use { it.readLines() } 14 | } 15 | -------------------------------------------------------------------------------- /hs/test/Day12Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day12Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day12 (day12) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "Sabqponm" 12 | , "abcryxxl" 13 | , "accszExk" 14 | , "acctuvwj" 15 | , "abdefghi" 16 | ] 17 | 18 | spec :: Spec 19 | spec = do 20 | describe "part 1" $ do 21 | it "examples" $ do 22 | fst (day12 example) `shouldBe` Just 31 23 | describe "part 2" $ do 24 | it "examples" $ do 25 | snd (day12 example) `shouldBe` Just 29 26 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day13Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day13Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(13, Day13(getTestInput(13)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals(140, Day13(getTestInput(13)).part2()) 15 | } 16 | 17 | @Test 18 | fun part1Fast() { 19 | assertEquals(13, Day13Fast(getTestInput(13)).part1()) 20 | } 21 | 22 | @Test 23 | fun part2Fast() { 24 | assertEquals(140, Day13Fast(getTestInput(13)).part2()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /hs/test/Day4Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day4Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day4 (day4a, day4b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "2-4,6-8" 12 | , "2-3,4-5" 13 | , "5-7,7-9" 14 | , "2-8,3-7" 15 | , "6-6,4-6" 16 | , "2-6,4-8" 17 | ] 18 | 19 | spec :: Spec 20 | spec = do 21 | describe "part 1" $ do 22 | it "examples" $ do 23 | day4a example `shouldBe` Right 2 24 | describe "part 2" $ do 25 | it "examples" $ do 26 | day4b example `shouldBe` Right 4 27 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day25.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day25(private val lines: List) { 5 | @Day.Part 6 | fun part1(): String = buildString { 7 | var n = lines.sumOf { line -> 8 | line.fold(0L) { k, c -> 9 | 5 * k + when (c) { 10 | '=' -> -2 11 | '-' -> -1 12 | else -> c.digitToInt() 13 | } 14 | } 15 | } 16 | while (n != 0L) { 17 | append("012=-"[n.mod(5)]) 18 | n = (n + 2).floorDiv(5) 19 | } 20 | reverse() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /hs/test/Day24Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day24Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day24 (day24a, day24b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "#.######" 12 | , "#>>.<^<#" 13 | , "#.<..<<#" 14 | , "#>v.><>#" 15 | , "#<^v^^>#" 16 | , "######.#" 17 | ] 18 | 19 | spec :: Spec 20 | spec = do 21 | describe "part 1" $ do 22 | it "examples" $ do 23 | day24a example `shouldBe` Just 18 24 | describe "part 2" $ do 25 | it "examples" $ do 26 | day24b example `shouldBe` Just 54 27 | -------------------------------------------------------------------------------- /hs/test/Day23Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day23Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day23 (day23a, day23b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "....#.." 12 | , "..###.#" 13 | , "#...#.#" 14 | , ".#...##" 15 | , "#.###.." 16 | , "##.#.##" 17 | , ".#..#.." 18 | ] 19 | 20 | spec :: Spec 21 | spec = do 22 | describe "part 1" $ do 23 | it "examples" $ do 24 | day23a example `shouldBe` 110 25 | describe "part 2" $ do 26 | it "examples" $ do 27 | day23b example `shouldBe` 20 28 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day4.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day4(lines: List) { 5 | @Suppress("DestructuringDeclarationWithTooManyEntries") 6 | private val input = lines.map { 7 | val (a, b, c, d) = it.split(',', '-', limit = 4) 8 | a.toInt()..b.toInt() to c.toInt()..d.toInt() 9 | } 10 | 11 | @Day.Part 12 | fun part1(): Int = input.count { (x, y) -> 13 | x.first >= y.first && x.last <= y.last || x.first <= y.first && x.last >= y.last 14 | } 15 | 16 | @Day.Part 17 | fun part2(): Int = input.count { (x, y) -> 18 | x.first <= y.last && x.last >= y.first 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /hs/src/Day25.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day25 3 | Description: 4 | -} 5 | {-# LANGUAGE MultiWayIf #-} 6 | module Day25 (day25) where 7 | 8 | import Data.Char (digitToInt, intToDigit) 9 | import Data.Text (Text) 10 | import qualified Data.Text as T (foldl', lines, unfoldr, reverse) 11 | 12 | day25 :: Text -> Text 13 | day25 = T.reverse . T.unfoldr g . sum . map (T.foldl' f 0) . T.lines where 14 | f k c = 5 * k + if | '=' <- c -> -2 | '-' <- c -> -1 | otherwise -> digitToInt c 15 | g 0 = Nothing 16 | g n = Just (c, q) where 17 | (q, r) = (n + 2) `divMod` 5 18 | c | 0 <- r = '=' | 1 <- r = '-' | otherwise = intToDigit $ r - 2 19 | -------------------------------------------------------------------------------- /py/aoc2022/day2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 2: Rock Paper Scissors 3 | """ 4 | 5 | 6 | def _parse(lines): 7 | return ( 8 | (ord(line[0]) - ord("A") + 1, ord(line[2]) - ord("X") + 1) for line in lines 9 | ) 10 | 11 | 12 | def _score(left, right): 13 | return (1 + right - left) % 3 * 3 + right 14 | 15 | 16 | def part1(lines): 17 | """ 18 | >>> part1(["A Y", "B X", "C Z"]) 19 | 15 20 | """ 21 | return sum(_score(left, right) for left, right in _parse(lines)) 22 | 23 | 24 | def part2(lines): 25 | """ 26 | >>> part2(["A Y", "B X", "C Z"]) 27 | 12 28 | """ 29 | return sum(_score(left, 1 + (left + right) % 3) for left, right in _parse(lines)) 30 | 31 | 32 | parts = (part1, part2) 33 | -------------------------------------------------------------------------------- /hs/src/Day1.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day1 3 | Description: 4 | -} 5 | module Day1 (day1a, day1b) where 6 | 7 | import Common (readEntire) 8 | import Data.Either (isLeft, rights) 9 | import Data.List (maximum, sortOn) 10 | import Data.List.Split (splitWhen) 11 | import Data.Ord (Down(Down)) 12 | import Data.Text (Text) 13 | import qualified Data.Text as T (lines) 14 | import qualified Data.Text.Read as T (decimal) 15 | 16 | parse :: Text -> [Int] 17 | parse = map (sum . rights) . splitWhen isLeft . map (readEntire T.decimal) . T.lines 18 | 19 | day1a :: Text -> Int 20 | day1a = maximum . parse 21 | 22 | day1b :: Text -> Int 23 | day1b = sum . take 3 . sortOn Down . parse 24 | -------------------------------------------------------------------------------- /hs/src/Day6.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day6 3 | Description: 4 | -} 5 | module Day6 (day6a, day6b) where 6 | 7 | import Control.Monad (ap) 8 | import Data.List (findIndex, scanl') 9 | import qualified Data.Set as Set (empty, insert, notMember) 10 | import Data.Text (Text) 11 | import qualified Data.Text as T (length, tails, take, unpack) 12 | 13 | day6 :: Int -> Text -> Maybe Int 14 | day6 n = fmap (n +) . findIndex (ok . T.take n) . takeWhile ((>= n) . T.length) . T.tails where 15 | ok s = and $ zipWith Set.notMember `ap` scanl' (flip Set.insert) Set.empty $ T.unpack s 16 | 17 | day6a :: Text -> Maybe Int 18 | day6a = day6 4 19 | 20 | day6b :: Text -> Maybe Int 21 | day6b = day6 14 22 | -------------------------------------------------------------------------------- /kt/processor/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.gitlab.arturbosch.detekt.Detekt 2 | 3 | plugins { 4 | kotlin("jvm") 5 | alias(libs.plugins.detekt) 6 | } 7 | 8 | dependencies { 9 | detektPlugins(libs.detekt.formatting) 10 | implementation(libs.kotlinpoet) 11 | implementation(libs.kotlinpoet.ksp) 12 | implementation(libs.ksp.api) 13 | } 14 | 15 | tasks.withType().configureEach { 16 | config.from(rootProject.file("detekt.yml")) 17 | buildUponDefaultConfig = true 18 | autoCorrect = System.getenv("CI").isNullOrEmpty() 19 | exclude { it.file.toPath().startsWith(buildDir.toPath()) } 20 | } 21 | tasks.register("detektAll") { dependsOn(tasks.withType()) } 22 | tasks.check { dependsOn(tasks.withType()) } 23 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day11.txt: -------------------------------------------------------------------------------- 1 | Monkey 0: 2 | Starting items: 79, 98 3 | Operation: new = old * 19 4 | Test: divisible by 23 5 | If true: throw to monkey 2 6 | If false: throw to monkey 3 7 | 8 | Monkey 1: 9 | Starting items: 54, 65, 75, 74 10 | Operation: new = old + 6 11 | Test: divisible by 19 12 | If true: throw to monkey 2 13 | If false: throw to monkey 0 14 | 15 | Monkey 2: 16 | Starting items: 79, 60, 97 17 | Operation: new = old * old 18 | Test: divisible by 13 19 | If true: throw to monkey 1 20 | If false: throw to monkey 3 21 | 22 | Monkey 3: 23 | Starting items: 74 24 | Operation: new = old + 3 25 | Test: divisible by 17 26 | If true: throw to monkey 0 27 | If false: throw to monkey 1 28 | -------------------------------------------------------------------------------- /hs/test/Day1Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day1Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day1 (day1a, day1b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "1000" 12 | , "2000" 13 | , "3000" 14 | , "" 15 | , "4000" 16 | , "" 17 | , "5000" 18 | , "6000" 19 | , "" 20 | , "7000" 21 | , "8000" 22 | , "9000" 23 | , "" 24 | , "10000" 25 | ] 26 | 27 | spec :: Spec 28 | spec = do 29 | describe "part 1" $ do 30 | it "examples" $ do 31 | day1a example `shouldBe` 24000 32 | describe "part 2" $ do 33 | it "examples" $ do 34 | day1b example `shouldBe` 45000 35 | -------------------------------------------------------------------------------- /hs/test/Day3Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day3Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day3 (day3a, day3b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "vJrwpWtwJgWrhcsFMMfFFhFp" 12 | , "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL" 13 | , "PmmdzqPrVvPwwTWBwg" 14 | , "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn" 15 | , "ttgJtRGJQctTZtZT" 16 | , "CrZsJsPPZsGzwwsLwLmpwMDw" 17 | ] 18 | 19 | spec :: Spec 20 | spec = do 21 | describe "part 1" $ do 22 | it "examples" $ do 23 | day3a example `shouldBe` 157 24 | describe "part 2" $ do 25 | it "examples" $ do 26 | day3b example `shouldBe` 70 27 | -------------------------------------------------------------------------------- /hs/test/Day18Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day18Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day18 (day18a, day18b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "2,2,2" 12 | , "1,2,2" 13 | , "3,2,2" 14 | , "2,1,2" 15 | , "2,3,2" 16 | , "2,2,1" 17 | , "2,2,3" 18 | , "2,2,4" 19 | , "2,2,6" 20 | , "1,2,5" 21 | , "3,2,5" 22 | , "2,1,5" 23 | , "2,3,5" 24 | ] 25 | 26 | spec :: Spec 27 | spec = do 28 | describe "part 1" $ do 29 | it "examples" $ do 30 | day18a example `shouldBe` 64 31 | describe "part 2" $ do 32 | it "examples" $ do 33 | day18b example `shouldBe` 58 34 | -------------------------------------------------------------------------------- /hs/test/Day5Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day5Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day5 (day5a, day5b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ " [D] " 12 | , "[N] [C] " 13 | , "[Z] [M] [P]" 14 | , " 1 2 3 " 15 | , "" 16 | , "move 1 from 2 to 1" 17 | , "move 3 from 1 to 3" 18 | , "move 2 from 2 to 1" 19 | , "move 1 from 1 to 2" 20 | ] 21 | 22 | spec :: Spec 23 | spec = do 24 | describe "part 1" $ do 25 | it "examples" $ do 26 | day5a example `shouldBe` "CMZ" 27 | describe "part 2" $ do 28 | it "examples" $ do 29 | day5b example `shouldBe` "MCD" 30 | -------------------------------------------------------------------------------- /py/tests/test_benchmark.py: -------------------------------------------------------------------------------- 1 | """ 2 | Benchmarks 3 | """ 4 | 5 | from importlib import metadata, resources 6 | 7 | 8 | def _make_test_bench(day, part): 9 | def _test_bench(benchmark): 10 | with resources.files("aoc2022").joinpath(f"day{day}.txt").open() as file: 11 | data = file.readlines() 12 | benchmark(part, data) 13 | 14 | return _test_bench 15 | 16 | 17 | def _make_test_benches(): 18 | for entry in metadata.entry_points().select(group="aoc2022.days"): 19 | day = "".join(c for c in entry.name if c.isdigit()) 20 | for part in entry.load(): 21 | yield f"test_{entry.name}_{part.__name__}_bench", _make_test_bench( 22 | day, part 23 | ) 24 | 25 | 26 | globals().update(_make_test_benches()) 27 | -------------------------------------------------------------------------------- /hs/src/Day2.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day2 3 | Description: 4 | -} 5 | module Day2 (day2a, day2b) where 6 | 7 | import Data.Char (ord) 8 | import Data.Text (Text) 9 | import qualified Data.Text as T (head, lines, words) 10 | 11 | parse :: Text -> (Int, Int) 12 | parse line = (parse1 lhs, parse1 rhs) where 13 | [lhs, rhs] = T.words line 14 | parse1 = succ . (`mod` 3) . (`mod` 23) . pred . ord . T.head 15 | 16 | score :: Int -> Int -> Int 17 | score lhs rhs = (1 + rhs - lhs) `mod` 3 * 3 + rhs 18 | 19 | day2a :: Text -> Int 20 | day2a input = sum $ uncurry score . parse <$> T.lines input 21 | 22 | day2b :: Text -> Int 23 | day2b input = sum [score lhs $ 1 + (lhs + rhs) `mod` 3 | (lhs, rhs) <- parse <$> T.lines input] 24 | -------------------------------------------------------------------------------- /hs/app/cbits/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "HsFFI.h" 5 | #include "Rts.h" 6 | 7 | extern StgClosure ZCMain_main_closure; 8 | 9 | static int envRtsMsgFunction(const char *s, va_list ap) { 10 | const char *trace = getenv("TRACE"); 11 | if (trace == NULL || trace[0] != '0') { 12 | return rtsDebugMsgFn(s, ap); 13 | } 14 | return 0; 15 | } 16 | 17 | int main(int argc, char *argv[]) { 18 | RtsConfig rtsConfig = defaultRtsConfig; 19 | rtsConfig.rts_opts_enabled = RtsOptsAll; 20 | rtsConfig.rts_opts_suggestions = true; 21 | rtsConfig.rts_opts = "-N -qg"; 22 | rtsConfig.rts_hs_main = true; 23 | debugMsgFn = envRtsMsgFunction; 24 | return hs_main(argc, argv, &ZCMain_main_closure, rtsConfig); 25 | } 26 | -------------------------------------------------------------------------------- /hs/bench/cbits/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "HsFFI.h" 5 | #include "Rts.h" 6 | 7 | extern StgClosure ZCMain_main_closure; 8 | 9 | static int envRtsMsgFunction(const char *s, va_list ap) { 10 | const char *trace = getenv("TRACE"); 11 | if (trace == NULL || trace[0] != '0') { 12 | return rtsDebugMsgFn(s, ap); 13 | } 14 | return 0; 15 | } 16 | 17 | int main(int argc, char *argv[]) { 18 | RtsConfig rtsConfig = defaultRtsConfig; 19 | rtsConfig.rts_opts_enabled = RtsOptsAll; 20 | rtsConfig.rts_opts_suggestions = true; 21 | rtsConfig.rts_opts = "-N -qg"; 22 | rtsConfig.rts_hs_main = true; 23 | debugMsgFn = envRtsMsgFunction; 24 | return hs_main(argc, argv, &ZCMain_main_closure, rtsConfig); 25 | } 26 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day15.txt: -------------------------------------------------------------------------------- 1 | Sensor at x=2, y=18: closest beacon is at x=-2, y=15 2 | Sensor at x=9, y=16: closest beacon is at x=10, y=16 3 | Sensor at x=13, y=2: closest beacon is at x=15, y=3 4 | Sensor at x=12, y=14: closest beacon is at x=10, y=16 5 | Sensor at x=10, y=20: closest beacon is at x=10, y=16 6 | Sensor at x=14, y=17: closest beacon is at x=10, y=16 7 | Sensor at x=8, y=7: closest beacon is at x=2, y=10 8 | Sensor at x=2, y=0: closest beacon is at x=2, y=10 9 | Sensor at x=0, y=11: closest beacon is at x=2, y=10 10 | Sensor at x=20, y=14: closest beacon is at x=25, y=17 11 | Sensor at x=17, y=20: closest beacon is at x=21, y=22 12 | Sensor at x=16, y=7: closest beacon is at x=15, y=3 13 | Sensor at x=14, y=3: closest beacon is at x=15, y=3 14 | Sensor at x=20, y=1: closest beacon is at x=15, y=3 15 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day10Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day10Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(13140, Day10(getTestInput(10)).part1()) 10 | } 11 | 12 | @Test 13 | fun part2() { 14 | assertEquals( 15 | """ 16 | ▓▓░░▓▓░░▓▓░░▓▓░░▓▓░░▓▓░░▓▓░░▓▓░░▓▓░░▓▓░░ 17 | ▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░ 18 | ▓▓▓▓░░░░▓▓▓▓░░░░▓▓▓▓░░░░▓▓▓▓░░░░▓▓▓▓░░░░ 19 | ▓▓▓▓▓░░░░░▓▓▓▓▓░░░░░▓▓▓▓▓░░░░░▓▓▓▓▓░░░░░ 20 | ▓▓▓▓▓▓░░░░░░▓▓▓▓▓▓░░░░░░▓▓▓▓▓▓░░░░░░▓▓▓▓ 21 | ▓▓▓▓▓▓▓░░░░░░░▓▓▓▓▓▓▓░░░░░░░▓▓▓▓▓▓▓░░░░░ 22 | """.trimIndent(), 23 | Day10(getTestInput(10)).part2() 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /hs/src/Day9/TH.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskellQuotes #-} 2 | module Day9.TH (spliceCountUnique) where 3 | 4 | import Data.Bits ((.&.), (.|.), shiftL) 5 | import Data.Int (Int32, Int64) 6 | import qualified Data.IntSet as IntSet (fromList, size) 7 | import qualified Data.Set as Set (fromList, size) 8 | import Data.Primitive.MachDeps (sIZEOF_INT, sIZEOF_INT32) 9 | import Language.Haskell.TH (Exp, Quote) 10 | 11 | countUnique64 :: [(Int32, Int32)] -> Int 12 | countUnique64 = IntSet.size . IntSet.fromList . map mix where 13 | mix (x, y) = fromIntegral @Int64 $ fromIntegral x `shiftL` 32 .|. fromIntegral y .&. 0xFFFFFFFF 14 | 15 | countUnique :: [(Int, Int)] -> Int 16 | countUnique = Set.size . Set.fromList 17 | 18 | spliceCountUnique :: (Quote m) => m Exp 19 | spliceCountUnique 20 | | sIZEOF_INT >= 2 * sIZEOF_INT32 = [| countUnique64 |] 21 | | otherwise = [| countUnique |] 22 | -------------------------------------------------------------------------------- /.github/workflows/py.yml: -------------------------------------------------------------------------------- 1 | name: Python CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: [ py/** ] 7 | pull_request: 8 | branches: [ main ] 9 | paths: [ py/** ] 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-python@v4 20 | with: 21 | python-version: 3.11 22 | - uses: snok/install-poetry@v1 23 | - run: poetry install --no-interaction 24 | working-directory: py 25 | - run: poetry run pylint aoc2022 26 | working-directory: py 27 | - run: poetry run black . --check 28 | working-directory: py 29 | - run: poetry run isort . --check 30 | working-directory: py 31 | - run: poetry run pytest --benchmark-skip 32 | working-directory: py 33 | -------------------------------------------------------------------------------- /hs/test/Day9Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day9Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day9 (day9a, day9b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example1, example2 :: Text 10 | example1 = T.unlines 11 | [ "R 4" 12 | , "U 4" 13 | , "L 3" 14 | , "D 1" 15 | , "R 4" 16 | , "D 1" 17 | , "L 5" 18 | , "R 2" 19 | ] 20 | example2 = T.unlines 21 | [ "R 5" 22 | , "U 8" 23 | , "L 8" 24 | , "D 3" 25 | , "R 17" 26 | , "D 10" 27 | , "L 25" 28 | , "U 20" 29 | ] 30 | 31 | spec :: Spec 32 | spec = do 33 | describe "part 1" $ do 34 | it "examples" $ do 35 | day9a example1 `shouldBe` 13 36 | describe "part 2" $ do 37 | it "examples" $ do 38 | day9b example1 `shouldBe` 1 39 | day9b example2 `shouldBe` 36 40 | -------------------------------------------------------------------------------- /py/aoc2022/day25.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 25: Full of Hot Air 3 | """ 4 | 5 | SAMPLE_INPUT = [ 6 | "1=-0-2", 7 | "12111", 8 | "2=0=", 9 | "21", 10 | "2=01", 11 | "111", 12 | "20012", 13 | "112", 14 | "1=-1=", 15 | "1-12", 16 | "12", 17 | "1=", 18 | "122", 19 | ] 20 | 21 | 22 | def _unsnafu(snafu): 23 | number = 0 24 | for char in snafu: 25 | number = 5 * number + (-2 if char == "=" else -1 if char == "-" else int(char)) 26 | return number 27 | 28 | 29 | def _snafu(number): 30 | snafu = "" 31 | while number: 32 | snafu += "012=-"[number % 5] 33 | number = (number + 2) // 5 34 | return snafu[::-1] 35 | 36 | 37 | def part1(lines): 38 | """ 39 | >>> part1(SAMPLE_INPUT) 40 | '2=-1=0' 41 | """ 42 | return _snafu(sum(_unsnafu(line.rstrip()) for line in lines)) 43 | 44 | 45 | parts = (part1,) 46 | -------------------------------------------------------------------------------- /rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aoc2022" 3 | description = "Advent of Code 2022 - my answers" 4 | version = "0.1.0" 5 | authors = ["Daniel Lin "] 6 | license = "BSD-3-Clause" 7 | edition = "2021" 8 | readme = "README.md" 9 | homepage = "https://github.com/ephemient/aoc2022/tree/main/rs" 10 | build = "build.rs" 11 | 12 | [lib] 13 | name = "aoc2022" 14 | path = "src/lib.rs" 15 | 16 | [[bin]] 17 | name = "aoc2022" 18 | path = "src/main.rs" 19 | 20 | [build-dependencies] 21 | build_const = "0.2.2" 22 | 23 | [dependencies] 24 | build_const = "0.2.2" 25 | rayon = "1.6.1" 26 | smallvec = "1.10.0" 27 | static_init = "1.0.3" 28 | 29 | [dev-dependencies] 30 | criterion = "0.4.0" 31 | gag = "1.0.0" 32 | pretty_assertions = "1.3.0" 33 | 34 | [[bench]] 35 | name = "criterion" 36 | harness = false 37 | 38 | [profile.dev] 39 | opt-level = 1 40 | 41 | [profile.test] 42 | opt-level = 3 43 | -------------------------------------------------------------------------------- /hs/test/Day22Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day22Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day22 (day22a, day22b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ " ...#" 12 | , " .#.." 13 | , " #..." 14 | , " ...." 15 | , "...#.......#" 16 | , "........#..." 17 | , "..#....#...." 18 | , "..........#." 19 | , " ...#...." 20 | , " .....#.." 21 | , " .#......" 22 | , " ......#." 23 | , "" 24 | , "10R5L5R10L4R5L5" 25 | ] 26 | 27 | spec :: Spec 28 | spec = do 29 | describe "part 1" $ do 30 | it "examples" $ do 31 | day22a example `shouldBe` 6032 32 | describe "part 2" $ do 33 | it "examples" $ do 34 | day22b example `shouldBe` 5031 35 | -------------------------------------------------------------------------------- /py/aoc2022/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Advent of Code 2022 - my answers in Python 3 | """ 4 | 5 | import sys 6 | from importlib import metadata, resources 7 | 8 | from natsort import natsorted 9 | 10 | 11 | def main(): 12 | # pylint: disable=missing-function-docstring 13 | names = sys.argv[1:] 14 | days = metadata.entry_points().select(group="aoc2022.days") 15 | for entry in natsorted(days, key=lambda entry: entry.name): 16 | day = "".join(c for c in entry.name if c.isdigit()) 17 | if names and entry.name.removeprefix("day") not in names: 18 | continue 19 | print(f"Day {entry.name.removeprefix('day')}") 20 | with resources.files("aoc2022").joinpath(f"day{day}.txt").open() as file: 21 | data = file.readlines() 22 | for part in entry.load(): 23 | print(part(data)) 24 | print() 25 | 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /hs/test/Day21Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day21Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day21 (day21a, day21b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "root: pppw + sjmn" 12 | , "dbpl: 5" 13 | , "cczh: sllz + lgvd" 14 | , "zczc: 2" 15 | , "ptdq: humn - dvpt" 16 | , "dvpt: 3" 17 | , "lfqf: 4" 18 | , "humn: 5" 19 | , "ljgn: 2" 20 | , "sjmn: drzm * dbpl" 21 | , "sllz: 4" 22 | , "pppw: cczh / lfqf" 23 | , "lgvd: ljgn * ptdq" 24 | , "drzm: hmdt - zczc" 25 | , "hmdt: 32" 26 | ] 27 | 28 | spec :: Spec 29 | spec = do 30 | describe "part 1" $ do 31 | it "examples" $ do 32 | day21a example `shouldBe` Right 152 33 | describe "part 2" $ do 34 | it "examples" $ do 35 | day21b example `shouldBe` Right 301 36 | -------------------------------------------------------------------------------- /kt/src/commonTest/kotlin/com/github/ephemient/aoc2022/Day6Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class Day6Test { 7 | @Test 8 | fun part1() { 9 | assertEquals(7, Day6(getTestInput(6)).part1()) 10 | assertEquals(5, Day6(getTestInput(6, "a")).part1()) 11 | assertEquals(6, Day6(getTestInput(6, "b")).part1()) 12 | assertEquals(10, Day6(getTestInput(6, "c")).part1()) 13 | assertEquals(11, Day6(getTestInput(6, "d")).part1()) 14 | } 15 | 16 | @Test 17 | fun part2() { 18 | assertEquals(19, Day6(getTestInput(6)).part2()) 19 | assertEquals(23, Day6(getTestInput(6, "a")).part2()) 20 | assertEquals(23, Day6(getTestInput(6, "b")).part2()) 21 | assertEquals(29, Day6(getTestInput(6, "c")).part2()) 22 | assertEquals(26, Day6(getTestInput(6, "d")).part2()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /py/README.md: -------------------------------------------------------------------------------- 1 | # [Advent of Code 2022](https://adventofcode.com/2022) 2 | ### my answers in [Python](https://www.python.org/) ![Python CI](https://github.com/ephemient/aoc2022/workflows/Python%20CI/badge.svg) 3 | 4 | This project builds with [Poetry](https://python-poetry.org/). 5 | 6 | Setup: 7 | 8 | ```sh 9 | curl -sSL https://install.python-poetry.org | python3 - 10 | poetry install 11 | ``` 12 | 13 | Run the test suite: 14 | 15 | ```sh 16 | poetry run pytest 17 | ``` 18 | 19 | Run the benchmarks: 20 | 21 | ```sh 22 | poetry run pytest --benchmark-enable 23 | ``` 24 | 25 | Print solutions for the inputs provided in local data files: 26 | 27 | ```sh 28 | poetry run aoc2022 29 | ``` 30 | 31 | Lint and format code with [Black](https://black.readthedocs.io/), [pylint](https://github.com/PyCQA/pylint), and [isort](https://pycqa.github.io/isort/): 32 | 33 | ```sh 34 | poetry run black . 35 | poetry run isort . 36 | poetry run pylint aoc2022 37 | ``` 38 | -------------------------------------------------------------------------------- /py/aoc2022/day1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 1: Calorie Counting 3 | """ 4 | 5 | SAMPLE_INPUT = [ 6 | "1000", 7 | "2000", 8 | "3000", 9 | "", 10 | "4000", 11 | "", 12 | "5000", 13 | "6000", 14 | "", 15 | "7000", 16 | "8000", 17 | "9000", 18 | "", 19 | "10000", 20 | ] 21 | 22 | 23 | def _parse(lines): 24 | sums = [] 25 | acc = 0 26 | for line in lines: 27 | line = line.strip() 28 | if line: 29 | acc += int(line) 30 | else: 31 | sums.append(acc) 32 | acc = 0 33 | sums.append(acc) 34 | return sums 35 | 36 | 37 | def part1(lines): 38 | """ 39 | >>> part1(SAMPLE_INPUT) 40 | 24000 41 | """ 42 | return max(_parse(lines)) 43 | 44 | 45 | def part2(lines): 46 | """ 47 | >>> part2(SAMPLE_INPUT) 48 | 45000 49 | """ 50 | return sum(sorted(_parse(lines))[-3:]) 51 | 52 | 53 | parts = (part1, part2) 54 | -------------------------------------------------------------------------------- /py/aoc2022/day4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 4: Camp Cleanup 3 | """ 4 | 5 | import re 6 | 7 | _PATTERN = re.compile(r"(\d+)-(\d+),(\d+)-(\d+)") 8 | 9 | 10 | def part1(lines): 11 | """ 12 | >>> part1(["2-4,6-8", "2-3,4-5", "5-7,7-9", "2-8,3-7", "6-6,4-6", "2-6,4-8"]) 13 | 2 14 | """ 15 | count = 0 16 | for line in lines: 17 | start1, end1, start2, end2 = map(int, re.match(_PATTERN, line).groups()) 18 | if start1 <= start2 and end1 >= end2 or start1 >= start2 and end1 <= end2: 19 | count += 1 20 | return count 21 | 22 | 23 | def part2(lines): 24 | """ 25 | >>> part2(["2-4,6-8", "2-3,4-5", "5-7,7-9", "2-8,3-7", "6-6,4-6", "2-6,4-8"]) 26 | 4 27 | """ 28 | count = 0 29 | for line in lines: 30 | start1, end1, start2, end2 = map(int, re.match(_PATTERN, line).groups()) 31 | if start1 <= end2 and end1 >= start2: 32 | count += 1 33 | return count 34 | 35 | 36 | parts = (part1, part2) 37 | -------------------------------------------------------------------------------- /hs/test/Day7Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day7Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day7 (day7a, day7b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "$ cd /" 12 | , "$ ls" 13 | , "dir a" 14 | , "14848514 b.txt" 15 | , "8504156 c.dat" 16 | , "dir d" 17 | , "$ cd a" 18 | , "$ ls" 19 | , "dir e" 20 | , "29116 f" 21 | , "2557 g" 22 | , "62596 h.lst" 23 | , "$ cd e" 24 | , "$ ls" 25 | , "584 i" 26 | , "$ cd .." 27 | , "$ cd .." 28 | , "$ cd d" 29 | , "$ ls" 30 | , "4060174 j" 31 | , "8033020 d.log" 32 | , "5626152 d.ext" 33 | , "7214296 k" 34 | ] 35 | 36 | spec :: Spec 37 | spec = do 38 | describe "part 1" $ do 39 | it "examples" $ do 40 | day7a example `shouldBe` 95437 41 | describe "part 2" $ do 42 | it "examples" $ do 43 | day7b example `shouldBe` 24933642 44 | -------------------------------------------------------------------------------- /hs/test/Day19Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day19Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day19 (day19a, day19b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "Blueprint 1:" 12 | , " Each ore robot costs 4 ore." 13 | , " Each clay robot costs 2 ore." 14 | , " Each obsidian robot costs 3 ore and 14 clay." 15 | , " Each geode robot costs 2 ore and 7 obsidian." 16 | , "" 17 | , "Blueprint 2:" 18 | , " Each ore robot costs 2 ore." 19 | , " Each clay robot costs 3 ore." 20 | , " Each obsidian robot costs 3 ore and 8 clay." 21 | , " Each geode robot costs 3 ore and 12 obsidian." 22 | ] 23 | 24 | spec :: Spec 25 | spec = do 26 | describe "part 1" $ do 27 | it "examples" $ do 28 | day19a example `shouldBe` Right 33 29 | describe "part 2" $ do 30 | it "examples" $ do 31 | day19b example `shouldBe` Right (56 * 62) 32 | -------------------------------------------------------------------------------- /py/aoc2022/day6.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 6: Tuning Trouble 3 | """ 4 | 5 | 6 | def _solve(size, data): 7 | for i in range(size, len(data) + 1): 8 | if len(set(data[i - size : i])) == size: 9 | return i 10 | return None 11 | 12 | 13 | def part1(lines): 14 | """ 15 | >>> part1(["mjqjpqmgbljsphdztnvjfqwrcgsmlb"]) 16 | 7 17 | >>> part1(["bvwbjplbgvbhsrlpgdmjqwftvncz"]) 18 | 5 19 | >>> part1(["nppdvjthqldpwncqszvftbrmjlhg"]) 20 | 6 21 | >>> part1(["nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg"]) 22 | 10 23 | >>> part1(["zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw"]) 24 | 11 25 | """ 26 | return _solve(4, lines[0]) 27 | 28 | 29 | def part2(lines): 30 | """ 31 | >>> part2(["mjqjpqmgbljsphdztnvjfqwrcgsmlb"]) 32 | 19 33 | >>> part2(["bvwbjplbgvbhsrlpgdmjqwftvncz"]) 34 | 23 35 | >>> part2(["nppdvjthqldpwncqszvftbrmjlhg"]) 36 | 23 37 | >>> part2(["nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg"]) 38 | 29 39 | >>> part2(["zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw"]) 40 | 26 41 | """ 42 | return _solve(14, lines[0]) 43 | 44 | 45 | parts = (part1, part2) 46 | -------------------------------------------------------------------------------- /kt/src/jvmMain/kotlin/com/github/ephemient/aoc2022/Main.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("JvmMainKt") 2 | 3 | package com.github.ephemient.aoc2022 4 | 5 | import java.io.File 6 | import java.io.IOException 7 | 8 | actual fun getInput(day: Int): List { 9 | val fileName = "day$day.txt" 10 | val inputStream = try { 11 | System.getenv("aoc2022_datadir")?.ifEmpty { null }?.let { File(File(it), fileName).inputStream() } 12 | } catch (_: IOException) { 13 | null 14 | } ?: checkNotNull(ClassLoader.getSystemClassLoader().getResourceAsStream(fileName)) { "No data for day $day" } 15 | return inputStream.bufferedReader().use { it.readLines() } 16 | } 17 | 18 | private val trace by lazy(LazyThreadSafetyMode.NONE) { 19 | System.getenv("TRACE")?.startsWith('0') != true 20 | } 21 | 22 | actual inline fun assert(condition: () -> Boolean) { 23 | assert(condition()) 24 | } 25 | 26 | actual inline fun assert(condition: () -> Boolean, lazyMessage: () -> Any) { 27 | assert(condition(), lazyMessage) 28 | } 29 | 30 | actual fun trace(message: String) { 31 | if (trace) System.err.println(message) 32 | } 33 | -------------------------------------------------------------------------------- /hs/test/Day6Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day6Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day6 (day6a, day6b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | spec :: Spec 10 | spec = do 11 | describe "part 1" $ do 12 | it "examples" $ do 13 | day6a "mjqjpqmgbljsphdztnvjfqwrcgsmlb" `shouldBe` Just 7 14 | day6a "bvwbjplbgvbhsrlpgdmjqwftvncz" `shouldBe` Just 5 15 | day6a "nppdvjthqldpwncqszvftbrmjlhg" `shouldBe` Just 6 16 | day6a "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg" `shouldBe` Just 10 17 | day6a "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw" `shouldBe` Just 11 18 | describe "part 2" $ do 19 | it "examples" $ do 20 | day6b "mjqjpqmgbljsphdztnvjfqwrcgsmlb" `shouldBe` Just 19 21 | day6b "bvwbjplbgvbhsrlpgdmjqwftvncz" `shouldBe` Just 23 22 | day6b "nppdvjthqldpwncqszvftbrmjlhg" `shouldBe` Just 23 23 | day6b "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg" `shouldBe` Just 29 24 | day6b "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw" `shouldBe` Just 26 25 | -------------------------------------------------------------------------------- /hs/src/Day3.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day3 3 | Description: 4 | -} 5 | module Day3 (day3a, day3b) where 6 | 7 | import Data.Bits (Bits((.&.), setBit, testBit, zeroBits), FiniteBits(countTrailingZeros, finiteBitSize)) 8 | import Data.Char (ord) 9 | import Data.List (foldl1') 10 | import Data.List.Split (chunksOf) 11 | import Data.Text (Text) 12 | import Data.Word (Word64) 13 | import qualified Data.Text as T (foldl', length, lines, splitAt) 14 | 15 | items :: Text -> Word64 16 | items = T.foldl' (\acc c -> setBit acc $ prio c) zeroBits where 17 | prio c = if c >= 'a' then ord c - ord 'a' + 1 else ord c - ord 'A' + 27 18 | 19 | bits :: (FiniteBits b) => b -> [Int] 20 | bits x = filter (testBit x) [0 .. finiteBitSize x - 1] 21 | 22 | day3a :: Text -> Int 23 | day3a input = sum $ do 24 | line <- T.lines input 25 | let (a, b) = T.splitAt (T.length line `div` 2) line 26 | bits $ items a .&. items b 27 | 28 | day3b :: Text -> Int 29 | day3b input = sum $ do 30 | group <- chunksOf 3 $ T.lines input 31 | pure . countTrailingZeros . foldl1' (.&.) $ items <$> group 32 | -------------------------------------------------------------------------------- /hs/src/Day4.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day4 3 | Description: 4 | -} 5 | {-# LANGUAGE OverloadedStrings #-} 6 | module Day4 (day4a, day4b) where 7 | 8 | import Common (count) 9 | import Control.Monad (ap) 10 | import Data.Maybe (fromMaybe) 11 | import Data.Text (Text) 12 | import qualified Data.Text as T (lines, stripPrefix) 13 | import qualified Data.Text.Read as T (decimal) 14 | 15 | parse :: (Integral a) => Text -> Either String (a, a, a, a) 16 | parse line = do 17 | (a, line) <- T.decimal line 18 | (b, line) <- T.decimal $ fromMaybe `ap` T.stripPrefix "-" $ line 19 | (c, line) <- T.decimal $ fromMaybe `ap` T.stripPrefix "," $ line 20 | (d, line) <- T.decimal $ fromMaybe `ap` T.stripPrefix "-" $ line 21 | pure (a, b, c, d) 22 | 23 | day4a :: Text -> Either String Int 24 | day4a input = fmap (count ok) . sequence $ parse <$> T.lines input where 25 | ok (a, b, c, d) = a >= c && b <= d || a <= c && b >= d 26 | 27 | day4b :: Text -> Either String Int 28 | day4b input = fmap (count ok) . sequence $ parse <$> T.lines input where 29 | ok (a, b, c, d) = a <= d && b >= c 30 | -------------------------------------------------------------------------------- /kt/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenCentral() 6 | gradlePluginPortal() 7 | } 8 | 9 | val parseToml = Gradle::class.java.classLoader.loadClass("org.tomlj.Toml") 10 | .getMethod("parse", java.io.InputStream::class.java) 11 | val toml = file("gradle/libs.versions.toml").inputStream().use { parseToml.invoke(null, it) } 12 | val versions = toml.withGroovyBuilder { 13 | "getTable"("versions")?.withGroovyBuilder { 14 | ("keySet"() as Set<*>).associateWith { "get"(listOf(it)).toString() } 15 | } 16 | }.orEmpty() 17 | val kotlinVersion = versions["kotlin"] 18 | 19 | plugins { 20 | resolutionStrategy { 21 | eachPlugin { 22 | if (requested.id.id.startsWith("org.jetbrains.kotlin.")) useVersion(kotlinVersion) 23 | } 24 | } 25 | } 26 | } 27 | 28 | gradle.afterProject { 29 | repositories { 30 | mavenCentral() 31 | } 32 | } 33 | 34 | rootProject.name = "aoc2022" 35 | include("graalvm", "processor", "processor-bench", "processor-js") 36 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day3.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day3(private val lines: List) { 5 | @Day.Part 6 | fun part1(): Int = lines.sumOf { 7 | (it.items(end = it.length / 2) and it.items(start = it.length / 2)).bitSum() 8 | } 9 | 10 | @Day.Part 11 | fun part2(): Int = lines.chunked(3).sumOf { (a, b, c) -> 12 | (a.items() and b.items() and c.items()).singleBit() 13 | } 14 | } 15 | 16 | private fun String.items(start: Int = 0, end: Int = length): Long { 17 | var set = 0L 18 | for (i in start until end) { 19 | when (val c = get(i)) { 20 | in 'a'..'z' -> set = set or 1L.shl(c - 'a' + 1) 21 | in 'A'..'Z' -> set = set or 1L.shl(c - 'A' + 27) 22 | } 23 | } 24 | return set 25 | } 26 | 27 | private fun Long.bitSum(): Int { 28 | var sum = 0 29 | for (i in 0 until 64) { 30 | if (this and 1L.shl(i) != 0L) sum += i 31 | } 32 | return sum 33 | } 34 | 35 | private fun Long.singleBit(): Int { 36 | require(this != 0L && this and this - 1 == 0L) 37 | return countTrailingZeroBits() 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/py-bench.yml: -------------------------------------------------------------------------------- 1 | name: Python benchmarks 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | get-inputs: 8 | uses: ephemient/aoc2022/.github/workflows/get-inputs.yml@main 9 | secrets: 10 | SESSION: ${{ secrets.SESSION }} 11 | 12 | build: 13 | needs: [ get-inputs ] 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions/checkout@v3 19 | with: 20 | ref: gh-docs 21 | path: gh-docs 22 | - uses: actions/download-artifact@v3 23 | with: 24 | name: inputs 25 | path: py/aoc2022 26 | - uses: actions/setup-python@v4 27 | with: 28 | python-version: 3.11 29 | - uses: snok/install-poetry@v1 30 | - run: poetry install --no-interaction 31 | working-directory: py 32 | - run: poetry run pytest --benchmark-enable --benchmark-only --benchmark-histogram=../gh-docs/benchmark 33 | working-directory: py 34 | - uses: EndBug/add-and-commit@v9 35 | with: 36 | cwd: gh-docs 37 | add: benchmark.svg 38 | message: 'pytest-benchmark ${{ github.sha }}' 39 | -------------------------------------------------------------------------------- /hs/README.md: -------------------------------------------------------------------------------- 1 | # [Advent of Code 2022](https://adventofcode.com/2022) 2 | ### my answers in [Haskell](https://www.haskell.org/) ![Haskell CI](https://github.com/ephemient/aoc2022/workflows/Haskell%20CI/badge.svg) 3 | 4 | This project builds with [The Haskell Cabal](https://www.haskell.org/cabal/). 5 | 6 | Setup: 7 | 8 | ```sh 9 | curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh 10 | cabal configure --enable-tests 11 | ``` 12 | 13 | Run the [Hspec](https://hspec.github.io/) test suite: 14 | 15 | ```sh 16 | cabal test aoc2022-test 17 | ``` 18 | 19 | Run [criterion](http://www.serpentine.com/criterion/) benchmarks ([results online](https://ephemient.github.io/aoc2022/aoc2022-bench.html)): 20 | 21 | ```sh 22 | cabal bench aoc2022-bench 23 | ``` 24 | 25 | Print solutions for the inputs provided in local data files: 26 | 27 | ```sh 28 | cabal run aoc2022-exe 29 | ``` 30 | 31 | Generate [Haddock](https://www.haskell.org/haddock/) API documentation: 32 | 33 | ```sh 34 | stack haddock aoc2021:lib 35 | ``` 36 | 37 | Run [hlint](https://github.com/ndmitchell/hlint) source code suggestions: 38 | 39 | ```sh 40 | stack build hlint --exec 'hlint src test bench' 41 | ``` 42 | -------------------------------------------------------------------------------- /rs/src/day25.rs: -------------------------------------------------------------------------------- 1 | pub fn part1<'a, I, S>(lines: I) -> String 2 | where 3 | I: IntoIterator, 4 | S: AsRef + 'a, 5 | { 6 | let mut n = lines 7 | .into_iter() 8 | .map(|line| { 9 | line.as_ref().chars().fold(0, |k, c| { 10 | 5 * k 11 | + match c { 12 | '=' => -2, 13 | '-' => -1, 14 | _ => c.to_digit(3).expect("error") as i64, 15 | } 16 | }) 17 | }) 18 | .sum::(); 19 | let mut res = vec![]; 20 | while n != 0 { 21 | res.push(['=', '-', '0', '1', '2'][(n + 2).rem_euclid(5) as usize]); 22 | n = (n + 2).div_euclid(5); 23 | } 24 | res.into_iter().rev().collect() 25 | } 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use super::*; 30 | use pretty_assertions::assert_eq; 31 | 32 | static EXAMPLE: &[&str] = &[ 33 | "1=-0-2", "12111", "2=0=", "21", "2=01", "111", "20012", "112", "1=-1=", "1-12", "12", 34 | "1=", "122", 35 | ]; 36 | 37 | #[test] 38 | fn part1_examples() { 39 | assert_eq!("2=-1=0", part1(EXAMPLE)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /py/aoc2022/day3.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 3: Rucksack Reorganization 3 | """ 4 | 5 | SAMPLE_INPUT = [ 6 | "vJrwpWtwJgWrhcsFMMfFFhFp", 7 | "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL", 8 | "PmmdzqPrVvPwwTWBwg", 9 | "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn", 10 | "ttgJtRGJQctTZtZT", 11 | "CrZsJsPPZsGzwwsLwLmpwMDw", 12 | ] 13 | 14 | 15 | def _prio(item): 16 | if "a" <= item <= "z": 17 | return ord(item) - ord("a") + 1 18 | if "A" <= item <= "Z": 19 | return ord(item) - ord("A") + 27 20 | raise RuntimeError(f"not ok: {item}") 21 | 22 | 23 | def part1(lines): 24 | """ 25 | >>> part1(SAMPLE_INPUT) 26 | 157 27 | """ 28 | total = 0 29 | for line in lines: 30 | line = line.strip() 31 | for item in set(line[: len(line) // 2]).intersection(line[len(line) // 2 :]): 32 | total += _prio(item) 33 | return total 34 | 35 | 36 | def part2(lines): 37 | """ 38 | >>> part2(SAMPLE_INPUT) 39 | 70 40 | """ 41 | total = 0 42 | for first, second, third in zip(*(map(str.strip, lines),) * 3): 43 | total += _prio(set(first).intersection(second).intersection(third).pop()) 44 | return total 45 | 46 | 47 | parts = (part1, part2) 48 | -------------------------------------------------------------------------------- /hs/test/Day13Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day13Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day13 (day13a, day13b) 7 | import Day13Fast (day13aFast, day13bFast) 8 | import Test.Hspec (Spec, describe, it, shouldBe) 9 | 10 | example :: Text 11 | example = T.unlines 12 | [ "[1,1,3,1,1]" 13 | , "[1,1,5,1,1]" 14 | , "" 15 | , "[[1],[2,3,4]]" 16 | , "[[1],4]" 17 | , "" 18 | , "[9]" 19 | , "[[8,7,6]]" 20 | , "" 21 | , "[[4,4],4,4]" 22 | , "[[4,4],4,4,4]" 23 | , "" 24 | , "[7,7,7,7]" 25 | , "[7,7,7]" 26 | , "" 27 | , "[]" 28 | , "[3]" 29 | , "" 30 | , "[[[]]]" 31 | , "[[]]" 32 | , "" 33 | , "[1,[2,[3,[4,[5,6,7]]]],8,9]" 34 | , "[1,[2,[3,[4,[5,6,0]]]],8,9]" 35 | ] 36 | 37 | spec :: Spec 38 | spec = do 39 | describe "part 1" $ do 40 | it "examples" $ do 41 | day13a example `shouldBe` Right 13 42 | it "examples (fast)" $ do 43 | day13aFast example `shouldBe` 13 44 | describe "part 2" $ do 45 | it "examples" $ do 46 | day13b example `shouldBe` Right 140 47 | it "examples (fast)" $ do 48 | day13bFast example `shouldBe` 140 49 | -------------------------------------------------------------------------------- /.github/workflows/hs.yml: -------------------------------------------------------------------------------- 1 | name: Haskell CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: [ hs/** ] 7 | pull_request: 8 | branches: [ main ] 9 | paths: [ hs/** ] 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: haskell/actions/setup@v2 20 | id: setup-haskell 21 | with: 22 | ghc-version: 9.2.5 23 | - run: cabal configure --enable-tests --enable-benchmarks 24 | working-directory: hs 25 | - run: cabal freeze 26 | working-directory: hs 27 | - uses: actions/cache@v3 28 | with: 29 | key: hs-${{ runner.os }}-${{ hashFiles('**/cabal.project.freeze') }}-build 30 | restore-keys: | 31 | hs-${{ runner.os }}-${{ hashFiles('**/cabal.project.freeze') }}- 32 | hs-${{ runner.os }}- 33 | path: ${{ steps.setup-haskell.outputs.cabal-store }} 34 | - run: cabal build all --only-dependencies 35 | working-directory: hs 36 | - run: cabal build all 37 | working-directory: hs 38 | - run: cabal test all --test-show-details=direct 39 | working-directory: hs 40 | -------------------------------------------------------------------------------- /hs/test/Day16Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day16Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day16 (day16) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "Valve AA has flow rate=0; tunnels lead to valves DD, II, BB" 12 | , "Valve BB has flow rate=13; tunnels lead to valves CC, AA" 13 | , "Valve CC has flow rate=2; tunnels lead to valves DD, BB" 14 | , "Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE" 15 | , "Valve EE has flow rate=3; tunnels lead to valves FF, DD" 16 | , "Valve FF has flow rate=0; tunnels lead to valves EE, GG" 17 | , "Valve GG has flow rate=0; tunnels lead to valves FF, HH" 18 | , "Valve HH has flow rate=22; tunnel leads to valve GG" 19 | , "Valve II has flow rate=0; tunnels lead to valves AA, JJ" 20 | , "Valve JJ has flow rate=21; tunnel leads to valve II" 21 | ] 22 | 23 | spec :: Spec 24 | spec = do 25 | describe "part 1" $ do 26 | it "examples" $ do 27 | day16 1 30 example `shouldBe` Right 1651 28 | describe "part 2" $ do 29 | it "examples" $ do 30 | day16 2 26 example `shouldBe` Right 1707 31 | -------------------------------------------------------------------------------- /hs/src/Day8.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day8 3 | Description: 4 | -} 5 | module Day8 (day8a, day8b) where 6 | 7 | import Control.Monad (ap) 8 | import Data.List (findIndex, maximum, scanl', tails, transpose) 9 | import Data.Text (Text) 10 | import qualified Data.Text as T (lines, unpack) 11 | 12 | day8a :: Text -> Int 13 | day8a input = length $ filter id $ concat both where 14 | heights = T.unpack <$> T.lines input 15 | visible = zipWith (>) `ap` scanl' max minBound 16 | rows = [zipWith (||) (visible row) $ reverse $ visible $ reverse row | row <- heights] 17 | cols = [zipWith (||) (visible col) $ reverse $ visible $ reverse col | col <- transpose heights] 18 | both = zipWith (zipWith (||)) rows $ transpose cols 19 | 20 | day8b :: Text -> Int 21 | day8b input = maximum $ concat both where 22 | heights = T.unpack <$> T.lines input 23 | score row = [maybe (length xs) succ $ findIndex (>= x) xs | (x:xs) <- tails row] 24 | rows = [zipWith (*) (score row) $ reverse $ score $ reverse row | row <- heights] 25 | cols = [zipWith (*) (score col) $ reverse $ score $ reverse col | col <- transpose heights] 26 | both = zipWith (zipWith (*)) rows $ transpose cols 27 | -------------------------------------------------------------------------------- /hs/src/Day7.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day7 3 | Description: 4 | -} 5 | {-# LANGUAGE OverloadedStrings, ViewPatterns #-} 6 | module Day7 (day7a, day7b) where 7 | 8 | import Data.List (find, foldl', tails) 9 | import Data.Map.Strict (Map) 10 | import qualified Data.Map.Strict as Map ((!), elems, insertWith, singleton) 11 | import Data.Text (Text) 12 | import qualified Data.Text as T (lines, stripPrefix) 13 | import qualified Data.Text.Read as T (decimal) 14 | 15 | parse :: (Integral a) => Text -> Map [Text] a 16 | parse = snd . foldl' go ([], Map.singleton [] 0) . T.lines where 17 | go (_, dirs) "$ cd /" = ([], dirs) 18 | go (cwd, dirs) "$ cd .." = (drop 1 cwd, dirs) 19 | go (cwd, dirs) (T.stripPrefix "$ cd " -> Just dir) = (dir:cwd, dirs) 20 | go (cwd, dirs) (T.decimal -> Right (size, _)) = (cwd, foldl' (f size) dirs $ tails cwd) 21 | go state _ = state 22 | f !size dirs dir = Map.insertWith (+) dir size dirs 23 | 24 | day7a :: Text -> Int 25 | day7a = sum . filter (<= 100000) . Map.elems . parse 26 | 27 | day7b :: Text -> Int 28 | day7b input = minimum . filter ok $ Map.elems dirs where 29 | dirs = parse input 30 | total = dirs Map.! [] 31 | ok x = 70000000 - (total - x) >= 30000000 32 | -------------------------------------------------------------------------------- /py/aoc2022/day20.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 20: Grove Positioning System 3 | """ 4 | 5 | 6 | def _mix(tuples: list[tuple[int, int]]): 7 | for i in range(len(tuples)): 8 | i, entry = next((j, entry) for j, entry in enumerate(tuples) if entry[0] == i) 9 | j = (i + entry[1]) % (len(tuples) - 1) 10 | if i < j: 11 | tuples[i:j] = tuples[i + 1 : j + 1] 12 | elif i > j: 13 | tuples[j + 1 : i + 1] = tuples[j:i] 14 | tuples[j] = entry 15 | 16 | 17 | def part1(lines): 18 | """ 19 | >>> part1(["1", "2", "-3", "3", "-2", "0", "4"]) 20 | 3 21 | """ 22 | tuples = list(enumerate(map(int, lines))) 23 | _mix(tuples) 24 | i = next(i for i, entry in enumerate(tuples) if entry[1] == 0) 25 | return sum(tuples[(i + x) % len(tuples)][1] for x in range(1000, 3001, 1000)) 26 | 27 | 28 | def part2(lines): 29 | """ 30 | >>> part2(["1", "2", "-3", "3", "-2", "0", "4"]) 31 | 1623178306 32 | """ 33 | tuples = list(enumerate(811589153 * int(line) for line in lines)) 34 | for _ in range(10): 35 | _mix(tuples) 36 | i = next(i for i, entry in enumerate(tuples) if entry[1] == 0) 37 | return sum(tuples[(i + x) % len(tuples)][1] for x in range(1000, 3001, 1000)) 38 | 39 | 40 | parts = (part1, part2) 41 | -------------------------------------------------------------------------------- /.github/workflows/rs.yml: -------------------------------------------------------------------------------- 1 | name: Rust CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: [ rs/** ] 7 | pull_request: 8 | branches: [ main ] 9 | paths: [ rs/** ] 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - run: touch day{1..25}.txt 20 | - id: rust-toolchain 21 | uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: stable 24 | profile: minimal 25 | components: clippy, rustfmt 26 | default: true 27 | - uses: actions/cache@v3 28 | with: 29 | key: rs-${{ runner.os }}-${{ steps.rust-toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.*') }}-build 30 | restore-keys: | 31 | rs-${{ runner.os }}-${{ steps.rust-toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.*') }}- 32 | rs-${{ runner.os }}-${{ steps.rust-toolchain.outputs.rustc_hash }}- 33 | path: ~/.cargo 34 | - run: cargo fmt -- --check 35 | working-directory: rs 36 | - run: cargo clippy -- --deny warnings 37 | working-directory: rs 38 | - run: cargo test 39 | working-directory: rs 40 | - run: cargo build --all-features --all-targets --release 41 | working-directory: rs 42 | -------------------------------------------------------------------------------- /py/aoc2022/day5.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 5: Supply Stacks 3 | """ 4 | 5 | from itertools import zip_longest 6 | 7 | SAMPLE_INPUT = [ 8 | " [D] ", 9 | "[N] [C] ", 10 | "[Z] [M] [P]", 11 | " 1 2 3 ", 12 | "", 13 | "move 1 from 2 to 1", 14 | "move 3 from 1 to 3", 15 | "move 2 from 2 to 1", 16 | "move 1 from 1 to 2", 17 | ] 18 | 19 | 20 | def _solve(reverse, lines): 21 | lines = [line.rstrip() for line in lines] 22 | middle = lines.index("") 23 | stacks = { 24 | stack[-1]: "".join(filter(None, stack[:-1])).lstrip() 25 | for stack in zip_longest(*lines[:middle]) 26 | if stack[-1] and stack[-1].isdigit() 27 | } 28 | for line in lines[middle + 1 :]: 29 | _, num, _, source, _, dest = line.split() 30 | num = int(num) 31 | temp = stacks[source] 32 | stacks[source] = temp[num:] 33 | stacks[dest] = (temp[num - 1 :: -1] if reverse else temp[:num]) + stacks[dest] 34 | return "".join(stacks[key][0] for key in sorted(stacks)) 35 | 36 | 37 | def part1(lines): 38 | """ 39 | >>> part1(SAMPLE_INPUT) 40 | 'CMZ' 41 | """ 42 | return _solve(True, lines) 43 | 44 | 45 | def part2(lines): 46 | """ 47 | >>> part2(SAMPLE_INPUT) 48 | 'MCD' 49 | """ 50 | return _solve(False, lines) 51 | 52 | 53 | parts = (part1, part2) 54 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day7.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day7(lines: List) { 5 | private val sizes = buildMap { 6 | put("", 0) 7 | var cwd = "" 8 | for (line in lines) { 9 | val match = PATTERN.matchEntire(line) ?: continue 10 | match.groups[1]?.value?.let { dir -> 11 | cwd = when (dir) { 12 | "/" -> "" 13 | ".." -> cwd.substringBeforeLast('/', "") 14 | else -> if (cwd.isEmpty()) dir else "$cwd/$dir" 15 | } 16 | } ?: match.groups[2]?.value?.toIntOrNull()?.let { size -> 17 | var dir = cwd 18 | while (true) { 19 | put(dir, getOrElse(dir) { 0 } + size) 20 | if (dir.isEmpty()) break 21 | dir = dir.substringBeforeLast('/', "") 22 | } 23 | } 24 | } 25 | } 26 | 27 | @Day.Part 28 | fun part1(): Int = sizes.values.sumOf { if (it <= 100000) it else 0 } 29 | 30 | @Day.Part 31 | fun part2(): Int { 32 | val total = sizes.getValue("") 33 | return sizes.values.asSequence().filter { 70000000 - (total - it) >= 30000000 }.min() 34 | } 35 | } 36 | 37 | private val PATTERN = """[$] cd (.*)|(\d+).*""".toRegex() 38 | -------------------------------------------------------------------------------- /rs/src/day4.rs: -------------------------------------------------------------------------------- 1 | fn parse(line: &str) -> Option<(u32, u32, u32, u32)> { 2 | let mut iter = line.split([',', '-']); 3 | Some(( 4 | iter.next()?.parse().ok()?, 5 | iter.next()?.parse().ok()?, 6 | iter.next()?.parse().ok()?, 7 | iter.next()?.parse().ok()?, 8 | )) 9 | } 10 | 11 | pub fn part1<'a, I, S>(lines: I) -> usize 12 | where 13 | I: IntoIterator, 14 | S: AsRef + 'a, 15 | { 16 | lines 17 | .into_iter() 18 | .filter_map(|line| parse(line.as_ref())) 19 | .filter(|(a, b, c, d)| a <= c && b >= d || a >= c && b <= d) 20 | .count() 21 | } 22 | 23 | pub fn part2<'a, I, S>(lines: I) -> usize 24 | where 25 | I: IntoIterator, 26 | S: AsRef + 'a, 27 | { 28 | lines 29 | .into_iter() 30 | .filter_map(|line| parse(line.as_ref())) 31 | .filter(|(a, b, c, d)| a <= d && b >= c) 32 | .count() 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | use pretty_assertions::assert_eq; 39 | 40 | static EXAMPLE: &[&str] = &[ 41 | "2-4,6-8", "2-3,4-5", "5-7,7-9", "2-8,3-7", "6-6,4-6", "2-6,4-8", 42 | ]; 43 | 44 | #[test] 45 | fn part1_examples() { 46 | assert_eq!(2, part1(EXAMPLE)); 47 | } 48 | 49 | #[test] 50 | fn part2_examples() { 51 | assert_eq!(4, part2(EXAMPLE)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /hs/src/Day9.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day9 3 | Description: 4 | -} 5 | {-# LANGUAGE OverloadedStrings, TemplateHaskell, ViewPatterns #-} 6 | module Day9 (day9a, day9b) where 7 | 8 | import Control.Arrow (first, second) 9 | import Data.List (scanl') 10 | import Data.Text (Text) 11 | import qualified Data.Text as T (lines, unpack, words) 12 | import qualified Data.Text.Read as T (decimal) 13 | import Day9.TH (spliceCountUnique) 14 | 15 | follow :: (Num a, Ord a) => (a, a) -> (a, a) -> (a, a) 16 | follow (hx, hy) (tx, ty) 17 | | abs dx <= 1 && abs dy <= 1 = (tx, ty) 18 | | abs dx > abs dy = (hx - signum dx, hy) 19 | | abs dx < abs dy = (hx, hy - signum dy) 20 | | otherwise = (hx - signum dx, hy - signum dy) 21 | where 22 | dx = hx - tx 23 | dy = hy - ty 24 | 25 | move :: (Enum a) => Char -> (a, a) -> (a, a) 26 | move 'L' = first pred 27 | move 'R' = first succ 28 | move 'U' = second pred 29 | move 'D' = second succ 30 | 31 | expand :: Text -> [Char] 32 | expand (T.words -> [T.unpack -> [d], T.decimal -> Right (n, "")]) = replicate n d 33 | 34 | day9 :: (Enum a, Num a, Ord a) => Text -> [[(a, a)]] 35 | day9 = iterate (scanl' (flip follow) (0, 0)) . scanl' (flip move) (0, 0) . concatMap expand . T.lines 36 | 37 | day9a :: Text -> Int 38 | day9a = $(spliceCountUnique) . (!! 1) . day9 39 | 40 | day9b :: Text -> Int 41 | day9b = $(spliceCountUnique) . (!! 9) . day9 42 | -------------------------------------------------------------------------------- /hs/test/Day15Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day15Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day15 (day15a, day15b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "Sensor at x=2, y=18: closest beacon is at x=-2, y=15" 12 | , "Sensor at x=9, y=16: closest beacon is at x=10, y=16" 13 | , "Sensor at x=13, y=2: closest beacon is at x=15, y=3" 14 | , "Sensor at x=12, y=14: closest beacon is at x=10, y=16" 15 | , "Sensor at x=10, y=20: closest beacon is at x=10, y=16" 16 | , "Sensor at x=14, y=17: closest beacon is at x=10, y=16" 17 | , "Sensor at x=8, y=7: closest beacon is at x=2, y=10" 18 | , "Sensor at x=2, y=0: closest beacon is at x=2, y=10" 19 | , "Sensor at x=0, y=11: closest beacon is at x=2, y=10" 20 | , "Sensor at x=20, y=14: closest beacon is at x=25, y=17" 21 | , "Sensor at x=17, y=20: closest beacon is at x=21, y=22" 22 | , "Sensor at x=16, y=7: closest beacon is at x=15, y=3" 23 | , "Sensor at x=14, y=3: closest beacon is at x=15, y=3" 24 | , "Sensor at x=20, y=1: closest beacon is at x=15, y=3" 25 | ] 26 | 27 | spec :: Spec 28 | spec = do 29 | describe "part 1" $ do 30 | it "examples" $ do 31 | day15a 10 example `shouldBe` 26 32 | describe "part 2" $ do 33 | it "examples" $ do 34 | day15b 20 example `shouldBe` 56000011 35 | -------------------------------------------------------------------------------- /hs/src/Day5.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day5 3 | Description: 4 | -} 5 | {-# LANGUAGE OverloadedStrings, ViewPatterns #-} 6 | module Day5 (day5a, day5b) where 7 | 8 | import Data.Char (isDigit) 9 | import Data.List (foldl') 10 | import Data.List.Split (splitWhen) 11 | import qualified Data.Map as Map 12 | import Data.Maybe (mapMaybe) 13 | import Data.Text (Text) 14 | import qualified Data.Text as T (append, lines, null, reverse, splitAt, stripStart, transpose, unpack, unsnoc, words) 15 | import qualified Data.Text.Read as T (decimal) 16 | 17 | day5 :: (Text -> Text) -> Text -> Text 18 | day5 f input = head . T.transpose . Map.elems $ foldl' go stacks0 moves where 19 | [top, bottom] = splitWhen T.null $ T.lines input 20 | stack (T.unsnoc -> Just (s, x)) | isDigit x = Just (x, T.stripStart s) 21 | stack _ = Nothing 22 | stacks0 = Map.fromList . mapMaybe stack $ T.transpose top 23 | move ["move", T.decimal -> Right (num, ""), "from", T.unpack -> [x], "to", T.unpack -> [y]] 24 | | isDigit x, isDigit y 25 | = Just (num, x, y) 26 | move _ = Nothing 27 | moves = mapMaybe (move . T.words) bottom 28 | go stacks (num, x, y) = 29 | let (a, b) = T.splitAt num $ stacks Map.! x 30 | in Map.update (Just . T.append (f a)) y $ Map.insert x b stacks 31 | 32 | day5a :: Text -> Text 33 | day5a = day5 T.reverse 34 | 35 | day5b :: Text -> Text 36 | day5b = day5 id 37 | -------------------------------------------------------------------------------- /hs/test/Day11Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Day11Spec (spec) where 3 | 4 | import Data.Text (Text) 5 | import qualified Data.Text as T (unlines) 6 | import Day11 (day11a, day11b) 7 | import Test.Hspec (Spec, describe, it, shouldBe) 8 | 9 | example :: Text 10 | example = T.unlines 11 | [ "Monkey 0:" 12 | , " Starting items: 79, 98" 13 | , " Operation: new = old * 19" 14 | , " Test: divisible by 23" 15 | , " If true: throw to monkey 2" 16 | , " If false: throw to monkey 3" 17 | , "" 18 | , "Monkey 1:" 19 | , " Starting items: 54, 65, 75, 74" 20 | , " Operation: new = old + 6" 21 | , " Test: divisible by 19" 22 | , " If true: throw to monkey 2" 23 | , " If false: throw to monkey 0" 24 | , "" 25 | , "Monkey 2:" 26 | , " Starting items: 79, 60, 97" 27 | , " Operation: new = old * old" 28 | , " Test: divisible by 13" 29 | , " If true: throw to monkey 1" 30 | , " If false: throw to monkey 3" 31 | , "" 32 | , "Monkey 3:" 33 | , " Starting items: 74" 34 | , " Operation: new = old + 3" 35 | , " Test: divisible by 17" 36 | , " If true: throw to monkey 0" 37 | , " If false: throw to monkey 1" 38 | ] 39 | 40 | spec :: Spec 41 | spec = do 42 | describe "part 1" $ do 43 | it "examples" $ do 44 | day11a example `shouldBe` Right 10605 45 | describe "part 2" $ do 46 | it "examples" $ do 47 | day11b example `shouldBe` Right 2713310158 48 | -------------------------------------------------------------------------------- /hs/src/Day10.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day10 3 | Description: 4 | -} 5 | {-# LANGUAGE OverloadedStrings, ViewPatterns #-} 6 | module Day10 (day10a, day10b) where 7 | 8 | import qualified Data.IntMap as IntMap (fromDistinctAscList, lookupLT) 9 | import Data.Ix (inRange) 10 | import Data.List (intercalate, unfoldr) 11 | import Data.List.Split (chunksOf) 12 | import Data.Maybe (mapMaybe) 13 | import Data.Text (Text) 14 | import qualified Data.Text as T (lines, stripPrefix) 15 | import qualified Data.Text.Read as T (decimal, signed) 16 | 17 | parse :: (Integral a) => Text -> [(Int, a)] 18 | parse = scanl f (0, 1) . T.lines where 19 | f (i, x) "noop" = (i + 1, x) 20 | f (i, x) (T.stripPrefix "addx " -> Just (T.signed T.decimal -> Right (dx, ""))) = (i + 2, x + dx) 21 | 22 | day10a :: Text -> Int 23 | day10a input = sum . zipWith (*) taps . map snd $ mapMaybe (flip IntMap.lookupLT signals) taps where 24 | signals = IntMap.fromDistinctAscList $ parse input 25 | taps = [20, 60, 100, 140, 180, 220] 26 | 27 | day10b :: Text -> String 28 | day10b input = intercalate "\n" $ take 6 rows where 29 | sprites = concat $ unfoldr f (0, 1, parse input) 30 | f (i, x, (j, y):signals) = Just (replicate (j - i) x, (j, y, signals)) 31 | f (_, x, _) = Just (repeat x, undefined) 32 | rows = zipWith draw [0..] <$> chunksOf 40 sprites 33 | draw i x = if inRange (-1, 1) $ x - i then '\x2593' else '\x2591' 34 | -------------------------------------------------------------------------------- /kt/README.md: -------------------------------------------------------------------------------- 1 | # [Advent of Code 2022](https://adventofcode.com/2022) 2 | ### my answers in [Kotlin](https://www.kotlinlang.org/) ![Kotlin CI](https://github.com/ephemient/aoc2022/workflows/Kotlin%20CI/badge.svg) 3 | 4 | This project builds with [Gradle](https://gradle.org/). 5 | 6 | Run the test suite: 7 | 8 | ```sh 9 | ./gradlew :allTests 10 | ``` 11 | 12 | Run [kotlinx.benchmark](https://github.com/Kotlin/kotlinx-benchmark) ([JMH](https://openjdk.java.net/projects/code-tools/jmh/)) benchmarks: 13 | 14 | ```sh 15 | env aoc2022_data=.. ./gradlew :benchmark 16 | ``` 17 | 18 | Print solutions for the inputs provided in local data files: 19 | 20 | ```sh 21 | env aoc2022_data=.. ./gradlew :runJvm :run{Debug,Release}Executable{Linux{X64,Arm64},MingwX86,Macos{X64,Arm64}} 22 | ``` 23 | 24 | Generate [Dokka](https://github.com/Kotlin/dokka) API documentation: 25 | 26 | ```sh 27 | ./gradlew :dokkaHtml 28 | ``` 29 | 30 | Run all checks, including [Detekt](https://detekt.github.io/) static code analysis and [ktlint](https://ktlint.github.io/) formatter: 31 | 32 | ```sh 33 | ./gradlew :check 34 | ``` 35 | 36 | Build/run/test/benchmark with [GraalVM native-image](https://www.graalvm.org/latest/reference-manual/native-image/): 37 | 38 | ```sh 39 | $GRAALVM_HOME/bin/gu install native-image 40 | ./gradlew :graalvm:nativeCompile 41 | ./gradlew :graalvm:nativeRun 42 | ./gradlew :graalvm:nativeTest 43 | ./gradlew :graalvm:nativeJmhRun 44 | ``` 45 | 46 | Check for newer versions of dependencies: 47 | 48 | ```sh 49 | ./gradlew :dependencyUpdates 50 | ``` 51 | -------------------------------------------------------------------------------- /py/aoc2022/day14.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 14: Regolith Reservoir 3 | """ 4 | 5 | SAMPLE_INPUT = [ 6 | "498,4 -> 498,6 -> 496,6", 7 | "503,4 -> 502,4 -> 502,9 -> 494,9", 8 | ] 9 | 10 | 11 | def _parse(lines): 12 | blocks = set() 13 | for line in lines: 14 | points = [tuple(map(int, point.split(","))) for point in line.split(" -> ")] 15 | for point1, point2 in zip(points, points[1:]): 16 | x1, y1 = point1 17 | x2, y2 = point2 18 | blocks.update( 19 | (x, y) 20 | for x in range(min(x1, x2), max(x1, x2) + 1) 21 | for y in range(min(y1, y2), max(y1, y2) + 1) 22 | ) 23 | return blocks 24 | 25 | 26 | def _fill(blocks, max_y): 27 | def _fill_helper(x, y): 28 | if (x, y) in blocks: 29 | return 30 | if y <= max_y: 31 | yield from _fill_helper(x, y + 1) 32 | yield from _fill_helper(x - 1, y + 1) 33 | yield from _fill_helper(x + 1, y + 1) 34 | blocks.add((x, y)) 35 | yield x, y 36 | 37 | yield from _fill_helper(500, 0) 38 | 39 | 40 | def both_parts(lines): 41 | """ 42 | >>> both_parts(SAMPLE_INPUT) 43 | (24, 93) 44 | """ 45 | blocks = _parse(lines) 46 | max_y = max(y for _, y in blocks) 47 | part1, part2 = None, 0 48 | for _, y in _fill(blocks, max_y): 49 | if y >= max_y and part1 is None: 50 | part1 = part2 51 | part2 += 1 52 | return part1, part2 53 | 54 | 55 | parts = (both_parts,) 56 | -------------------------------------------------------------------------------- /rs/src/day2.rs: -------------------------------------------------------------------------------- 1 | use std::vec::Vec; 2 | 3 | fn parse<'a, I, S>(lines: I) -> Vec<(u8, u8)> 4 | where 5 | I: IntoIterator, 6 | S: AsRef + 'a, 7 | { 8 | lines 9 | .into_iter() 10 | .filter_map(|line| { 11 | let mut iter = line.as_ref().split_ascii_whitespace(); 12 | Some(( 13 | iter.next()?.as_bytes().first()? - 64, 14 | iter.next()?.as_bytes().first()? - 87, 15 | )) 16 | }) 17 | .collect() 18 | } 19 | 20 | fn score(left: u8, right: u8) -> u8 { 21 | (4 + right - left) % 3 * 3 + right 22 | } 23 | 24 | pub fn part1<'a, I, S>(lines: I) -> u32 25 | where 26 | I: IntoIterator, 27 | S: AsRef + 'a, 28 | { 29 | parse(lines) 30 | .into_iter() 31 | .map(|(left, right)| score(left, right) as u32) 32 | .sum() 33 | } 34 | 35 | pub fn part2<'a, I, S>(lines: I) -> u32 36 | where 37 | I: IntoIterator, 38 | S: AsRef + 'a, 39 | { 40 | parse(lines) 41 | .into_iter() 42 | .map(|(left, right)| score(left, 1 + (left + right) % 3) as u32) 43 | .sum() 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | use pretty_assertions::assert_eq; 50 | 51 | static EXAMPLE: &[&str] = &["A Y", "B X", "C Z"]; 52 | 53 | #[test] 54 | fn part1_examples() { 55 | assert_eq!(15, part1(EXAMPLE)); 56 | } 57 | 58 | #[test] 59 | fn part2_examples() { 60 | assert_eq!(12, part2(EXAMPLE)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day1.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day1(lines: List) { 5 | private val sums = buildList { 6 | var sum = 0 7 | for (line in lines) { 8 | val value = line.toIntOrNull() 9 | if (value != null) { 10 | sum += value 11 | } else { 12 | add(sum) 13 | sum = 0 14 | } 15 | } 16 | add(sum) 17 | } 18 | 19 | @Day.Part 20 | fun part1(): Int = sums.max() 21 | 22 | @Day.Part 23 | fun part2(): Int { 24 | val sums = sums.toIntArray() 25 | sums.quickSelectDescending(2) 26 | return sums[0] + sums[1] + sums[2] 27 | } 28 | } 29 | 30 | private fun IntArray.quickSelectDescending(index: Int) { 31 | require(index in indices) 32 | var start = 0 33 | var end = size 34 | var iter = 0 35 | while (end - start > 1) { 36 | require(iter++ < 120) 37 | val pivotIndex = start + (end - start) / 2 38 | val pivot = this[pivotIndex] 39 | this[pivotIndex] = this[end - 1] 40 | var n = start 41 | for (i in start until end - 1) { 42 | val tmp = this[i] 43 | if (tmp < pivot) continue 44 | this[i] = this[n] 45 | this[n++] = tmp 46 | } 47 | this[end - 1] = this[n] 48 | this[n] = pivot 49 | if (n < index) { 50 | start = n + 1 51 | } else { 52 | end = n 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /kt/src/nativeMain/kotlin/com/github/ephemient/aoc2022/PriorityQueue.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | actual class PriorityQueue actual constructor(private val comparator: Comparator) { 4 | private var storage: MutableList = mutableListOf() 5 | 6 | actual fun isEmpty(): Boolean = storage.isEmpty() 7 | 8 | actual fun add(element: E): Boolean { 9 | storage.add(element) 10 | var i = storage.lastIndex 11 | while (i > 0) { 12 | val j = (i - 1) / 2 13 | val a = storage[j] 14 | val b = storage[i] 15 | if (comparator.compare(a, b) <= 0) break 16 | storage[i] = a 17 | storage[j] = b 18 | i = j 19 | } 20 | return true 21 | } 22 | 23 | @Throws(NoSuchElementException::class) 24 | actual fun remove(): E { 25 | val first = storage.first() 26 | val last = storage.removeLast() 27 | if (storage.isNotEmpty()) { 28 | storage[0] = last 29 | var i = 0 30 | while (2 * i + 1 < storage.lastIndex) { 31 | val j = if (2 * i + 2 < storage.lastIndex) { 32 | if (comparator.compare(storage[2 * i + 1], storage[2 * i + 2]) < 0) 2 * i + 1 else 2 * i + 2 33 | } else 2 * i + 1 34 | if (comparator.compare(storage[i], storage[j]) <= 0) break 35 | storage[i] = storage[j].also { storage[j] = storage[i] } 36 | i = j 37 | } 38 | } 39 | return first 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rs/src/day1.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Reverse; 2 | use std::vec::Vec; 3 | 4 | fn parse<'a, I, S>(lines: I) -> Vec 5 | where 6 | I: IntoIterator, 7 | S: AsRef + 'a, 8 | { 9 | let mut sums = Vec::new(); 10 | let mut sum = 0; 11 | for line in lines { 12 | match line.as_ref().parse::() { 13 | Ok(num) => sum += num, 14 | Err(_) => { 15 | sums.push(sum); 16 | sum = 0; 17 | } 18 | } 19 | } 20 | sums.push(sum); 21 | sums 22 | } 23 | 24 | pub fn part1<'a, I, S>(lines: I) -> u32 25 | where 26 | I: IntoIterator, 27 | S: AsRef + 'a, 28 | { 29 | let nums = parse(lines); 30 | nums.into_iter().max().unwrap_or(0) 31 | } 32 | 33 | pub fn part2<'a, I, S>(lines: I) -> u32 34 | where 35 | I: IntoIterator, 36 | S: AsRef + 'a, 37 | { 38 | let mut nums = parse(lines); 39 | let (maxs, &mut max, _) = nums[..].select_nth_unstable_by_key(2, |&num| Reverse(num)); 40 | maxs.iter_mut().fold(max, |x, &mut y| x + y) 41 | } 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | use super::*; 46 | use pretty_assertions::assert_eq; 47 | 48 | static EXAMPLE: &[&str] = &[ 49 | "1000", "2000", "3000", "", "4000", "", "5000", "6000", "", "7000", "8000", "9000", "", 50 | "10000", 51 | ]; 52 | 53 | #[test] 54 | fn part1_examples() { 55 | assert_eq!(24000, part1(EXAMPLE)); 56 | } 57 | 58 | #[test] 59 | fn part2_examples() { 60 | assert_eq!(45000, part2(EXAMPLE)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/hs-bench.yml: -------------------------------------------------------------------------------- 1 | name: Haskell benchmarks 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | get-inputs: 8 | uses: ephemient/aoc2022/.github/workflows/get-inputs.yml@main 9 | secrets: 10 | SESSION: ${{ secrets.SESSION }} 11 | 12 | build: 13 | needs: [ get-inputs ] 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions/checkout@v3 19 | with: 20 | ref: gh-docs 21 | path: gh-docs 22 | - uses: actions/download-artifact@v3 23 | with: 24 | name: inputs 25 | path: hs 26 | - uses: haskell/actions/setup@v2 27 | id: setup-haskell 28 | with: 29 | ghc-version: 9.2.5 30 | - run: cabal configure --enable-tests --enable-benchmarks 31 | working-directory: hs 32 | - run: cabal freeze 33 | working-directory: hs 34 | - uses: actions/cache@v3 35 | with: 36 | key: hs-${{ runner.os }}-${{ hashFiles('**/cabal.project.freeze') }}-bench 37 | restore-keys: | 38 | hs-${{ runner.os }}-${{ hashFiles('**/cabal.project.freeze') }}- 39 | hs-${{ runner.os }}- 40 | path: ${{ steps.setup-haskell.outputs.cabal-store }} 41 | - run: cabal bench bench:aoc2022-bench --benchmark-options='-o ${{ github.workspace }}/gh-docs/aoc2022-bench.html' 42 | working-directory: hs 43 | - uses: EndBug/add-and-commit@v9 44 | with: 45 | cwd: gh-docs 46 | add: aoc2022-bench.html 47 | message: 'Haskell Criterion ${{ github.sha }}' 48 | -------------------------------------------------------------------------------- /hs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Daniel Lin (c) 2022 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Daniel Lin nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /py/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Daniel Lin (c) 2022 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Daniel Lin nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day20.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day20(lines: List) { 5 | private val boxes = lines.map { Box(it.toInt()) } 6 | 7 | @Day.Part 8 | fun part1(): Int { 9 | val array = boxes.toTypedArray() 10 | array.mix() 11 | val base = array.indexOfFirst { it.value == 0 } 12 | check(base >= 0) 13 | return array[(base + 1000) % array.size].value + 14 | array[(base + 2000) % array.size].value + 15 | array[(base + 3000) % array.size].value 16 | } 17 | 18 | @Day.Part 19 | fun part2(): Long { 20 | val array = boxes.toTypedArray() 21 | repeat(10) { array.mix(MULTIPLIER) } 22 | val base = array.indexOfFirst { it.value == 0 } 23 | check(base >= 0) 24 | return MULTIPLIER * ( 25 | array[(base + 1000) % array.size].value + 26 | array[(base + 2000) % array.size].value + 27 | array[(base + 3000) % array.size].value 28 | ) 29 | } 30 | 31 | private class Box(val value: Int) 32 | 33 | private fun Array.mix(multiplier: Long = 1L) { 34 | for (node in boxes) { 35 | val i = indexOf(node) 36 | check(i >= 0) 37 | val j = (i + node.value * multiplier).mod(lastIndex) 38 | check(j >= 0) 39 | copyInto(this, minOf(i, j + 1), minOf(i + 1, j), maxOf(i, j + 1)) 40 | this[j] = node 41 | } 42 | } 43 | 44 | companion object { 45 | private const val MULTIPLIER = 811589153L 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rs/build.rs: -------------------------------------------------------------------------------- 1 | use build_const::ConstWriter; 2 | use std::collections::BTreeMap; 3 | use std::env; 4 | use std::fs::{self, File}; 5 | use std::io::{self, BufRead, BufReader, Error, ErrorKind}; 6 | use std::iter::FromIterator; 7 | use std::path::Path; 8 | 9 | fn main() -> io::Result<()> { 10 | for day in 1..=25 { 11 | println!("cargo:rerun-if-changed=../day{}.txt", day); 12 | } 13 | 14 | let days = BTreeMap::from_iter( 15 | fs::read_dir( 16 | Path::new( 17 | &env::var("CARGO_MANIFEST_DIR").map_err(|e| Error::new(ErrorKind::Other, e))?, 18 | ) 19 | .parent() 20 | .ok_or_else(|| Error::new(ErrorKind::Other, "no parent directory"))?, 21 | )? 22 | .filter_map(|entry| Some(entry.ok()?.path())) 23 | .filter(|path| path.is_file()) 24 | .filter_map(|path| { 25 | let name = path.file_name()?.to_str()?; 26 | if !name.starts_with("day") || !name.ends_with(".txt") { 27 | return None; 28 | } 29 | Some(( 30 | name[3..name.len() - 4].parse::().ok()?, 31 | path.to_owned(), 32 | )) 33 | }), 34 | ); 35 | 36 | let mut consts = ConstWriter::for_build("aoc2022")?.finish_dependencies(); 37 | for (day, path) in days { 38 | let lines = BufReader::new(File::open(path)?) 39 | .lines() 40 | .collect::>>()?; 41 | consts.add_value_raw(&format!("DAY{}", day), "&[&str]", &format!("&{:?}", lines)); 42 | } 43 | consts.finish(); 44 | 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/kt.yml: -------------------------------------------------------------------------------- 1 | name: Kotlin CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: [ kt/** ] 7 | pull_request: 8 | branches: [ main ] 9 | paths: [ kt/** ] 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-java@v3 20 | with: 21 | distribution: temurin 22 | java-version: 17 23 | cache: gradle 24 | - uses: actions/cache@v3 25 | with: 26 | key: kt-${{ runner.os }}-konan-${{ hashFiles('**/*.gradle*') }}-build 27 | restore-keys: | 28 | kt-${{ runner.os }}-konan-${{ hashFiles('**/*.gradle*') }}- 29 | kt-${{ runner.os }}-konan- 30 | path: ~/.konan 31 | - uses: gradle/gradle-build-action@v2 32 | with: 33 | arguments: :check :assembleBenchmarks detektAll 34 | build-root-directory: kt 35 | 36 | graalvm-build: 37 | runs-on: ubuntu-latest 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | - uses: graalvm/setup-graalvm@v1 42 | with: 43 | version: latest 44 | java-version: 17 45 | components: native-image 46 | github-token: ${{ secrets.GITHUB_TOKEN }} 47 | cache: gradle 48 | - uses: gradle/gradle-build-action@v2 49 | with: 50 | arguments: :graalvm:nativeCompile :graalvm:nativeTest 51 | build-root-directory: kt 52 | 53 | gradle-wrapper-validation: 54 | runs-on: ubuntu-latest 55 | 56 | steps: 57 | - uses: actions/checkout@v3 58 | - uses: gradle/wrapper-validation-action@v1 59 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day9.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.math.absoluteValue 4 | import kotlin.math.sign 5 | 6 | @Day 7 | class Day9(lines: List) { 8 | private val moves = sequence { 9 | var x = 0 10 | var y = 0 11 | yield(x to y) 12 | for (line in lines) { 13 | when (line[0]) { 14 | 'L' -> repeat(line.drop(2).toInt()) { yield(--x to y) } 15 | 'R' -> repeat(line.drop(2).toInt()) { yield(++x to y) } 16 | 'U' -> repeat(line.drop(2).toInt()) { yield(x to --y) } 17 | 'D' -> repeat(line.drop(2).toInt()) { yield(x to ++y) } 18 | } 19 | } 20 | } 21 | 22 | @Day.Part 23 | fun part1(): Int = moves.follow().toSet().size 24 | 25 | @Day.Part 26 | fun part2(): Int = moves 27 | .follow() // 1 28 | .follow() // 2 29 | .follow() // 3 30 | .follow() // 4 31 | .follow() // 5 32 | .follow() // 6 33 | .follow() // 7 34 | .follow() // 8 35 | .follow() // 9 36 | .toSet() 37 | .size 38 | } 39 | 40 | private fun Sequence.follow(): Sequence = sequence { 41 | var tailX = 0 42 | var tailY = 0 43 | yield(tailX to tailY) 44 | for ((headX, headY) in this@follow) { 45 | val deltaX = headX - tailX 46 | val deltaY = headY - tailY 47 | if (deltaX.absoluteValue <= 1 && deltaY.absoluteValue <= 1) continue 48 | tailX = if (deltaX.absoluteValue >= deltaY.absoluteValue) headX - deltaX.sign else headX 49 | tailY = if (deltaX.absoluteValue <= deltaY.absoluteValue) headY - deltaY.sign else headY 50 | yield(tailX to tailY) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /rs/src/day6.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeSet; 2 | 3 | fn solve(data: &str) -> Option { 4 | let mut window = ['\0'; N]; 5 | for (i, c) in data.chars().enumerate() { 6 | window[i % N] = c; 7 | if i >= N && window.iter().collect::>().len() == N { 8 | return Some(i + 1); 9 | } 10 | } 11 | None 12 | } 13 | 14 | pub fn part1<'a, I, S>(lines: I) -> Option 15 | where 16 | I: IntoIterator, 17 | S: AsRef + 'a, 18 | { 19 | solve::<4>(lines.into_iter().next()?.as_ref()) 20 | } 21 | 22 | pub fn part2<'a, I, S>(lines: I) -> Option 23 | where 24 | I: IntoIterator, 25 | S: AsRef + 'a, 26 | { 27 | solve::<14>(lines.into_iter().next()?.as_ref()) 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | use pretty_assertions::assert_eq; 34 | 35 | #[test] 36 | fn part1_examples() { 37 | assert_eq!(Some(7), part1(&["mjqjpqmgbljsphdztnvjfqwrcgsmlb"])); 38 | assert_eq!(Some(5), part1(&["bvwbjplbgvbhsrlpgdmjqwftvncz"])); 39 | assert_eq!(Some(6), part1(&["nppdvjthqldpwncqszvftbrmjlhg"])); 40 | assert_eq!(Some(10), part1(&["nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg"])); 41 | assert_eq!(Some(11), part1(&["zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw"])); 42 | } 43 | 44 | #[test] 45 | fn part2_examples() { 46 | assert_eq!(Some(19), part2(&["mjqjpqmgbljsphdztnvjfqwrcgsmlb"])); 47 | assert_eq!(Some(23), part2(&["bvwbjplbgvbhsrlpgdmjqwftvncz"])); 48 | assert_eq!(Some(23), part2(&["nppdvjthqldpwncqszvftbrmjlhg"])); 49 | assert_eq!(Some(29), part2(&["nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg"])); 50 | assert_eq!(Some(26), part2(&["zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw"])); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day5.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day5(lines: List) { 5 | private val initialStacks: Map 6 | private val moves: List> 7 | init { 8 | val n = lines.indexOf("") 9 | initialStacks = buildMap { 10 | lines[n - 1].forEachIndexed { i, c -> 11 | if (!c.isDigit()) return@forEachIndexed 12 | for (j in 0 until n) { 13 | if (lines[j].getOrNull(i)?.isWhitespace() == false) { 14 | put(c, buildString(n - 1 - j) { for (k in j until n - 1) append(lines[k][i]) }) 15 | break 16 | } 17 | } 18 | } 19 | } 20 | moves = buildList(lines.size - n - 1) { 21 | for (i in n + 1 until lines.size) { 22 | val (num, x, y) = PATTERN.matchEntire(lines[i])!!.destructured 23 | add(Triple(num.toInt(), x.single(), y.single())) 24 | } 25 | } 26 | } 27 | 28 | @Day.Part 29 | fun part1(): String = solve(true) 30 | 31 | @Day.Part 32 | fun part2(): String = solve(false) 33 | 34 | private fun solve(reverse: Boolean): String { 35 | val stacks = initialStacks.toMutableMap() 36 | for ((num, x, y) in moves) { 37 | val source = stacks.getValue(x) 38 | stacks[x] = source.drop(num) 39 | stacks[y] = (if (reverse) source.take(num).reversed() else source.take(num)) + stacks.getValue(y) 40 | } 41 | return stacks.values.joinToString("") { it[0].toString() } 42 | } 43 | 44 | companion object { 45 | private val PATTERN = """move (\d+) from (\d) to (\d)""".toRegex() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/get-inputs.yml: -------------------------------------------------------------------------------- 1 | name: Get inputs 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | year: 7 | required: false 8 | type: number 9 | secrets: 10 | SESSION: 11 | required: true 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Get Day 19 | id: get-day 20 | run: | 21 | from datetime import datetime, timedelta, timezone 22 | import os 23 | tz = timezone(timedelta(hours=-5)) 24 | year = ${{ inputs.year }} or 2022 25 | day = max(0, min(25, (datetime.now(tz) - datetime(year, 12, 1, 0, 0, 0, 0, tz)).days + 1)) 26 | with open(os.environ["GITHUB_OUTPUT"], "a") as f: 27 | print("year=" + str(year), file=f) 28 | print("day=" + str(min(25, max(0, day))), file=f) 29 | print("days=" + ' '.join(map(str, range(1, day + 1))), file=f) 30 | shell: python 31 | - id: cache 32 | uses: actions/cache@v3 33 | with: 34 | key: inputs-${{ steps.get-day.outputs.day }} 35 | restore-keys: inputs- 36 | path: day*.txt 37 | - name: Download inputs 38 | if: steps.cache.outputs.cache-hit != 'true' 39 | run: | 40 | for day in ${{ steps.get-day.outputs.days }}; do 41 | [[ -e day$day.txt ]] || curl -A https://github.com/ephemient/aoc2022/blob/main/.github/workflows/get-inputs.yml -b session=$SESSION -o day$day.txt -f https://adventofcode.com/${{ steps.get-day.outputs.year }}/day/$day/input 42 | done 43 | shell: bash --noprofile --norc -euxo pipefail {0} 44 | env: 45 | SESSION: ${{ secrets.SESSION }} 46 | - uses: actions/upload-artifact@v3 47 | with: 48 | name: inputs 49 | path: day*.txt 50 | -------------------------------------------------------------------------------- /kt/src/nativeTest/kotlin/com/github/ephemient/aoc2022/Test.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlinx.cinterop.* 4 | import platform.posix.* 5 | import kotlin.test.Ignore 6 | 7 | actual fun getTestInput(day: Int, extra: String): List { 8 | val dataDir = checkNotNull( 9 | getenv("aoc2022_test_datadir")?.toKString()?.ifEmpty { null } 10 | ) { "missing \$aoc2022_test_datadir" } 11 | val filename = "day$day$extra.txt" 12 | val file = checkNotNull(fopen("$dataDir/$filename", "r")) { 13 | val errno = errno 14 | "fopen(\$aoc2022_test_datadir/$filename): ${strerror(errno)?.toKString() ?: errno.toString()}" 15 | } 16 | try { 17 | return buildList { 18 | var previousNewline = true 19 | val buffer = ByteArray(BUFSIZ) 20 | while (buffer.usePinned { fgets(it.addressOf(0), buffer.size, file) } != null) { 21 | val eos = buffer.indexOf(0) 22 | val nextNewline = eos > 0 && buffer[eos - 1] == '\n'.code.toByte() 23 | val eol = if (nextNewline) eos - 1 else eos 24 | if (previousNewline) { 25 | add(buffer.toKString(endIndex = eol)) 26 | } else { 27 | this[lastIndex] = last() + buffer.toKString(endIndex = eol) 28 | } 29 | previousNewline = nextNewline 30 | } 31 | if (ferror(file) != 0) { 32 | val errno = errno 33 | fprintf( 34 | stderr, 35 | "fgets(\$aoc2022_test_datadir/$filename) = ${strerror(errno)?.toKString() ?: errno.toString()}" 36 | ) 37 | } 38 | } 39 | } finally { 40 | fclose(file) 41 | } 42 | } 43 | 44 | actual typealias SlowTest = Ignore 45 | -------------------------------------------------------------------------------- /hs/src/Day13.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day13 3 | Description: 4 | -} 5 | {-# LANGUAGE TypeFamilies #-} 6 | module Day13 (day13a, day13b) where 7 | 8 | import Data.List (partition) 9 | import Data.List.Split (chunksOf) 10 | import Data.Ord (comparing) 11 | import Data.Text (Text) 12 | import Data.Void (Void) 13 | import Text.Megaparsec (MonadParsec, ParseErrorBundle, Token, (<|>), between, parse, sepBy, sepEndBy, single, some) 14 | import Text.Megaparsec.Char (eol) 15 | import qualified Text.Megaparsec.Char.Lexer as L (decimal) 16 | 17 | data Packet a = Literal a | List [Packet a] deriving (Eq) 18 | instance (Ord a) => Ord (Packet a) where 19 | compare (Literal x) (Literal y) = compare x y 20 | compare x@(Literal _) y = compare (List [x]) y 21 | compare x y@(Literal _) = compare x (List [y]) 22 | compare (List x) (List y) = foldr (<>) (comparing (map $ const ()) x y) $ zipWith compare x y 23 | 24 | parser :: (MonadParsec e s m, Token s ~ Char, Integral a) => m (Packet a) 25 | parser = Literal <$> L.decimal <|> 26 | List <$> between (single '[') (single ']') (parser `sepBy` single ',') 27 | 28 | day13a :: Text -> Either (ParseErrorBundle Text Void) Int 29 | day13a input = do 30 | packets <- parse (parser `sepEndBy` some eol) "day13.txt" input 31 | pure $ sum [i | (i, [x, y]) <- zip [1..] $ chunksOf 2 packets , x <= y] 32 | 33 | day13b :: Text -> Either (ParseErrorBundle Text Void) Int 34 | day13b input = do 35 | packets <- parse (parser `sepEndBy` some eol) "day13.txt" input 36 | let a = List [List [Literal 2]] 37 | b = List [List [Literal 6]] 38 | (belowA, aboveA) = partition (< a) packets 39 | belowB = filter (< b) aboveA 40 | x = 1 + length belowA 41 | y = 1 + x + length belowB 42 | pure $ x * y 43 | -------------------------------------------------------------------------------- /kt/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | dependency-updates = "0.44.0" 3 | detekt = "1.22.0" 4 | graal-sdk = "22.3.0" 5 | junit-jupiter = "5.9.1" 6 | kotlin = "1.7.21" 7 | kotlinpoet = "1.12.0" 8 | kotlinx-benchmark = "0.4.6" 9 | kotlinx-coroutines = "1.6.4" 10 | ksp = "1.7.21-1.0.8" 11 | native-image-plugin = "0.9.19" 12 | 13 | [plugins] 14 | dependency-updates = { id = "com.github.ben-manes.versions", version.ref = "dependency-updates" } 15 | detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } 16 | kotlinx-benchmark = { id = "org.jetbrains.kotlinx.benchmark", version.ref = "kotlinx-benchmark" } 17 | ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } 18 | native-image = { id = "org.graalvm.buildtools.native", version.ref = "native-image-plugin" } 19 | 20 | [libraries] 21 | detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } 22 | graal-sdk = { module = "org.graalvm.sdk:graal-sdk", version.ref = "graal-sdk" } 23 | junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter" } 24 | junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter" } 25 | kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" } 26 | kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet" } 27 | kotlinx-benchmark = { module = "org.jetbrains.kotlinx:kotlinx-benchmark-runtime", version.ref = "kotlinx-benchmark" } 28 | kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } 29 | kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } 30 | ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } 31 | -------------------------------------------------------------------------------- /kt/src/jvmTest/resources/day10.txt: -------------------------------------------------------------------------------- 1 | addx 15 2 | addx -11 3 | addx 6 4 | addx -3 5 | addx 5 6 | addx -1 7 | addx -8 8 | addx 13 9 | addx 4 10 | noop 11 | addx -1 12 | addx 5 13 | addx -1 14 | addx 5 15 | addx -1 16 | addx 5 17 | addx -1 18 | addx 5 19 | addx -1 20 | addx -35 21 | addx 1 22 | addx 24 23 | addx -19 24 | addx 1 25 | addx 16 26 | addx -11 27 | noop 28 | noop 29 | addx 21 30 | addx -15 31 | noop 32 | noop 33 | addx -3 34 | addx 9 35 | addx 1 36 | addx -3 37 | addx 8 38 | addx 1 39 | addx 5 40 | noop 41 | noop 42 | noop 43 | noop 44 | noop 45 | addx -36 46 | noop 47 | addx 1 48 | addx 7 49 | noop 50 | noop 51 | noop 52 | addx 2 53 | addx 6 54 | noop 55 | noop 56 | noop 57 | noop 58 | noop 59 | addx 1 60 | noop 61 | noop 62 | addx 7 63 | addx 1 64 | noop 65 | addx -13 66 | addx 13 67 | addx 7 68 | noop 69 | addx 1 70 | addx -33 71 | noop 72 | noop 73 | noop 74 | addx 2 75 | noop 76 | noop 77 | noop 78 | addx 8 79 | noop 80 | addx -1 81 | addx 2 82 | addx 1 83 | noop 84 | addx 17 85 | addx -9 86 | addx 1 87 | addx 1 88 | addx -3 89 | addx 11 90 | noop 91 | noop 92 | addx 1 93 | noop 94 | addx 1 95 | noop 96 | noop 97 | addx -13 98 | addx -19 99 | addx 1 100 | addx 3 101 | addx 26 102 | addx -30 103 | addx 12 104 | addx -1 105 | addx 3 106 | addx 1 107 | noop 108 | noop 109 | noop 110 | addx -9 111 | addx 18 112 | addx 1 113 | addx 2 114 | noop 115 | noop 116 | addx 9 117 | noop 118 | noop 119 | noop 120 | addx -1 121 | addx 2 122 | addx -37 123 | addx 1 124 | addx 3 125 | noop 126 | addx 15 127 | addx -21 128 | addx 22 129 | addx -6 130 | addx 1 131 | noop 132 | addx 2 133 | addx 1 134 | noop 135 | addx -10 136 | noop 137 | noop 138 | addx 20 139 | addx 1 140 | addx 2 141 | addx 2 142 | addx -6 143 | addx -11 144 | noop 145 | noop 146 | noop 147 | -------------------------------------------------------------------------------- /hs/src/Day12.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day12 3 | Description: 4 | -} 5 | {-# LANGUAGE ViewPatterns #-} 6 | module Day12 (day12) where 7 | 8 | import Control.Arrow (first, second) 9 | import Data.Char (ord) 10 | import Data.Ix (inRange) 11 | import Data.List (foldl') 12 | import qualified Data.Sequence as Seq (Seq((:<|)), (><), fromList, singleton) 13 | import qualified Data.Set as Set (insert, notMember, singleton) 14 | import Data.Text (Text) 15 | import qualified Data.Text as T (index, length, lines, unpack) 16 | import qualified Data.Vector as V ((!), (!?), fromList) 17 | 18 | bfs :: (Ord a) => (a -> [a]) -> a -> [(a, Int)] 19 | bfs next start = go (Set.singleton start) (Seq.singleton (start, 0)) where 20 | go seen ((a, !n) Seq.:<| q) = (a, n) : go seen' q' where 21 | seen' = foldl' (flip Set.insert) seen next' 22 | q' = q Seq.>< Seq.fromList [(a', n + 1) | a' <- next'] 23 | next' = filter (`Set.notMember` seen) $ next a 24 | go _ _ = [] 25 | 26 | ord' :: Char -> Int 27 | ord' 'S' = ord 'a' 28 | ord' 'E' = ord 'z' 29 | ord' a = ord a 30 | 31 | day12 :: Text -> (Maybe Int, Maybe Int) 32 | day12 input = foldr ans (Nothing, Nothing) $ bfs next start where 33 | lines = T.lines input 34 | start:_ = [(y, x) | (y, line) <- zip [0..] lines, (x, 'E') <- zip [0..] $ T.unpack line] 35 | heights = V.fromList lines 36 | get (y, x) = heights V.! y `T.index` x 37 | next (y, x) = filter (ok $ get (y, x)) [(y - 1, x), (y, x - 1), (y, x + 1), (y + 1, x)] 38 | ok a (y, x) | Just line <- heights V.!? y = 39 | inRange (0, T.length line - 1) x && ord' a - ord' (T.index line x) <= 1 40 | ok _ _ = False 41 | ans (get -> 'S', n) = first . const $ Just n 42 | ans (get -> 'a', n) = second . const $ Just n 43 | ans _ = id 44 | -------------------------------------------------------------------------------- /rs/src/util.rs: -------------------------------------------------------------------------------- 1 | pub struct ArrayChunks<'a, const N: usize, T> { 2 | remaining: &'a [T], 3 | } 4 | impl<'a, const N: usize, T> Iterator for ArrayChunks<'a, N, T> { 5 | type Item = &'a [T; N]; 6 | fn next(&mut self) -> Option { 7 | if self.remaining.len() >= N { 8 | let (chunk, remaining) = self.remaining.split_at(N); 9 | self.remaining = remaining; 10 | chunk.try_into().ok() 11 | } else { 12 | None 13 | } 14 | } 15 | } 16 | pub fn array_chunks(array: &[T]) -> ArrayChunks { 17 | ArrayChunks { remaining: array } 18 | } 19 | 20 | pub struct IterChunks { 21 | iter: I, 22 | } 23 | impl Iterator for IterChunks 24 | where 25 | I: Iterator, 26 | { 27 | type Item = [::Item; N]; 28 | fn next(&mut self) -> Option { 29 | let mut vec = Vec::with_capacity(N); 30 | for _ in 0..N { 31 | vec.push(self.iter.next()?); 32 | } 33 | vec.try_into().ok() 34 | } 35 | } 36 | pub fn iter_chunks(iter: I) -> IterChunks { 37 | IterChunks { iter } 38 | } 39 | 40 | pub struct IterPairs { 41 | prev: Option<::Item>, 42 | iter: I, 43 | } 44 | impl Iterator for IterPairs 45 | where 46 | I: Iterator, 47 | ::Item: Clone, 48 | { 49 | type Item = (::Item, ::Item); 50 | fn next(&mut self) -> Option { 51 | let prev = self.prev.take().or_else(|| self.iter.next())?; 52 | let next = self.iter.next()?; 53 | self.prev = Some(next.clone()); 54 | Some((prev, next)) 55 | } 56 | } 57 | pub fn iter_pairs(iter: I) -> IterPairs { 58 | IterPairs { prev: None, iter } 59 | } 60 | -------------------------------------------------------------------------------- /hs/src/Day18.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day18 3 | Description: 4 | -} 5 | module Day18 (day18a, day18b) where 6 | 7 | import Common (count) 8 | import Control.Arrow ((&&&)) 9 | import Data.Ix (inRange) 10 | import Data.List (foldl') 11 | import Data.Semigroup (Max(Max), Min(Min)) 12 | import qualified Data.Set as Set (fromList, insert, member, notMember, singleton) 13 | import Data.Text (Text) 14 | import qualified Data.Text as T (cons, lines, unpack, snoc) 15 | 16 | neighbors :: (Num a) => (a, a, a) -> [(a, a, a)] 17 | neighbors (x, y, z) = 18 | [(x - 1, y, z), (x + 1, y, z), (x, y - 1, z), (x, y + 1, z), (x, y, z - 1), (x, y, z + 1)] 19 | 20 | day18a :: Text -> Int 21 | day18a input = count (`Set.notMember` points') $ points >>= neighbors where 22 | points = read @(Int, Int, Int) . T.unpack . T.cons '(' . flip T.snoc ')' <$> T.lines input 23 | points' = Set.fromList points 24 | 25 | day18b :: Text -> Int 26 | day18b input = count (`Set.member` outside) $ points >>= neighbors where 27 | points = read @(Int, Int, Int) . T.unpack . T.cons '(' . flip T.snoc ')' <$> T.lines input 28 | points' = Set.fromList points 29 | ((Min minX, Max maxX), (Min minY, Max maxY), (Min minZ, Max maxZ)) = mconcat 30 | [(Min &&& Max $ x, Min &&& Max $ y, Min &&& Max $ z) | (x, y, z) <- points] 31 | outside = let start = (minX - 1, minY - 1, minZ - 1) in dfs (Set.singleton start) [start] 32 | dfs seen [] = seen 33 | dfs seen (point:q) = dfs (foldl' (flip Set.insert) seen next) $ next ++ q where 34 | next = filter ok $ neighbors point 35 | ok point@(x, y, z) = 36 | inRange (minX - 1, maxX + 1) x && 37 | inRange (minY - 1, maxY + 1) y && 38 | inRange (minZ - 1, maxZ + 1) z && 39 | Set.notMember point points' && 40 | Set.notMember point seen 41 | -------------------------------------------------------------------------------- /rs/src/day3.rs: -------------------------------------------------------------------------------- 1 | use super::util::iter_chunks; 2 | 3 | fn items(input: &[u8]) -> u64 { 4 | input.iter().fold(0, |acc, byte| { 5 | acc | (1 6 | << (if byte & 32 != 0 { 7 | byte & 31 8 | } else { 9 | (byte & 31) + 26 10 | })) 11 | }) 12 | } 13 | 14 | pub fn part1<'a, I, S>(lines: I) -> u32 15 | where 16 | I: IntoIterator, 17 | S: AsRef + 'a, 18 | { 19 | lines 20 | .into_iter() 21 | .map(|line| -> u32 { 22 | let bytes = line.as_ref().as_bytes(); 23 | let (first, second) = bytes.split_at(bytes.len() / 2); 24 | let bits = items(first) & items(second); 25 | (0..u64::BITS).filter(|b| bits & (1 << b) != 0).sum() 26 | }) 27 | .sum() 28 | } 29 | 30 | pub fn part2<'a, I, S>(lines: I) -> u32 31 | where 32 | I: IntoIterator, 33 | S: AsRef + 'a, 34 | { 35 | iter_chunks(lines.into_iter()) 36 | .filter_map(|chunk: [_; 3]| { 37 | chunk 38 | .into_iter() 39 | .map(|line| items(line.as_ref().as_bytes())) 40 | .reduce(|x, y| x & y) 41 | }) 42 | .map(u64::trailing_zeros) 43 | .sum() 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | use pretty_assertions::assert_eq; 50 | 51 | static EXAMPLE: &[&str] = &[ 52 | "vJrwpWtwJgWrhcsFMMfFFhFp", 53 | "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL", 54 | "PmmdzqPrVvPwwTWBwg", 55 | "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn", 56 | "ttgJtRGJQctTZtZT", 57 | "CrZsJsPPZsGzwwsLwLmpwMDw", 58 | ]; 59 | 60 | #[test] 61 | fn part1_examples() { 62 | assert_eq!(157, part1(EXAMPLE)); 63 | } 64 | 65 | #[test] 66 | fn part2_examples() { 67 | assert_eq!(70, part2(EXAMPLE)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /py/aoc2022/day9.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 9: Rope Bridge 3 | """ 4 | 5 | 6 | def _moves(lines): 7 | current_x, current_y = 0, 0 8 | yield current_x, current_y 9 | for line in lines: 10 | direction, count = line.split() 11 | for _ in range(int(count)): 12 | match direction: 13 | case "L": 14 | current_x -= 1 15 | case "R": 16 | current_x += 1 17 | case "U": 18 | current_y -= 1 19 | case "D": 20 | current_y += 1 21 | yield current_x, current_y 22 | 23 | 24 | def _follow(points): 25 | tail_x, tail_y = 0, 0 26 | yield tail_x, tail_y 27 | for head_x, head_y in points: 28 | delta_x, delta_y = head_x - tail_x, head_y - tail_y 29 | if abs(delta_x) <= 1 and abs(delta_y) <= 1: 30 | continue 31 | if abs(delta_x) < abs(delta_y): 32 | tail_x, tail_y = head_x, head_y - delta_y // abs(delta_y) 33 | elif abs(delta_x) > abs(delta_y): 34 | tail_x, tail_y = head_x - delta_x // abs(delta_x), head_y 35 | else: 36 | tail_x, tail_y = head_x - delta_x // abs(delta_x), head_y - delta_y // abs( 37 | delta_y 38 | ) 39 | yield tail_x, tail_y 40 | 41 | 42 | def part1(lines): 43 | """ 44 | >>> part1(["R 4", "U 4", "L 3", "D 1", "R 4", "D 1", "L 5", "R 2"]) 45 | 13 46 | """ 47 | return len(set(_follow(_moves(lines)))) 48 | 49 | 50 | def part2(lines): 51 | """ 52 | >>> part2(["R 5", "U 8", "L 8", "D 3", "R 17", "D 10", "L 25", "U 20"]) 53 | 36 54 | """ 55 | # fmt: off 56 | # pylint: disable=line-too-long 57 | return len(set(_follow(_follow(_follow(_follow(_follow(_follow(_follow(_follow(_follow(_moves(lines)))))))))))) 58 | # fmt: on 59 | 60 | 61 | parts = (part1, part2) 62 | -------------------------------------------------------------------------------- /py/aoc2022/day12.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 12: Hill Climbing Algorithm 3 | """ 4 | 5 | from collections import deque 6 | 7 | 8 | def _bfs(start, neighbors): 9 | seen = {start} 10 | queue = deque(((start, 0),)) 11 | while queue: 12 | item, depth = queue.popleft() 13 | yield item, depth 14 | for neighbor in neighbors(item): 15 | if neighbor in seen: 16 | continue 17 | seen.add(neighbor) 18 | queue.append((neighbor, depth + 1)) 19 | 20 | 21 | def both_parts(lines): 22 | """ 23 | >>> both_parts(["Sabqponm", "abcryxxl", "accszExk", "acctuvwj", "abdefghi"]) 24 | (31, 29) 25 | """ 26 | (start,) = ( 27 | (row, col) 28 | for row, line in enumerate(lines) 29 | for col, c in enumerate(line) 30 | if c == "E" 31 | ) 32 | 33 | def neighbors(point): 34 | row0, col0 = point 35 | src = lines[row0][col0] 36 | src = ord("a" if src == "S" else "z" if src == "E" else src) 37 | for row1 in range(row0 - 1, row0 + 2): 38 | if row1 not in range(len(lines)): 39 | continue 40 | for col1 in range(col0 - 1, col0 + 2) if row0 == row1 else (col0,): 41 | if col1 not in range(len(lines[row1])): 42 | continue 43 | dst = lines[row1][col1] 44 | dst = ord("a" if dst == "S" else "z" if dst == "E" else dst) 45 | if src - dst <= 1: 46 | yield row1, col1 47 | 48 | part1, part2 = None, None 49 | for (row, col), depth in _bfs(start, neighbors): 50 | if lines[row][col] == "S" and part1 is None: 51 | part1 = depth 52 | elif lines[row][col] == "a" and part2 is None: 53 | part2 = depth 54 | if part1 is not None and part2 is not None: 55 | break 56 | return part1, part2 57 | 58 | 59 | parts = (both_parts,) 60 | -------------------------------------------------------------------------------- /py/aoc2022/day7.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 7: No Space Left On Device 3 | """ 4 | 5 | from collections import defaultdict 6 | 7 | SAMPLE_INPUT = [ 8 | "$ cd /", 9 | "$ ls", 10 | "dir a", 11 | "14848514 b.txt", 12 | "8504156 c.dat", 13 | "dir d", 14 | "$ cd a", 15 | "$ ls", 16 | "dir e", 17 | "29116 f", 18 | "2557 g", 19 | "62596 h.lst", 20 | "$ cd e", 21 | "$ ls", 22 | "584 i", 23 | "$ cd ..", 24 | "$ cd ..", 25 | "$ cd d", 26 | "$ ls", 27 | "4060174 j", 28 | "8033020 d.log", 29 | "5626152 d.ext", 30 | "7214296 k", 31 | ] 32 | 33 | 34 | def _parse(lines): 35 | cwd, sizes = "", defaultdict(int) 36 | for line in lines: 37 | match line.split(): 38 | case ("$", "cd", "/"): 39 | cwd = "" 40 | case ("$", "cd", ".."): 41 | cwd = cwd[: cwd.rindex("/") if "/" in cwd else 0] 42 | case ("$", "cd", path): 43 | cwd = f"{cwd}/{path}" if cwd else path 44 | case (size, *_): 45 | try: 46 | size = int(size) 47 | except ValueError: 48 | continue 49 | path = cwd 50 | while True: 51 | sizes[path] += size 52 | if not path: 53 | break 54 | path = path[: path.rindex("/") if "/" in path else 0] 55 | return sizes 56 | 57 | 58 | def part1(lines): 59 | """ 60 | >>> part1(SAMPLE_INPUT) 61 | 95437 62 | """ 63 | return sum(size for size in _parse(lines).values() if size <= 100000) 64 | 65 | 66 | def part2(lines): 67 | """ 68 | >>> part2(SAMPLE_INPUT) 69 | 24933642 70 | """ 71 | sizes = _parse(lines) 72 | total = sizes[""] 73 | return min(size for size in sizes.values() if 70000000 - (total - size) >= 30000000) 74 | 75 | 76 | parts = (part1, part2) 77 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day14.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day14(lines: List) { 5 | private val result = run { 6 | var maxX = 0 7 | var maxY = 0 8 | val points = lines.map { line -> 9 | line.split(" -> ").map { 10 | val (x, y) = it.split(',', limit = 2) 11 | x.toInt() to y.toInt() 12 | }.onEach { (x, y) -> 13 | check(x >= 0 && y >= 0) 14 | if (x > maxX) maxX = x 15 | if (y > maxY) maxY = y 16 | } 17 | } 18 | val width = maxOf(maxX, 500 + maxY + 1) + 1 19 | val blocks = BooleanArray(width * (maxY + 2)) 20 | for (segments in points) { 21 | segments.reduce { point1, point2 -> 22 | for (x in minOf(point1.first, point2.first)..maxOf(point1.first, point2.first)) { 23 | for (y in minOf(point1.second, point2.second)..maxOf(point1.second, point2.second)) { 24 | blocks[x + y * width] = true 25 | } 26 | } 27 | point2 28 | } 29 | } 30 | var count = 0 31 | var countAtMaxY = -1 32 | DeepRecursiveFunction { (x, y) -> 33 | check(x in 0 until width) 34 | if (blocks[x + y * width]) return@DeepRecursiveFunction 35 | if (y == maxY && countAtMaxY < 0) countAtMaxY = count 36 | if (y <= maxY) { 37 | callRecursive(x to y + 1) 38 | callRecursive(x - 1 to y + 1) 39 | callRecursive(x + 1 to y + 1) 40 | } 41 | blocks[x + y * width] = true 42 | count++ 43 | }(500 to 0) 44 | check(countAtMaxY >= 0) 45 | countAtMaxY to count 46 | } 47 | 48 | @Day.Part 49 | fun part1(): Int = result.first 50 | 51 | @Day.Part 52 | fun part2(): Int = result.second 53 | } 54 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day12.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day12(private val lines: List) { 5 | private val result = bfs( 6 | lines.withIndex().firstNotNullOf { (y, line) -> line.indexOf('E').takeIf { it >= 0 }?.let { x -> y to x } } 7 | ) { to -> to.filteredNeighbors { from -> canMove(from, to) } } 8 | .scan(-1 to -1) { result, (value, point) -> 9 | when (lines[point.first][point.second]) { 10 | 'S' -> if (result.first < 0) return@scan result.copy(first = value) 11 | 'a' -> if (result.second < 0) return@scan result.copy(second = value) 12 | } 13 | result 14 | } 15 | .first { it.first >= 0 && it.second >= 0 } 16 | 17 | @Day.Part 18 | fun part1(): Int = result.first 19 | 20 | @Day.Part 21 | fun part2(): Int = result.second 22 | 23 | private inline fun IntPair.filteredNeighbors(predicate: (IntPair) -> Boolean): List = 24 | arrayOf(first - 1 to second, first to second - 1, first to second + 1, first + 1 to second) 25 | .filter { it.first in lines.indices && it.second in lines[it.first].indices && predicate(it) } 26 | 27 | private fun canMove(from: IntPair, to: IntPair): Boolean { 28 | val a = lines[from.first][from.second] 29 | val b = lines[to.first][to.second] 30 | return b.mappedCode - a.mappedCode <= 1 31 | } 32 | } 33 | 34 | private fun bfs(start: T, next: (T) -> Iterable): Sequence> = sequence { 35 | val seen = mutableSetOf(start) 36 | val queue = ArrayDeque(listOf(IndexedValue(0, start))) 37 | while (queue.isNotEmpty()) { 38 | val (i, a) = queue.removeFirst().also { yield(it) } 39 | for (b in next(a)) if (seen.add(b)) queue.add(IndexedValue(i + 1, b)) 40 | } 41 | } 42 | 43 | private val Char.mappedCode: Int 44 | get() = when (this) { 45 | 'S' -> 'a' 46 | 'E' -> 'z' 47 | else -> this 48 | }.code 49 | -------------------------------------------------------------------------------- /py/aoc2022/day18.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 18: Boiling Boulders 3 | """ 4 | 5 | SAMPLE_INPUT = [ 6 | "2,2,2", 7 | "1,2,2", 8 | "3,2,2", 9 | "2,1,2", 10 | "2,3,2", 11 | "2,2,1", 12 | "2,2,3", 13 | "2,2,4", 14 | "2,2,6", 15 | "1,2,5", 16 | "3,2,5", 17 | "2,1,5", 18 | "2,3,5", 19 | ] 20 | 21 | 22 | def _parse(lines): 23 | for line in lines: 24 | x, y, z = line.rstrip().split(",", 2) 25 | yield int(x), int(y), int(z) 26 | 27 | 28 | def _neighbors(point): 29 | x, y, z = point 30 | return [ 31 | (x - 1, y, z), 32 | (x + 1, y, z), 33 | (x, y - 1, z), 34 | (x, y + 1, z), 35 | (x, y, z - 1), 36 | (x, y, z + 1), 37 | ] 38 | 39 | 40 | def part1(lines): 41 | """ 42 | >>> part1(SAMPLE_INPUT) 43 | 64 44 | """ 45 | points = set(_parse(lines)) 46 | return sum( 47 | neighbor not in points for point in points for neighbor in _neighbors(point) 48 | ) 49 | 50 | 51 | def part2(lines): 52 | """ 53 | >>> part2(SAMPLE_INPUT) 54 | 58 55 | """ 56 | points = set(_parse(lines)) 57 | min_x, max_x = min(x for x, _, _ in points), max(x for x, _, _ in points) 58 | min_y, max_y = min(y for _, y, _ in points), max(y for _, y, _ in points) 59 | min_z, max_z = min(z for _, _, z in points), max(z for _, _, z in points) 60 | outside = {(min_x - 1, min_y - 1, min_z - 1)} 61 | queue = list(outside) 62 | while queue: 63 | for point in _neighbors(queue.pop()): 64 | x, y, z = point 65 | if ( 66 | min_x - 1 <= x <= max_x + 1 67 | and min_y - 1 <= y <= max_y + 1 68 | and min_z - 1 <= z <= max_z + 1 69 | and point not in points 70 | and point not in outside 71 | ): 72 | outside.add(point) 73 | queue.append(point) 74 | return sum( 75 | neighbor in outside for point in points for neighbor in _neighbors(point) 76 | ) 77 | 78 | 79 | parts = (part1, part2) 80 | -------------------------------------------------------------------------------- /py/aoc2022/day24.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 24: Blizzard Basin 3 | """ 4 | 5 | import heapq 6 | 7 | SAMPLE_INPUT = ["#.######", "#>>.<^<#", "#.<..<<#", "#>v.><>#", "#<^v^^>#", "######.#"] 8 | 9 | 10 | def _search(lines, start, end, time=0): 11 | end_x, end_y = end 12 | seen, queue = {(start, time)}, [(0, (start, time))] 13 | while queue: 14 | position, time = heapq.heappop(queue)[1] 15 | if position == end: 16 | return time 17 | x, y = position 18 | time += 1 19 | for x, y in [(x - 1, y), (x, y - 1), (x, y), (x, y + 1), (x + 1, y)]: 20 | if y < 0 or y >= len(lines) or x < 1 or x >= len(line := lines[y]) - 1: 21 | continue 22 | if y in (0, len(lines) - 1): 23 | if line[x] != ".": 24 | continue 25 | elif lines[y][(x - 1 + time) % (len(line) - 2) + 1] == "<": 26 | continue 27 | elif lines[y][(x - 1 - time) % (len(line) - 2) + 1] == ">": 28 | continue 29 | elif lines[(y - 1 + time) % (len(lines) - 2) + 1][x] == "^": 30 | continue 31 | elif lines[(y - 1 - time) % (len(lines) - 2) + 1][x] == "v": 32 | continue 33 | state = ((x, y), time) 34 | if state not in seen: 35 | seen.add(state) 36 | heapq.heappush(queue, (time + abs(x - end_x) + abs(y - end_y), state)) 37 | raise LookupError() 38 | 39 | 40 | def part1(lines): 41 | """ 42 | >>> part1(SAMPLE_INPUT) 43 | 18 44 | """ 45 | lines = [line.rstrip() for line in lines] 46 | return _search(lines, (1, 0), (len(lines[-1]) - 2, len(lines) - 1)) 47 | 48 | 49 | def part2(lines): 50 | """ 51 | >>> part2(SAMPLE_INPUT) 52 | 54 53 | """ 54 | lines = [line.rstrip() for line in lines] 55 | start = (1, 0) 56 | end = (len(lines[-1]) - 2, len(lines) - 1) 57 | return _search( 58 | lines, start, end, _search(lines, end, start, _search(lines, start, end)) 59 | ) 60 | 61 | 62 | parts = (part1, part2) 63 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day18.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day18(lines: List) { 5 | private val points = buildSet { 6 | lines.mapTo(this) { line -> 7 | val (x, y, z) = line.split(',', limit = 3) 8 | IntTriple(x.toInt(), y.toInt(), z.toInt()) 9 | } 10 | } 11 | 12 | @Day.Part 13 | fun part1(): Int = points.sumOf { point -> point.neighbors().count { it !in points } } 14 | 15 | @Day.Part 16 | @Suppress("CyclomaticComplexMethod") 17 | fun part2(): Int { 18 | var minX = Int.MAX_VALUE 19 | var maxX = Int.MIN_VALUE 20 | var minY = Int.MAX_VALUE 21 | var maxY = Int.MIN_VALUE 22 | var minZ = Int.MAX_VALUE 23 | var maxZ = Int.MIN_VALUE 24 | for ((x, y, z) in points) { 25 | if (x < minX) minX = x 26 | if (x > maxX) maxX = x 27 | if (y < minY) minY = y 28 | if (y > maxY) maxY = y 29 | if (z < minZ) minZ = z 30 | if (z > maxZ) maxZ = z 31 | } 32 | val outside = buildSet { 33 | val queue = mutableListOf(IntTriple(minX - 1, minY - 1, minZ - 1).also { add(it) }) 34 | while (queue.isNotEmpty()) { 35 | for (neighbor in queue.removeLast().neighbors()) { 36 | neighbor.first in minX - 1..maxX + 1 && 37 | neighbor.second in minY - 1..maxY + 1 && 38 | neighbor.third in minZ - 1..maxZ + 1 && 39 | neighbor !in points && 40 | add(neighbor) && 41 | queue.add(neighbor) 42 | } 43 | } 44 | } 45 | return points.sumOf { point -> point.neighbors().count { it in outside } } 46 | } 47 | } 48 | 49 | private fun IntTriple.neighbors() = listOf( 50 | copy(first = first - 1), 51 | copy(first = first + 1), 52 | copy(second = second - 1), 53 | copy(second = second + 1), 54 | copy(third = third - 1), 55 | copy(third = third + 1), 56 | ) 57 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day24.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.math.abs 4 | 5 | @Day 6 | class Day24(private val lines: List) { 7 | private val start = lines.first().indexOf('.') to 0 8 | private val end = lines.last().lastIndexOf('.') to lines.lastIndex 9 | 10 | @Suppress("ComplexCondition") 11 | private fun isFree(x: Int, y: Int, time: Int): Boolean = 12 | if (x <= 0 || y <= 0 || y >= lines.lastIndex || x >= lines.getOrElse(y) { "" }.lastIndex) { 13 | lines.getOrNull(y)?.getOrNull(x) == '.' 14 | } else { 15 | lines[y][(x - 1 + time).mod(lines[y].length - 2) + 1] != '<' && 16 | lines[y][(x - 1 - time).mod(lines[y].length - 2) + 1] != '>' && 17 | lines[(y - 1 + time).mod(lines.size - 2) + 1][x] != '^' && 18 | lines[(y - 1 - time).mod(lines.size - 2) + 1][x] != 'v' 19 | } 20 | 21 | private fun search(start: IntPair, end: IntPair, startTime: Int = 0): Int { 22 | val (endX, endY) = end 23 | val seen = mutableSetOf(IndexedValue(startTime, start)) 24 | val queue = PriorityQueue(compareBy(IndexedValue>::index)) 25 | queue.add(IndexedValue(0, IndexedValue(startTime, start))) 26 | while (!queue.isEmpty()) { 27 | val entry = queue.remove().value 28 | val (time, position) = entry 29 | if (position == end) return time 30 | val (x, y) = position 31 | for ((x2, y2) in arrayOf(x - 1 to y, x to y - 1, x to y, x to y + 1, x + 1 to y)) { 32 | if (!isFree(x2, y2, time + 1)) continue 33 | val state = IndexedValue(time + 1, x2 to y2) 34 | if (seen.add(state)) queue.add(IndexedValue(time + abs(x2 - endX) + abs(y2 - endY), state)) 35 | } 36 | } 37 | throw NoSuchElementException() 38 | } 39 | 40 | @Day.Part 41 | fun part1(): Int = search(start, end) 42 | 43 | @Day.Part 44 | fun part2(): Int = search(start, end, search(end, start, search(start, end))) 45 | } 46 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day10.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | @Day 4 | class Day10(lines: List) { 5 | private val instructions = lines.map { it.toInstruction() } 6 | 7 | @Day.Part 8 | fun part1(): Int { 9 | val iterator = instructions.iterator() 10 | var cycle = 0 11 | var x = 1 12 | return taps.sumOf { i -> 13 | var lastX = x 14 | while (cycle < i) { 15 | lastX = x 16 | when (val instruction = iterator.next()) { 17 | Instruction.Noop -> cycle++ 18 | is Instruction.AddX -> { 19 | cycle += 2 20 | x += instruction.dx 21 | } 22 | } 23 | } 24 | i * lastX 25 | } 26 | } 27 | 28 | @Day.Part 29 | fun part2(): String = buildString(245) { 30 | val iterator = instructions.iterator() 31 | var cycle = 0 32 | var x = 1 33 | for (row in 0 until 240 step 40) { 34 | if (row != 0) append('\n') 35 | repeat(40) { col -> 36 | append(if (x - col in -1..1) '\u2593' else '\u2591') 37 | while (cycle < row + col) { 38 | when (val instruction = iterator.next()) { 39 | Instruction.Noop -> cycle++ 40 | is Instruction.AddX -> { 41 | cycle += 2 42 | x += instruction.dx 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | private sealed class Instruction { 51 | object Noop : Instruction() 52 | data class AddX(val dx: Int) : Instruction() 53 | } 54 | 55 | companion object { 56 | private val taps get() = intArrayOf(20, 60, 100, 140, 180, 220) 57 | 58 | private fun String.toInstruction(): Instruction = when { 59 | this == "noop" -> Instruction.Noop 60 | else -> Instruction.AddX(removePrefix("addx ").toInt()) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /py/aoc2022/day8.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 8: Treetop Tree House 3 | """ 4 | 5 | 6 | def _scan_visibility(line): 7 | max_height = None 8 | for height in line: 9 | if max_height is None or height > max_height: 10 | max_height = height 11 | yield True 12 | else: 13 | yield False 14 | 15 | 16 | def part1(lines): 17 | """ 18 | >>> part1(["30373", "25512", "65332", "33549", "35390"]) 19 | 21 20 | """ 21 | lines = [line.rstrip() for line in lines] 22 | visibilities = [list(_scan_visibility(line)) for line in lines] 23 | for line, row in zip(lines, visibilities): 24 | for col_index, value in enumerate(_scan_visibility(line[::-1])): 25 | row[-1 - col_index] |= value 26 | for col_index, line in enumerate(zip(*lines)): 27 | line = "".join(line) 28 | for row_index, value in enumerate(_scan_visibility(line)): 29 | visibilities[row_index][col_index] |= value 30 | for row_index, value in enumerate(_scan_visibility(line[::-1])): 31 | visibilities[-1 - row_index][col_index] |= value 32 | return sum(sum(row) for row in visibilities) 33 | 34 | 35 | def _scan_score(line): 36 | horizon = 0 37 | for index, height in enumerate(line): 38 | for horizon in range(index - 1, -1, -1): 39 | if line[horizon] >= height: 40 | break 41 | yield index - horizon 42 | 43 | 44 | def part2(lines): 45 | """ 46 | >>> part2(["30373", "25512", "65332", "33549", "35390"]) 47 | 8 48 | """ 49 | lines = [line.rstrip() for line in lines] 50 | scores = [list(_scan_score(line)) for line in lines] 51 | for line, row in zip(lines, scores): 52 | for col_index, value in enumerate(_scan_score(line[::-1])): 53 | row[-1 - col_index] *= value 54 | for col_index, line in enumerate(zip(*lines)): 55 | line = "".join(line) 56 | for row_index, value in enumerate(_scan_score(line)): 57 | scores[row_index][col_index] *= value 58 | for row_index, value in enumerate(_scan_score(line[::-1])): 59 | scores[-1 - row_index][col_index] *= value 60 | return max(max(row) for row in scores) 61 | 62 | 63 | parts = (part1, part2) 64 | -------------------------------------------------------------------------------- /py/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "aoc2022" 3 | version = "0.1.0" 4 | description = "" 5 | license = "BSD-3-Clause" 6 | authors = ["Daniel Lin "] 7 | readme = "README.md" 8 | repository = "https://github.com/ephemient/aoc2022/tree/main/py" 9 | classifiers = [ 10 | "Development Status :: 3 - Alpha", 11 | "Programming Language :: Python :: 3", 12 | "Environment :: Console", 13 | "Intended Audience :: Developers", 14 | "Intended Audience :: Education", 15 | "Operating System :: OS Independent" 16 | ] 17 | include = ["aoc2022/day*.txt"] 18 | 19 | [tool.poetry.dependencies] 20 | python = "^3.11" 21 | natsort = "^8.2.0" 22 | 23 | [tool.poetry.dev-dependencies] 24 | black = "^22.12.0" 25 | isort = "^5.11.4" 26 | pylint = "^2.15.9" 27 | pytest = "^7.2.0" 28 | pytest-benchmark = { version = "^4.0.0", extras = ["histogram"] } 29 | 30 | [tool.poetry.scripts] 31 | aoc2022 = "aoc2022.main:main" 32 | 33 | [tool.poetry.plugins."aoc2022.days"] 34 | day1 = "aoc2022.day1:parts" 35 | day2 = "aoc2022.day2:parts" 36 | day3 = "aoc2022.day3:parts" 37 | day4 = "aoc2022.day4:parts" 38 | day5 = "aoc2022.day5:parts" 39 | day6 = "aoc2022.day6:parts" 40 | day7 = "aoc2022.day7:parts" 41 | day8 = "aoc2022.day8:parts" 42 | day9 = "aoc2022.day9:parts" 43 | day10 = "aoc2022.day10:parts" 44 | day11 = "aoc2022.day11:parts" 45 | day12 = "aoc2022.day12:parts" 46 | day13 = "aoc2022.day13:parts" 47 | day13Fast = "aoc2022.day13_fast:parts" 48 | day14 = "aoc2022.day14:parts" 49 | day15 = "aoc2022.day15:parts" 50 | day16 = "aoc2022.day16:parts" 51 | day17 = "aoc2022.day17:parts" 52 | day18 = "aoc2022.day18:parts" 53 | day19 = "aoc2022.day19:parts" 54 | day20 = "aoc2022.day20:parts" 55 | day21 = "aoc2022.day21:parts" 56 | day22 = "aoc2022.day22:parts" 57 | day23 = "aoc2022.day23:parts" 58 | day24 = "aoc2022.day24:parts" 59 | day25 = "aoc2022.day25:parts" 60 | 61 | [tool.black] 62 | target_version = ["py311"] 63 | 64 | [tool.isort] 65 | profile = "black" 66 | 67 | [tool.pylint.format] 68 | max-line-length = "88" 69 | 70 | [tool.pylint.basic] 71 | good-names-rgxs = ["[xyz]\\d*"] 72 | 73 | [tool.pytest.ini_options] 74 | addopts = '--doctest-modules --benchmark-disable --benchmark-sort=fullname' 75 | required_plugins = ['pytest-benchmark'] 76 | 77 | [build-system] 78 | requires = ["poetry-core>=1.0.0"] 79 | build-backend = "poetry.core.masonry.api" 80 | -------------------------------------------------------------------------------- /hs/src/Day13Fast.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day13Fast 3 | Description: 4 | -} 5 | {-# LANGUAGE ViewPatterns #-} 6 | module Day13Fast (day13aFast, day13bFast) where 7 | 8 | import Data.List (partition, stripPrefix) 9 | import Data.List.Split (chunksOf) 10 | import Data.Text (Text) 11 | import qualified Data.Text as T (lines, null, uncons) 12 | import qualified Data.Text.Read as T (decimal) 13 | 14 | data PacketToken a = PacketOpen | PacketClose | PacketInt a deriving (Eq) 15 | 16 | tokenize :: (Integral a) => Text -> [PacketToken a] 17 | tokenize (T.decimal -> Right (a, s)) = PacketInt a:tokenize s 18 | tokenize (T.uncons -> Just (a, s)) 19 | | '[' <- a = PacketOpen:tokenize s 20 | | ']' <- a = PacketClose:tokenize s 21 | | otherwise = tokenize s 22 | tokenize _ = [] 23 | 24 | compare' :: (Ord a) => [PacketToken a] -> [PacketToken a] -> Ordering 25 | compare' [] [] = EQ 26 | compare' [] _ = LT 27 | compare' _ [] = GT 28 | compare' (PacketClose:as) (PacketClose:bs) = compare' as bs 29 | compare' (PacketClose:_) _ = LT 30 | compare' _ (PacketClose:_) = GT 31 | compare' (PacketInt a:as) (PacketInt b:bs) = compare a b <> compare' as bs 32 | compare' (PacketInt a:as) (break (/= PacketOpen) -> (length -> n, bs)) 33 | | PacketInt b:bs' <- bs 34 | = compare a b <> maybe LT (compare' as) (stripPrefix (replicate n PacketClose) bs') 35 | | otherwise = GT 36 | compare' ~(break (/= PacketOpen) -> (length -> n, as)) (PacketInt b:bs) 37 | | PacketInt a:as' <- as 38 | = compare a b <> maybe GT (flip compare' bs) (stripPrefix (replicate n PacketClose) as') 39 | | otherwise = LT 40 | compare' (a:as) (b:bs) | a == b = compare' as bs 41 | 42 | day13aFast :: Text -> Int 43 | day13aFast input = sum 44 | [ i 45 | | (i, x:y:_) <- zip [1..] . chunksOf 3 . map tokenize $ T.lines input 46 | , compare' x y <= EQ 47 | ] 48 | 49 | day13bFast :: Text -> Int 50 | day13bFast input = x * y where 51 | packets = map tokenize . filter (not . T.null) $ T.lines input 52 | a = [PacketOpen, PacketOpen, PacketInt 2, PacketClose, PacketClose] 53 | b = [PacketOpen, PacketOpen, PacketInt 6, PacketClose, PacketClose] 54 | (belowA, aboveA) = partition (\x -> compare' x a < EQ) packets 55 | belowB = filter (\x -> compare' x b < EQ) aboveA 56 | x = 1 + length belowA 57 | y = 1 + x + length belowB 58 | -------------------------------------------------------------------------------- /rs/src/day20.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::num::ParseIntError; 3 | 4 | fn parse<'a, I, S>(lines: I) -> Result, ParseIntError> 5 | where 6 | I: IntoIterator, 7 | S: AsRef + 'a, 8 | { 9 | lines 10 | .into_iter() 11 | .enumerate() 12 | .map(|(i, line)| Ok((i, line.as_ref().parse()?))) 13 | .collect() 14 | } 15 | 16 | fn mix(array: &mut [(usize, isize)]) -> Option<()> { 17 | for i in 0..array.len() { 18 | let (i, &a) = array.iter().enumerate().find(|(_, (j, _))| j == &i)?; 19 | let j = (i as isize + a.1).rem_euclid(array.len() as isize - 1) as usize; 20 | match i.cmp(&j) { 21 | Ordering::Less => array.copy_within(i + 1..j + 1, i), 22 | Ordering::Equal => {} 23 | Ordering::Greater => array.copy_within(j..i, j + 1), 24 | } 25 | array[j] = a; 26 | } 27 | Some(()) 28 | } 29 | 30 | pub fn part1<'a, I, S>(lines: I) -> Option 31 | where 32 | I: IntoIterator, 33 | S: AsRef + 'a, 34 | { 35 | let mut array = parse(lines).ok()?; 36 | mix(&mut array)?; 37 | let (i, _) = array.iter().enumerate().find(|(_, (_, x))| x == &0)?; 38 | Some( 39 | [1000, 2000, 3000] 40 | .iter() 41 | .map(|x| array[(i + x) % array.len()].1) 42 | .sum(), 43 | ) 44 | } 45 | 46 | pub fn part2<'a, I, S>(lines: I) -> Option 47 | where 48 | I: IntoIterator, 49 | S: AsRef + 'a, 50 | { 51 | let mut array = parse(lines).ok()?; 52 | for entry in &mut array { 53 | entry.1 *= 811589153; 54 | } 55 | for _ in 0..10 { 56 | mix(&mut array)?; 57 | } 58 | let (i, _) = array.iter().enumerate().find(|(_, (_, x))| x == &0)?; 59 | Some( 60 | [1000, 2000, 3000] 61 | .iter() 62 | .map(|x| array[(i + x) % array.len()].1) 63 | .sum(), 64 | ) 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | use pretty_assertions::assert_eq; 71 | 72 | static EXAMPLE: &[&str] = &["1", "2", "-3", "3", "-2", "0", "4"]; 73 | 74 | #[test] 75 | fn part1_examples() { 76 | assert_eq!(Some(3), part1(EXAMPLE)); 77 | } 78 | 79 | #[test] 80 | fn part2_examples() { 81 | assert_eq!(Some(1623178306), part2(EXAMPLE)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /hs/src/Day24.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day24 3 | Description: 4 | -} 5 | {-# LANGUAGE NondecreasingIndentation, ViewPatterns #-} 6 | module Day24 (day24a, day24b) where 7 | 8 | import qualified Data.Heap as Heap (FstMinPolicy, insert, singleton, view) 9 | import Data.List (foldl') 10 | import qualified Data.Set as Set (insert, notMember, singleton) 11 | import Data.Text (Text) 12 | import qualified Data.Text as T (index, length, lines) 13 | import Data.Tuple (swap) 14 | import Data.Vector (Vector) 15 | import qualified Data.Vector as V ((!), fromList, last, length) 16 | 17 | search :: Vector Text -> (Int, Int) -> (Int, Int) -> Int -> Maybe Int 18 | search lines start end@(endX, endY) startTime = go (Set.singleton (start, startTime)) $ 19 | Heap.singleton @Heap.FstMinPolicy (0, (start, startTime)) where 20 | isFree (x, y) time 21 | | y < 0 || y >= V.length lines || x < 1 || x > T.length line - 2 = False 22 | | y == 0 || y == V.length lines - 1 = T.index line x == '.' 23 | | T.index line ((x - 1 + time) `mod` (T.length line - 2) + 1) == '<' = False 24 | | T.index line ((x - 1 - time) `mod` (T.length line - 2) + 1) == '>' = False 25 | | T.index (lines V.! ((y - 1 + time) `mod` (V.length lines - 2) + 1)) x == '^' = False 26 | | T.index (lines V.! ((y - 1 - time) `mod` (V.length lines - 2) + 1)) x == 'v' = False 27 | | otherwise = True 28 | where line = lines V.! y 29 | go seen (Heap.view -> Just ((_, (pos@(x, y), time)), heap)) 30 | | pos == end = Just time 31 | | otherwise = go seen' heap' where 32 | choices = filter (\state@(pos, time) -> isFree pos time && Set.notMember state seen) $ 33 | (, time + 1) <$> [(x - 1, y), (x, y - 1), (x, y), (x, y + 1), (x + 1, y)] 34 | seen' = foldl' (flip Set.insert) seen choices 35 | heap' = foldl' (flip Heap.insert) heap 36 | [(time + abs (x - endX) + abs (y - endY), choice) | choice@((x, y), time) <- choices] 37 | go _ _ = Nothing 38 | 39 | day24a :: Text -> Maybe Int 40 | day24a input = search lines (1, 0) (T.length (V.last lines) - 2, V.length lines - 1) 0 where 41 | lines = V.fromList $ T.lines input 42 | 43 | day24b :: Text -> Maybe Int 44 | day24b input = search lines start end 0 >>= search lines end start >>= search lines start end where 45 | lines = V.fromList $ T.lines input 46 | start = (1, 0) 47 | end = (T.length (V.last lines) - 2, V.length lines - 1) 48 | -------------------------------------------------------------------------------- /hs/src/Day23.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day23 3 | Description: 4 | -} 5 | module Day23 (day23a, day23b) where 6 | 7 | import Control.Arrow ((&&&), (***)) 8 | import Data.List (findIndex, scanl', tails) 9 | import qualified Data.Map as Map (elems, filter, fromListWith, keysSet) 10 | import Data.Maybe (fromJust) 11 | import Data.Semigroup (Max(Max), Min(Min)) 12 | import Data.Set (Set) 13 | import qualified Data.Set as Set (difference, fromList, member, size, toList, union) 14 | import Data.Text (Text) 15 | import qualified Data.Text as T (lines, unpack) 16 | 17 | data Direction = N | S | W | E deriving (Bounded, Enum) 18 | 19 | dirs :: [[Direction]] 20 | dirs = take 4 <$> tails (cycle [minBound..maxBound]) 21 | 22 | sides :: (Enum a, Num a) => Direction -> (a, a) -> [(a, a)] 23 | sides N (x, y) = [(x', y - 1) | x' <- [x - 1..x + 1]] 24 | sides S (x, y) = [(x', y + 1) | x' <- [x - 1..x + 1]] 25 | sides W (x, y) = [(x - 1, y') | y' <- [y - 1..y + 1]] 26 | sides E (x, y) = [(x + 1, y') | y' <- [y - 1..y + 1]] 27 | 28 | move :: (Num a) => Direction -> (a, a) -> (a, a) 29 | move N (x, y) = (x, y - 1) 30 | move S (x, y) = (x, y + 1) 31 | move W (x, y) = (x - 1, y) 32 | move E (x, y) = (x + 1, y) 33 | 34 | neighbors :: (Enum a, Eq a, Num a) => (a, a) -> [(a, a)] 35 | neighbors (x, y) = [(x', y') | x' <- [x - 1..x + 1] , y' <- [y - 1..y + 1] , x /= x' || y /= y'] 36 | 37 | step :: (Enum a, Num a, Ord a) => Set (a, a) -> [Direction] -> Set (a, a) 38 | step state dirs = state `Set.difference` 39 | Set.fromList (concat $ Map.elems proposals) `Set.union` Map.keysSet proposals where 40 | proposals = Map.filter ((== 1) . length) $ Map.fromListWith (<>) 41 | [ (move dir pos, [pos]) 42 | | pos <- Set.toList state 43 | , any (`Set.member` state) $ neighbors pos 44 | , dir <- take 1 $ filter (not . any (`Set.member` state) . (`sides` pos)) dirs 45 | ] 46 | 47 | parse :: Text -> Set (Int, Int) 48 | parse input = Set.fromList 49 | [(x, y) | (y, line) <- zip [0..] $ T.lines input , (x, '#') <- zip [0..] $ T.unpack line] 50 | 51 | day23a :: Text -> Int 52 | day23a input = (maxX - minX + 1) * (maxY - minY + 1) - Set.size state where 53 | state = scanl' step (parse input) dirs !! 10 54 | ((Min minX, Max maxX), (Min minY, Max maxY)) = 55 | foldMap ((Min &&& Max) *** (Min &&& Max)) state 56 | 57 | day23b :: Text -> Int 58 | day23b input = fromJust (findIndex id . zipWith (==) states $ drop 1 states) + 1 where 59 | states = scanl' step (parse input) dirs 60 | -------------------------------------------------------------------------------- /py/aoc2022/day13.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 13: Distress Signal 3 | """ 4 | 5 | import ast 6 | from collections.abc import Iterable 7 | from itertools import zip_longest 8 | 9 | SAMPLE_INPUT = [ 10 | "[1,1,3,1,1]", 11 | "[1,1,5,1,1]", 12 | "", 13 | "[[1],[2,3,4]]", 14 | "[[1],4]", 15 | "", 16 | "[9]", 17 | "[[8,7,6]]", 18 | "", 19 | "[[4,4],4,4]", 20 | "[[4,4],4,4,4]", 21 | "", 22 | "[7,7,7,7]", 23 | "[7,7,7]", 24 | "", 25 | "[]", 26 | "[3]", 27 | "", 28 | "[[[]]]", 29 | "[[]]", 30 | "", 31 | "[1,[2,[3,[4,[5,6,7]]]],8,9]", 32 | "[1,[2,[3,[4,[5,6,0]]]],8,9]", 33 | ] 34 | 35 | 36 | def _parse(line): 37 | if any(char not in "\n,0123456789[]" for char in line): 38 | raise ValueError() 39 | return ast.literal_eval(line) 40 | 41 | 42 | def _compare(lhs, rhs): 43 | if not isinstance(lhs, Iterable) and not isinstance(rhs, Iterable): 44 | return 0 if lhs == rhs else -1 if lhs < rhs else 1 45 | try: 46 | lhs = iter(lhs) 47 | except TypeError: 48 | lhs = iter((lhs,)) 49 | try: 50 | rhs = iter(rhs) 51 | except TypeError: 52 | rhs = iter((rhs,)) 53 | while True: 54 | try: 55 | left = next(lhs) 56 | except StopIteration: 57 | try: 58 | next(rhs) 59 | return -1 60 | except StopIteration: 61 | return 0 62 | try: 63 | right = next(rhs) 64 | except StopIteration: 65 | return 1 66 | comparison = _compare(left, right) 67 | if comparison: 68 | return comparison 69 | 70 | 71 | def part1(lines): 72 | """ 73 | >>> part1(SAMPLE_INPUT) 74 | 13 75 | """ 76 | total = 0 77 | for i, pair in enumerate(zip_longest(*(iter(lines),) * 3)): 78 | if _compare(_parse(pair[0]), _parse(pair[1])) <= 0: 79 | total += i + 1 80 | return total 81 | 82 | 83 | def part2(lines): 84 | """ 85 | >>> part2(SAMPLE_INPUT) 86 | 140 87 | """ 88 | divider1, divider2 = [[2]], [[6]] 89 | count1, count2 = 1, 1 90 | for line in filter(str.strip, lines): 91 | packet = _parse(line) 92 | if _compare(packet, divider1) < 0: 93 | count1 += 1 94 | elif _compare(packet, divider2) < 0: 95 | count2 += 1 96 | return count1 * (count1 + count2) 97 | 98 | 99 | parts = (part1, part2) 100 | -------------------------------------------------------------------------------- /hs/src/Day20.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day20 3 | Description: 4 | -} 5 | {-# LANGUAGE LambdaCase, NondecreasingIndentation #-} 6 | module Day20 (day20a, day20b) where 7 | 8 | import Common (readEntire) 9 | import Control.Monad (forM_, replicateM_) 10 | import Control.Monad.ST (runST) 11 | import Data.Bool (bool) 12 | import Data.Text (Text) 13 | import qualified Data.Text as T (lines) 14 | import qualified Data.Text.Read as T (decimal, signed) 15 | import Data.Vector.Generic (Vector) 16 | import qualified Data.Vector.Generic as V (enumFromN, fromList, length, map, thaw) 17 | import Data.Vector.Generic.Mutable (MVector, PrimMonad, PrimState) 18 | import qualified Data.Vector.Generic.Mutable as MV (length, move, read, slice, write) 19 | import Data.Vector.Unboxed (Unbox) 20 | import qualified Data.Vector.Unboxed as UV (zip) 21 | 22 | parse :: (Integral a, Unbox a, Vector v a) => Text -> Either String (v a) 23 | parse input = V.fromList <$> mapM (readEntire $ T.signed T.decimal) (T.lines input) 24 | 25 | mvFindIndex :: (PrimMonad m, MVector v a) => (a -> Bool) -> v (PrimState m) a -> m (Maybe Int) 26 | mvFindIndex p v = foldr f (pure Nothing) [0..MV.length v - 1] where 27 | f i k = MV.read v i >>= bool k (pure $ Just i) . p 28 | 29 | mix :: (PrimMonad m, MVector v (Int, Int)) => v (PrimState m) (Int, Int) -> m () 30 | mix v = forM_ [0..MV.length v - 1] $ \i -> mvFindIndex ((== i) . fst) v >>= \case 31 | Just i -> do 32 | a@(_, x) <- MV.read v i 33 | let j = (i + x) `mod` (MV.length v - 1) 34 | case compare i j of 35 | LT -> MV.move (MV.slice i (j - i) v) (MV.slice (i + 1) (j - i) v) 36 | GT -> MV.move (MV.slice (j + 1) (i - j) v) (MV.slice j (i - j) v) 37 | _ -> pure () 38 | MV.write v j a 39 | 40 | day20a :: Text -> Either String Int 41 | day20a input = do 42 | nums <- parse input 43 | pure $ runST $ do 44 | v <- V.thaw $ UV.zip (V.enumFromN 0 $ V.length nums) nums 45 | mix v 46 | Just i <- mvFindIndex ((== 0) . snd) v 47 | sum <$> sequence [snd <$> MV.read v ((i + x) `mod` MV.length v) | x <- [1000, 2000, 3000]] 48 | 49 | day20b :: Text -> Either String Int 50 | day20b input = do 51 | nums <- V.map (* 811589153) <$> parse input 52 | pure $ runST $ do 53 | v <- V.thaw $ UV.zip (V.enumFromN 0 $ V.length nums) nums 54 | replicateM_ 10 $ mix v 55 | Just i <- mvFindIndex ((== 0) . snd) v 56 | sum <$> sequence [snd <$> MV.read v ((i + x) `mod` MV.length v) | x <- [1000, 2000, 3000]] 57 | -------------------------------------------------------------------------------- /kt/buildSrc/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTest.kt: -------------------------------------------------------------------------------- 1 | // Copied from https://github.com/JetBrains/kotlin/blob/v1.7.21/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTest.kt 2 | // which needs to be recompiled with a newer Gradle API to address https://youtrack.jetbrains.com/issue/KT-54634 3 | /* 4 | * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license 5 | * that can be found in the license/LICENSE.txt file. 6 | */ 7 | 8 | package org.jetbrains.kotlin.gradle.targets.jvm.tasks 9 | 10 | import org.gradle.api.internal.tasks.testing.JvmTestExecutionSpec 11 | import org.gradle.api.internal.tasks.testing.TestDescriptorInternal 12 | import org.gradle.api.internal.tasks.testing.TestExecuter 13 | import org.gradle.api.internal.tasks.testing.TestResultProcessor 14 | import org.gradle.api.internal.tasks.testing.TestStartEvent 15 | import org.gradle.api.tasks.CacheableTask 16 | import org.gradle.api.tasks.Input 17 | import org.gradle.api.tasks.Optional 18 | import org.gradle.api.tasks.testing.Test 19 | 20 | @CacheableTask 21 | open class KotlinJvmTest : Test() { 22 | @Input 23 | @Optional 24 | var targetName: String? = null 25 | 26 | override fun createTestExecuter(): TestExecuter = 27 | if (targetName != null) Executor( 28 | super.createTestExecuter(), 29 | targetName!! 30 | ) 31 | else super.createTestExecuter() 32 | 33 | class Executor( 34 | private val delegate: TestExecuter, 35 | private val targetName: String 36 | ) : TestExecuter by delegate { 37 | override fun execute(testExecutionSpec: JvmTestExecutionSpec, testResultProcessor: TestResultProcessor) { 38 | delegate.execute(testExecutionSpec, object : TestResultProcessor by testResultProcessor { 39 | override fun started(test: TestDescriptorInternal, event: TestStartEvent) { 40 | val myTest = object : TestDescriptorInternal by test { 41 | override fun getDisplayName(): String = "${test.displayName}[$targetName]" 42 | override fun getClassName(): String? = test.className?.replace('$', '.') 43 | override fun getClassDisplayName(): String? = test.classDisplayName?.replace('$', '.') 44 | } 45 | testResultProcessor.started(myTest, event) 46 | } 47 | }) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /kt/src/nativeMain/kotlin/com/github/ephemient/aoc2022/Main.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlinx.cinterop.* 4 | import platform.posix.* 5 | 6 | @OptIn(UnsafeNumber::class) 7 | actual fun getInput(day: Int): List { 8 | val dataDir = checkNotNull(getenv("aoc2022_datadir")?.toKString()?.ifEmpty { null }) { "missing \$aoc2022_datadir" } 9 | var offset = 0 10 | var buffer = ByteArray(2 * BUFSIZ) 11 | val fd = open("$dataDir/day$day.txt", O_RDONLY) 12 | check(fd != -1) { 13 | val errno = errno 14 | "open(\$aoc2022_data/day$day.txt): ${strerror(errno)?.toKString() ?: errno.toString()}" 15 | } 16 | try { 17 | return buildList { 18 | while (true) { 19 | val count = buffer.usePinned { 20 | read(fd, it.addressOf(offset), (buffer.size - offset).coerceAtMost(BUFSIZ).convert()) 21 | } 22 | when { 23 | count < 0 -> { 24 | val errno = errno 25 | check(errno == EINTR) { 26 | "read(\$aoc2022_data/day$day.txt): ${strerror(errno)?.toKString() ?: errno.toString()}" 27 | } 28 | continue 29 | } 30 | count > 0 -> { 31 | var start = 0 32 | val end = offset + count.toInt() 33 | for (pos in offset until end) { 34 | if (buffer[pos] == '\n'.code.toByte()) { 35 | add(buffer.toKString(startIndex = start, endIndex = pos)) 36 | start = pos + 1 37 | } 38 | } 39 | buffer.copyInto(buffer, startIndex = start, endIndex = end) 40 | offset = end - start 41 | if (buffer.size - offset < BUFSIZ) buffer = buffer.copyOf(buffer.size * 2) 42 | } 43 | else -> break 44 | } 45 | } 46 | if (offset != 0) add(buffer.toKString(endIndex = offset)) 47 | } 48 | } finally { 49 | close(fd) 50 | } 51 | } 52 | 53 | actual inline fun assert(condition: () -> Boolean) { 54 | assert(condition()) 55 | } 56 | 57 | actual inline fun assert(condition: () -> Boolean, lazyMessage: () -> Any) { 58 | assert(condition(), lazyMessage) 59 | } 60 | 61 | actual fun trace(message: String) { 62 | if (getenv("TRACE")?.get(0) != '0'.code.toByte()) fputs("$message\n", stderr) 63 | } 64 | -------------------------------------------------------------------------------- /rs/src/day5.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | fn solve<'a, const R: bool, I, S>(lines: I) -> String 4 | where 5 | I: IntoIterator, 6 | S: AsRef + 'a, 7 | { 8 | let mut iter = lines.into_iter(); 9 | let mut columns: Vec = vec![]; 10 | for line in iter.by_ref() { 11 | let line = line.as_ref(); 12 | if line.is_empty() { 13 | break; 14 | } 15 | for (i, c) in line.chars().enumerate() { 16 | match columns.get_mut(i) { 17 | Some(column) => column.push(c), 18 | _ => columns.push(c.to_string()), 19 | } 20 | } 21 | } 22 | let mut stacks: BTreeMap = columns 23 | .into_iter() 24 | .filter_map(|mut column| { 25 | let key = column.pop().filter(char::is_ascii_digit)?; 26 | Some((key, column.trim_start().to_string())) 27 | }) 28 | .collect(); 29 | iter.filter_map(|line| { 30 | let mut iter = line.as_ref().split_ascii_whitespace(); 31 | let num: usize = iter.nth(1)?.parse().ok()?; 32 | let x = iter.nth(1)?.chars().next()?; 33 | let y = iter.nth(1)?.chars().next()?; 34 | let source = stacks.get_mut(&x)?; 35 | let head = if R { 36 | source.drain(..num).rev().collect() 37 | } else { 38 | source.drain(..num).as_str().to_string() 39 | }; 40 | let dest = stacks.get_mut(&y)?; 41 | *dest = head + dest; 42 | Some(()) 43 | }) 44 | .for_each(drop); 45 | stacks 46 | .into_iter() 47 | .filter_map(|(_, stack)| stack.chars().next()) 48 | .collect() 49 | } 50 | 51 | pub fn part1<'a, I, S>(lines: I) -> String 52 | where 53 | I: IntoIterator, 54 | S: AsRef + 'a, 55 | { 56 | solve::(lines) 57 | } 58 | 59 | pub fn part2<'a, I, S>(lines: I) -> String 60 | where 61 | I: IntoIterator, 62 | S: AsRef + 'a, 63 | { 64 | solve::(lines) 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | use pretty_assertions::assert_eq; 71 | 72 | static EXAMPLE: &[&str] = &[ 73 | " [D] ", 74 | "[N] [C] ", 75 | "[Z] [M] [P]", 76 | " 1 2 3 ", 77 | "", 78 | "move 1 from 2 to 1", 79 | "move 3 from 1 to 3", 80 | "move 2 from 2 to 1", 81 | "move 1 from 1 to 2", 82 | ]; 83 | 84 | #[test] 85 | fn part1_examples() { 86 | assert_eq!("CMZ", part1(EXAMPLE)); 87 | } 88 | 89 | #[test] 90 | fn part2_examples() { 91 | assert_eq!("MCD", part2(EXAMPLE)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /kt/src/commonMain/kotlin/com/github/ephemient/aoc2022/Day15.kt: -------------------------------------------------------------------------------- 1 | package com.github.ephemient.aoc2022 2 | 3 | import kotlin.math.abs 4 | 5 | @Day 6 | class Day15(lines: List) { 7 | private val inputs = lines.map { 8 | val x0 = it.substring(it.indexOf("x=") + 2, it.indexOf(',')).toInt() 9 | val y0 = it.substring(it.indexOf("y=") + 2, it.indexOf(':')).toInt() 10 | val x1 = it.substring(it.lastIndexOf("x=") + 2, it.lastIndexOf(',')).toInt() 11 | val y1 = it.substring(it.lastIndexOf("y=") + 2).toInt() 12 | x0 to y0 to (x1 to y1) 13 | } 14 | private val isTestInput = inputs.all { (sensor, _) -> sensor.first in 0..20 && sensor.second in 0..20 } 15 | 16 | @Day.Part 17 | fun part1(): Int { 18 | val y = if (isTestInput) 10 else 2000000 19 | val intervals = mutableListOf() 20 | val beacons = mutableSetOf() 21 | for ((sensor, beacon) in inputs) { 22 | val dx = abs(sensor.first - beacon.first) + abs(sensor.second - beacon.second) - abs(y - sensor.second) 23 | if (dx >= 0) intervals.addInterval(sensor.first - dx..sensor.first + dx) 24 | if (beacon.second == y) beacons.add(beacon.first) 25 | } 26 | return intervals.sumOf { it.last - it.first + 1 } - beacons.size 27 | } 28 | 29 | @Day.Part 30 | fun part2(): Long = sequence { 31 | val size = if (isTestInput) 20 else 4000000 32 | for (y in 0..size) { 33 | val intervals = mutableListOf() 34 | for ((sensor, beacon) in inputs) { 35 | val dx = abs(sensor.first - beacon.first) + abs(sensor.second - beacon.second) - abs(y - sensor.second) 36 | val lo = (sensor.first - dx).coerceAtLeast(0) 37 | val hi = (sensor.first + dx).coerceAtMost(size) 38 | if (lo <= hi) intervals.addInterval(lo..hi) 39 | } 40 | val hi = intervals.fold(0) { prev, interval -> 41 | for (x in prev until interval.first) yield(4000000L * x + y) 42 | interval.last + 1 43 | } 44 | for (x in hi..size) yield(4000000L * x + y) 45 | } 46 | }.single() 47 | } 48 | 49 | private fun MutableList.addInterval(range: IntRange) { 50 | val loIndex = binarySearch { it.last.compareTo(range.first - 1) }.let { it shr 31 xor it } 51 | val hiIndex = binarySearch(fromIndex = loIndex) { it.first.compareTo(range.last + 1) }.let { it shr 31 xor it } 52 | val mergedRange = if (loIndex < hiIndex) { 53 | minOf(this[loIndex].first, range.first)..maxOf(this[hiIndex - 1].last, range.last) 54 | } else { 55 | range 56 | } 57 | subList(loIndex, hiIndex).clear() 58 | add(loIndex, mergedRange) 59 | } 60 | -------------------------------------------------------------------------------- /hs/src/Day14.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day14 3 | Description: 4 | -} 5 | {-# LANGUAGE BlockArguments, OverloadedStrings #-} 6 | module Day14 (day14) where 7 | 8 | import Common (readEntire) 9 | import Control.Arrow ((&&&), (***)) 10 | import Control.Monad (forM_, when) 11 | import Control.Monad.ST (ST, runST) 12 | import Data.Array.MArray (MArray, newArray, readArray, writeArray) 13 | import Data.Array.ST (STUArray) 14 | import Data.Bool (bool) 15 | import Data.Ix (Ix) 16 | import Data.Maybe (fromMaybe) 17 | import Data.STRef (modifySTRef', newSTRef, readSTRef, writeSTRef) 18 | import Data.Semigroup (Max(Max), Min(Min)) 19 | import Data.Text (Text) 20 | import qualified Data.Text as T (lines, splitOn) 21 | import qualified Data.Text.Read as T (decimal) 22 | 23 | parse :: (Bounded i, Integral i, Ix i, MArray a Bool (ST s)) => Text -> ST s (a (i, i) Bool, i) 24 | parse input = do 25 | blocks <- newArray 26 | ( (min minX $ 500 - maxY - 1, min 0 minY) 27 | , (max maxX $ 500 + maxY + 1, max 0 maxY + 1) 28 | ) False 29 | forM_ segments $ \points -> 30 | forM_ (zip points $ tail points) $ \((x1, y1), (x2, y2)) -> 31 | forM_ [min y1 y2..max y1 y2] $ \y -> 32 | forM_ [min x1 x2..max x1 x2] $ \x -> 33 | writeArray blocks (x, y) True 34 | pure (blocks, maxY) 35 | where 36 | segments = 37 | [ [ (x', y') 38 | | [x, y] <- T.splitOn "," <$> T.splitOn " -> " line 39 | , x' <- either fail pure $ readEntire T.decimal x 40 | , y' <- either fail pure $ readEntire T.decimal y 41 | ] 42 | | line <- T.lines input 43 | ] 44 | ((Min minX, Max maxX), (Min minY, Max maxY)) = 45 | foldMap ((Min &&& Max) *** (Min &&& Max)) $ concat segments 46 | 47 | fill :: (MArray a Bool (ST s), Ix i, Num i, Show i) => a (i, i) Bool -> i -> ST s (Int, Int) 48 | fill blocks maxY = do 49 | counterAtMaxY <- newSTRef Nothing 50 | counter <- newSTRef 0 51 | let fill' (x, y) = readArray blocks (x, y) >>= flip bool (pure ()) do 52 | when (y == maxY) $ readSTRef counterAtMaxY >>= maybe 53 | (readSTRef counter >>= writeSTRef counterAtMaxY . Just) (const $ pure ()) 54 | when (y <= maxY) $ fill' (x, y + 1) >> fill' (x - 1, y + 1) >> fill' (x + 1, y + 1) 55 | writeArray blocks (x, y) True >> modifySTRef' counter (+ 1) 56 | fill' (500, 0) 57 | counterAtMaxY <- readSTRef counterAtMaxY 58 | counter <- readSTRef counter 59 | pure (fromMaybe counter counterAtMaxY, counter) 60 | 61 | day14 :: Text -> (Int, Int) 62 | day14 input = runST $ parsed >>= uncurry fill where 63 | parsed :: ST s (STUArray s (Int, Int) Bool, Int) 64 | parsed = parse input 65 | -------------------------------------------------------------------------------- /hs/src/Day21.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day21 3 | Description: 4 | -} 5 | {-# LANGUAGE OverloadedStrings, TypeFamilies #-} 6 | module Day21 (day21a, day21b) where 7 | 8 | import Data.Char (isAlphaNum) 9 | import qualified Data.Map as Map ((!), fromList, insert) 10 | import Data.Ratio ((%), denominator, numerator) 11 | import Data.String (IsString) 12 | import Data.Text (Text) 13 | import Data.Void (Void) 14 | import Text.Megaparsec (MonadParsec, ParseErrorBundle, Token, Tokens, (<|>), choice, parse, sepEndBy, takeWhile1P) 15 | import Text.Megaparsec.Char (eol, string) 16 | import qualified Text.Megaparsec.Char.Lexer as L (decimal) 17 | 18 | data Expr a b = Literal a | b :+ b | b :- b | b :* b | b :/ b 19 | 20 | parser :: (Integral a, MonadParsec e s m, IsString (Tokens s), Token s ~ Char) => m [(Tokens s, Expr a (Tokens s))] 21 | parser = parseLine `sepEndBy` eol where 22 | parseLine = (,) <$> name <* string ": " <*> expr 23 | expr = Literal <$> L.decimal <|> flip ($) <$> name <*> op <*> name 24 | op = (:+) <$ string " + " <|> 25 | (:-) <$ string " - " <|> 26 | (:*) <$ string " * " <|> 27 | (:/) <$ string " / " 28 | name = takeWhile1P Nothing isAlphaNum 29 | 30 | day21a :: Text -> Either (ParseErrorBundle Text Void) Int 31 | day21a input = do 32 | monkeys <- Map.fromList <$> parse parser "day21.txt" input 33 | let monkeys' = eval <$> monkeys 34 | eval (Literal a) = a 35 | eval (x :+ y) = monkeys' Map.! x + monkeys' Map.! y 36 | eval (x :- y) = monkeys' Map.! x - monkeys' Map.! y 37 | eval (x :* y) = monkeys' Map.! x * monkeys' Map.! y 38 | eval (x :/ y) = monkeys' Map.! x `div` monkeys' Map.! y 39 | pure $ monkeys' Map.! "root" 40 | 41 | day21b :: Text -> Either (ParseErrorBundle Text Void) Int 42 | day21b input = do 43 | monkeys <- Map.fromList <$> parse parser "day21.txt" input 44 | let (lhs, rhs) = case monkeys Map.! "root" of 45 | lhs :+ rhs -> (lhs, rhs) 46 | lhs :- rhs -> (lhs, rhs) 47 | lhs :* rhs -> (lhs, rhs) 48 | lhs :/ rhs -> (lhs, rhs) 49 | monkeys' = Map.insert "humn" (1, 0) $ eval <$> monkeys 50 | eval (Literal a) = (0, a % 1) 51 | eval (x :+ y) | (a, b) <- monkeys' Map.! x, (c, d) <- monkeys' Map.! y = (a + c, b + d) 52 | eval (x :- y) | (a, b) <- monkeys' Map.! x, (c, d) <- monkeys' Map.! y = (a - c, b - d) 53 | eval (x :* y) 54 | | (0, b) <- monkeys' Map.! x, (c, d) <- monkeys' Map.! y = (b * c, b * d) 55 | | (a, b) <- monkeys' Map.! x, (0, d) <- monkeys' Map.! y = (a * d, b * d) 56 | eval (x :/ y) | (a, b) <- monkeys' Map.! x, (0, d) <- monkeys' Map.! y = (a / d, b / d) 57 | (m, b) = monkeys' Map.! lhs 58 | (n, c) = monkeys' Map.! rhs 59 | x = (c - b) / (m - n) 60 | pure $ if denominator x == 1 then numerator x else error "non-integral" 61 | -------------------------------------------------------------------------------- /py/aoc2022/day17.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 17: Pyroclastic Flow 3 | """ 4 | 5 | ROCKS = ( 6 | (4, (15,)), 7 | (3, (2, 7, 2)), 8 | (3, (7, 4, 4)), 9 | (1, (1, 1, 1, 1)), 10 | (2, (3, 3)), 11 | ) 12 | 13 | 14 | def _check_free(rock, lines, x, y): 15 | return all(not (line & row << x) for line, row in zip(lines[y:], rock)) 16 | 17 | 18 | def _step(width, rock, jet, jet_index, height, lines): 19 | # pylint: disable=too-many-arguments 20 | x, y = 2, len(lines) + 3 21 | while _check_free(rock, lines, x, y): 22 | match jet[jet_index]: 23 | case "<": 24 | x2 = x - 1 25 | case ">": 26 | x2 = x + 1 27 | case _: 28 | raise ValueError() 29 | jet_index = (jet_index + 1) % len(jet) 30 | if 0 <= x2 <= 7 - width and _check_free(rock, lines, x2, y): 31 | x = x2 32 | y -= 1 33 | y, lines = y + 1, list(lines) 34 | for rock_y, row in enumerate(rock): 35 | if y + rock_y >= len(lines): 36 | lines.append(row << x) 37 | height += 1 38 | else: 39 | lines[y + rock_y] = lines[y + rock_y] | row << x 40 | visible, queue = [0] * len(lines) + [1], [(0, len(lines))] 41 | while queue: 42 | x, y = queue.pop() 43 | for x2, y2 in ((x, y - 1), (x - 1, y), (x + 1, y), (x, y + 1)): 44 | if ( 45 | x2 in range(7) 46 | and y2 in range(len(visible)) 47 | and not (visible[y2] & 1 << x2) 48 | ): 49 | visible[y2] |= 1 << x2 50 | if not (y2 < len(lines) and lines[y2] & 1 << x2): 51 | queue.append((x2, y2)) 52 | lines = [line & row for line, row in zip(lines, visible)] 53 | trim = 0 54 | while not lines[trim]: 55 | trim += 1 56 | return jet_index, height, lines[trim:] 57 | 58 | 59 | def _solve(jet, count): 60 | jet_index, height, lines = 0, 0, [127] 61 | seen, heights = {}, [] 62 | for i in range(count): 63 | rock_index = i % len(ROCKS) 64 | width, rock = ROCKS[rock_index] 65 | j = seen.setdefault((tuple(lines), jet_index, rock_index), i) 66 | if j < i: 67 | return heights[j + (count - j) % (i - j)] + (count - j) // (i - j) * ( 68 | height - heights[j] 69 | ) 70 | heights.append(height) 71 | jet_index, height, lines = _step(width, rock, jet, jet_index, height, lines) 72 | return height 73 | 74 | 75 | def part1(lines): 76 | """ 77 | >>> part1([">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>"]) 78 | 3068 79 | """ 80 | return _solve(next(iter(lines)).strip(), 2022) 81 | 82 | 83 | def part2(lines): 84 | """ 85 | >>> part2([">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>"]) 86 | 1514285714288 87 | """ 88 | return _solve(next(iter(lines)).strip(), 1000000000000) 89 | 90 | 91 | parts = (part1, part2) 92 | -------------------------------------------------------------------------------- /.github/workflows/rs-bench.yml: -------------------------------------------------------------------------------- 1 | name: Rust benchmarks 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | get-inputs: 8 | uses: ephemient/aoc2022/.github/workflows/get-inputs.yml@main 9 | secrets: 10 | SESSION: ${{ secrets.SESSION }} 11 | 12 | build: 13 | needs: [ get-inputs ] 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions/checkout@v3 19 | with: 20 | ref: gh-docs 21 | path: gh-docs 22 | - uses: actions/download-artifact@v3 23 | with: 24 | name: inputs 25 | - id: rust-toolchain 26 | uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: stable 29 | profile: minimal 30 | default: true 31 | - uses: actions/cache@v3 32 | with: 33 | key: rs-${{ runner.os }}-${{ steps.rust-toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.*') }}-bench 34 | restore-keys: | 35 | rs-${{ runner.os }}-${{ steps.rust-toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.*') }}- 36 | rs-${{ runner.os }}-${{ steps.rust-toolchain.outputs.rustc_hash }}- 37 | path: ~/.cargo 38 | - run: cargo install cargo-criterion 39 | working-directory: rs 40 | - run: cargo criterion 41 | working-directory: rs 42 | - run: rsync --archive --delete --verbose --whole-file target/criterion/reports/ ../gh-docs/criterion/ 43 | working-directory: rs 44 | - name: Inline Criterion benchmark results 45 | run: | 46 | cat >>gh-docs/criterion/index.html <<'EOF' 47 | 69 | EOF 70 | - uses: EndBug/add-and-commit@v9 71 | with: 72 | cwd: gh-docs 73 | add: criterion 74 | message: 'Rust Criterion ${{ github.sha }}' 75 | -------------------------------------------------------------------------------- /hs/src/Day15.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module: Day15 3 | Description: 4 | -} 5 | {-# LANGUAGE MultiWayIf, OverloadedStrings #-} 6 | module Day15 (day15a, day15b) where 7 | 8 | import Control.Monad ((>=>), foldM) 9 | import Control.Monad.Writer (execWriter, tell) 10 | import qualified Data.IntSet as IntSet (fromList, size) 11 | import Data.Ix (rangeSize) 12 | import Data.List (foldl') 13 | import Data.Set (Set) 14 | import qualified Data.Set as Set (empty, lookupMax, lookupMin, singleton, spanAntitone, unions) 15 | import Data.Text (Text) 16 | import qualified Data.Text as T (lines, stripPrefix) 17 | import qualified Data.Text.Read as T (decimal, signed) 18 | import GHC.Exts (the) 19 | 20 | parseLine :: (Integral a) => Text -> ((a, a), (a, a)) 21 | parseLine line 22 | | Just line <- T.stripPrefix "Sensor at x=" line 23 | , Right (x0, line) <- T.signed T.decimal line 24 | , Just line <- T.stripPrefix ", y=" line 25 | , Right (y0, line) <- T.signed T.decimal line 26 | , Just line <- T.stripPrefix ": closest beacon is at x=" line 27 | , Right (x1, line) <- T.signed T.decimal line 28 | , Just line <- T.stripPrefix ", y=" line 29 | , Right (y1, "") <- T.signed T.decimal line 30 | = ((x0, y0), (x1, y1)) 31 | 32 | intervalAdd :: Set (Int, Int) -> (Int, Int) -> Set (Int, Int) 33 | intervalAdd intervals (lo, hi) = Set.unions [los, mid', his] where 34 | (los, rest) = Set.spanAntitone (\(_, y) -> y < lo - 1) intervals 35 | (mid, his) = Set.spanAntitone (\(x, _) -> x <= hi + 1) rest 36 | mid' = Set.singleton $ if 37 | | Just (lo', _) <- Set.lookupMin mid 38 | , Just (_, hi') <- Set.lookupMax mid 39 | -> (min lo lo', max hi hi') 40 | | otherwise -> (lo, hi) 41 | 42 | intervalSize :: Set (Int, Int) -> Int 43 | intervalSize = foldl' (flip $ (+) . rangeSize) 0 44 | 45 | intervalGaps :: Int -> Int -> Set (Int, Int) -> [Int] 46 | intervalGaps lo hi = execWriter . (foldM f lo >=> g) where 47 | f x (y, z) = z + 1 <$ tell [x..y - 1] 48 | g x = tell [x..hi] 49 | 50 | day15a :: Int -> Text -> Int 51 | day15a y input = intervalSize intervals - IntSet.size beacons where 52 | inputs = parseLine <$> T.lines input 53 | intervals = foldl' intervalAdd Set.empty 54 | [ (x0 - dx, x0 + dx) 55 | | ((x0, y0), (x1, y1)) <- inputs 56 | , let dx = abs (x0 - x1) + abs (y0 - y1) - abs (y - y0) 57 | , dx >= 0 58 | ] 59 | beacons = IntSet.fromList . map fst . filter ((== y) . snd) $ snd <$> inputs 60 | 61 | day15b :: Int -> Text -> Int 62 | day15b size input = the 63 | [ 4000000 * x + y 64 | | y <- [0..size] 65 | , let intervals = foldl' intervalAdd Set.empty 66 | [ (lo, hi) 67 | | ((x0, y0), (x1, y1)) <- inputs 68 | , let dx = abs (x0 - x1) + abs (y0 - y1) - abs (y - y0) 69 | , let lo = max 0 $ x0 - dx 70 | , let hi = min size $ x0 + dx 71 | , lo <= hi 72 | ] 73 | , x <- intervalGaps 0 size intervals 74 | ] where inputs = parseLine <$> T.lines input 75 | -------------------------------------------------------------------------------- /py/aoc2022/day23.py: -------------------------------------------------------------------------------- 1 | """ 2 | Day 23: Unstable Diffusion 3 | """ 4 | 5 | from collections import defaultdict 6 | from itertools import cycle, islice 7 | 8 | SAMPLE_INPUT = [ 9 | "....#..", 10 | "..###.#", 11 | "#...#.#", 12 | ".#...##", 13 | "#.###..", 14 | "##.#.##", 15 | ".#..#..", 16 | ] 17 | 18 | 19 | def _directions(): 20 | return cycle(("NSWE", "SWEN", "WENS", "ENSW")) 21 | 22 | 23 | def _sides(direction, x, y): 24 | if direction == "N": 25 | return [(x, y - 1) for x in range(x - 1, x + 2)] 26 | if direction == "S": 27 | return [(x, y + 1) for x in range(x - 1, x + 2)] 28 | if direction == "W": 29 | return [(x - 1, y) for y in range(y - 1, y + 2)] 30 | if direction == "E": 31 | return [(x + 1, y) for y in range(y - 1, y + 2)] 32 | raise ValueError() 33 | 34 | 35 | def _move(direction, x, y): 36 | if direction == "N": 37 | return x, y - 1 38 | if direction == "S": 39 | return x, y + 1 40 | if direction == "W": 41 | return x - 1, y 42 | if direction == "E": 43 | return x + 1, y 44 | raise ValueError() 45 | 46 | 47 | def _neighbors(x, y): 48 | return [ 49 | (x2, y2) 50 | for x2 in range(x - 1, x + 2) 51 | for y2 in range(y - 1, y + 2) 52 | if x2 != x or y2 != y 53 | ] 54 | 55 | 56 | def _parse(lines): 57 | return { 58 | (x, y) for y, line in enumerate(lines) for x, c in enumerate(line) if c == "#" 59 | } 60 | 61 | 62 | def _step(state, directions): 63 | proposals = defaultdict(list) 64 | for x, y in state: 65 | if not any(neighbor in state for neighbor in _neighbors(x, y)): 66 | continue 67 | for direction in directions: 68 | if not any(neighbor in state for neighbor in _sides(direction, x, y)): 69 | proposals[_move(direction, x, y)].append((x, y)) 70 | break 71 | state = set(state) 72 | for new, old in proposals.items(): 73 | if len(old) != 1: 74 | continue 75 | state.remove(old[0]) 76 | state.add(new) 77 | return state 78 | 79 | 80 | def part1(lines): 81 | """ 82 | >>> part1(SAMPLE_INPUT) 83 | 110 84 | """ 85 | state = _parse(lines) 86 | for directions in islice(_directions(), 10): 87 | state = _step(state, directions) 88 | min_x, max_x = min(x for x, _ in state), max(x for x, _ in state) 89 | min_y, max_y = min(y for _, y in state), max(y for _, y in state) 90 | return (max_y - min_y + 1) * (max_x - min_x + 1) - len(state) 91 | 92 | 93 | def part2(lines): 94 | """ 95 | >>> part2(SAMPLE_INPUT) 96 | 20 97 | """ 98 | state, previous = _parse(lines), None 99 | for i, directions in enumerate(_directions()): 100 | state = _step(state, directions) 101 | if state == previous: 102 | return i + 1 103 | previous = state 104 | raise LookupError() 105 | 106 | 107 | parts = (part1, part2) 108 | -------------------------------------------------------------------------------- /hs/app/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NondecreasingIndentation #-} 2 | module Main (main) where 3 | 4 | import Day1 (day1a, day1b) 5 | import Day2 (day2a, day2b) 6 | import Day3 (day3a, day3b) 7 | import Day4 (day4a, day4b) 8 | import Day5 (day5a, day5b) 9 | import Day6 (day6a, day6b) 10 | import Day7 (day7a, day7b) 11 | import Day8 (day8a, day8b) 12 | import Day9 (day9a, day9b) 13 | import Day10 (day10a, day10b) 14 | import Day11 (day11a, day11b) 15 | import Day12 (day12) 16 | import Day13 (day13a, day13b) 17 | import Day13Fast (day13aFast, day13bFast) 18 | import Day14 (day14) 19 | import Day15 (day15a, day15b) 20 | import Day16 (day16) 21 | import Day17 (day17) 22 | import Day18 (day18a, day18b) 23 | import Day19 (day19a, day19b) 24 | import Day20 (day20a, day20b) 25 | import Day21 (day21a, day21b) 26 | import Day22 (day22a, day22b) 27 | import Day23 (day23a, day23b) 28 | import Day24 (day24a, day24b) 29 | import Day25 (day25) 30 | 31 | import Control.Monad ((<=<), ap, when) 32 | import Data.Function (on) 33 | import Data.Maybe (mapMaybe) 34 | import Data.Text (Text) 35 | import qualified Data.Text.IO as TIO (putStrLn, readFile) 36 | import Paths_aoc2022 (getDataFileName) 37 | import System.Environment (getArgs) 38 | import Text.Megaparsec (errorBundlePretty) 39 | import Text.Read (readMaybe) 40 | 41 | getDayInput :: Int -> IO Text 42 | getDayInput i = getDataFileName ("day" ++ show i ++ ".txt") >>= TIO.readFile 43 | 44 | run :: Int -> (a -> IO ()) -> [Text -> a] -> IO () 45 | run = run' `ap` show 46 | 47 | run' :: Int -> String -> (a -> IO ()) -> [Text -> a] -> IO () 48 | run' day name showIO funcs = do 49 | args <- getArgs 50 | when (null args || name `elem` args) $ do 51 | putStrLn $ "Day " ++ name 52 | contents <- getDayInput day 53 | mapM_ (showIO . ($ contents)) funcs 54 | putStrLn "" 55 | 56 | main :: IO () 57 | main = do 58 | run 1 print [day1a, day1b] 59 | run 2 print [day2a, day2b] 60 | run 3 print [day3a, day3b] 61 | run 4 (either fail print) [day4a, day4b] 62 | run 5 TIO.putStrLn [day5a, day5b] 63 | run 6 (maybe (fail "(⊥)") print) [day6a, day6b] 64 | run 7 print [day7a, day7b] 65 | run 8 print [day8a, day8b] 66 | run 9 print [day9a, day9b] 67 | run 10 putStrLn [show . day10a, day10b] 68 | run 11 (either (fail . errorBundlePretty) print) [day11a, day11b] 69 | run 12 (uncurry ((>>) `on` maybe (fail "(⊥)") print)) [day12] 70 | run 13 (either (fail . errorBundlePretty) print) [day13a, day13b] 71 | run' 13 "13Fast" print [day13aFast, day13bFast] 72 | run 14 (uncurry ((>>) `on` print)) [day14] 73 | run 15 print [day15a 2000000, day15b 4000000] 74 | run 16 (either (fail . errorBundlePretty) print) [day16 1 30, day16 2 26] 75 | run 17 print [day17 2022, day17 1000000000000] 76 | run 18 print [day18a, day18b] 77 | run 19 (either (fail . errorBundlePretty) print) [day19a, day19b] 78 | run 20 (either fail print) [day20a, day20b] 79 | run 21 (either (fail . errorBundlePretty) print) [day21a, day21b] 80 | run 22 print [day22a, day22b] 81 | run 23 print [day23a, day23b] 82 | run 24 (maybe (fail "(⊥)") print) [day24a, day24b] 83 | run 25 TIO.putStrLn [day25] 84 | -------------------------------------------------------------------------------- /rs/src/day18.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::{max, min}; 2 | use std::collections::BTreeSet; 3 | 4 | fn parse_point(s: &str) -> Option<(i32, i32, i32)> { 5 | let mut iter = s.splitn(3, ','); 6 | Some(( 7 | iter.next()?.parse().ok()?, 8 | iter.next()?.parse().ok()?, 9 | iter.next()?.parse().ok()?, 10 | )) 11 | } 12 | 13 | fn neighbors((x, y, z): (i32, i32, i32)) -> [(i32, i32, i32); 6] { 14 | [ 15 | (x - 1, y, z), 16 | (x + 1, y, z), 17 | (x, y - 1, z), 18 | (x, y + 1, z), 19 | (x, y, z - 1), 20 | (x, y, z + 1), 21 | ] 22 | } 23 | 24 | pub fn part1<'a, I, S>(lines: I) -> usize 25 | where 26 | I: IntoIterator, 27 | S: AsRef + 'a, 28 | { 29 | let points = lines 30 | .into_iter() 31 | .filter_map(|line| parse_point(line.as_ref())) 32 | .collect::>(); 33 | points 34 | .iter() 35 | .copied() 36 | .flat_map(neighbors) 37 | .filter(|point| !points.contains(point)) 38 | .count() 39 | } 40 | 41 | pub fn part2<'a, I, S>(lines: I) -> usize 42 | where 43 | I: IntoIterator, 44 | S: AsRef + 'a, 45 | { 46 | let (mut min_x, mut max_x, mut min_y, mut max_y, mut min_z, mut max_z) = 47 | (i32::MAX, i32::MIN, i32::MAX, i32::MIN, i32::MAX, i32::MIN); 48 | let points = lines 49 | .into_iter() 50 | .filter_map(|line| parse_point(line.as_ref())) 51 | .inspect(|&(x, y, z)| { 52 | min_x = min(min_x, x); 53 | max_x = max(max_x, x); 54 | min_y = min(min_y, y); 55 | max_y = max(max_y, y); 56 | min_z = min(min_z, z); 57 | max_z = max(max_z, z); 58 | }) 59 | .collect::>(); 60 | let mut queue = vec![(min_x - 1, min_y - 1, min_z - 1)]; 61 | let mut outside = queue.iter().copied().collect::>(); 62 | while let Some(point) = queue.pop() { 63 | for (x, y, z) in neighbors(point) { 64 | if (min_x - 1..=max_x + 1).contains(&x) 65 | && (min_y - 1..=max_y + 1).contains(&y) 66 | && (min_z - 1..=max_z + 1).contains(&z) 67 | && !points.contains(&(x, y, z)) 68 | && outside.insert((x, y, z)) 69 | { 70 | queue.push((x, y, z)); 71 | } 72 | } 73 | } 74 | points 75 | .iter() 76 | .copied() 77 | .flat_map(neighbors) 78 | .filter(|point| outside.contains(point)) 79 | .count() 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use super::*; 85 | use pretty_assertions::assert_eq; 86 | 87 | static EXAMPLE: &[&str] = &[ 88 | "2,2,2", "1,2,2", "3,2,2", "2,1,2", "2,3,2", "2,2,1", "2,2,3", "2,2,4", "2,2,6", "1,2,5", 89 | "3,2,5", "2,1,5", "2,3,5", 90 | ]; 91 | 92 | #[test] 93 | fn part1_examples() { 94 | assert_eq!(64, part1(EXAMPLE)); 95 | } 96 | 97 | #[test] 98 | fn part2_examples() { 99 | assert_eq!(58, part2(EXAMPLE)); 100 | } 101 | } 102 | --------------------------------------------------------------------------------