diff --git a/README.md b/README.md index fc48442..01cdc3d 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ Development occurs in language-specific directories: |[Day1.hs](hs/src/Day1.hs)|[Day1.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day1.kt)|[day1.py](py/aoc2024/day1.py)|[day1.rs](rs/src/day1.rs)| |[Day2.hs](hs/src/Day2.hs)|[Day2.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day2.kt)|[day2.py](py/aoc2024/day2.py)|[day2.rs](rs/src/day2.rs)| |[Day3.hs](hs/src/Day3.hs)|[Day3.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day3.kt)|[day3.py](py/aoc2024/day3.py)|[day3.rs](rs/src/day3.rs)| -|[Day4.hs](hs/src/Day4.hs)|||| +|[Day4.hs](hs/src/Day4.hs)|[Day4.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day4.kt)||| diff --git a/kt/aoc2024-exe/src/commonBench/kotlin/com/github/ephemient/aoc2024/exe/Day4Bench.kt b/kt/aoc2024-exe/src/commonBench/kotlin/com/github/ephemient/aoc2024/exe/Day4Bench.kt new file mode 100644 index 0000000..4f40e45 --- /dev/null +++ b/kt/aoc2024-exe/src/commonBench/kotlin/com/github/ephemient/aoc2024/exe/Day4Bench.kt @@ -0,0 +1,23 @@ +package com.github.ephemient.aoc2024.exe + +import com.github.ephemient.aoc2024.Day4 +import kotlinx.benchmark.Benchmark +import kotlinx.benchmark.Scope +import kotlinx.benchmark.Setup +import kotlinx.benchmark.State + +@State(Scope.Benchmark) +class Day4Bench { + private lateinit var input: String + + @Setup + fun setup() { + input = getDayInput(4) + } + + @Benchmark + fun part1() = Day4(input).part1() + + @Benchmark + fun part2() = Day4(input).part2() +} diff --git a/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day4.kt b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day4.kt new file mode 100644 index 0000000..b68f7c1 --- /dev/null +++ b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day4.kt @@ -0,0 +1,46 @@ +package com.github.ephemient.aoc2024 + +class Day4( + input: String, +) { + private val lines = input.lines() + + fun part1() = lines.indices.sumOf { y -> + lines[y].indices.sumOf { x -> + Direction.entries.count { (dx, dy) -> + "XMAS".withIndex().all { (i, c) -> + lines.getOrNull(y + i * dy)?.getOrNull(x + i * dx) == c + } + } + } + } + + fun part2() = lines.indices.sumOf { y -> + lines[y].indices.count { x -> + if (lines[y][x] != 'A') return@count false + val n = lines.getOrNull(y - 1) ?: return@count false + val s = lines.getOrNull(y + 1) ?: return@count false + val nw = n.getOrNull(x - 1) ?: return@count false + val ne = n.getOrNull(x + 1) ?: return@count false + val sw = s.getOrNull(x - 1) ?: return@count false + val se = s.getOrNull(x + 1) ?: return@count false + (nw == 'M' && se == 'S' || se == 'M' && nw == 'S') && + (sw == 'M' && ne == 'S' || ne == 'M' && sw == 'S') + } + } + + private enum class Direction(val dx: Int, val dy: Int) { + EAST(1, 0), + NORTHEAST(1, -1), + NORTH(0, -1), + NORTHWEST(-1, -1), + WEST(-1, 0), + SOUTHWEST(-1, 1), + SOUTH(0, 1), + SOUTHEAST(1, 1), + ; + + operator fun component1() = dx + operator fun component2() = dy + } +} diff --git a/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Days.kt b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Days.kt index a277ec6..1b3b8d8 100644 --- a/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Days.kt +++ b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Days.kt @@ -4,6 +4,7 @@ val days: List = listOf( Day(1, ::Day1, Day1::part1, Day1::part2), Day(2, ::Day2, Day2::part1, Day2::part2), Day(3, ::Day3, Day3::part1, Day3::part2), + Day(4, ::Day4, Day4::part1, Day4::part2), ) data class Day( diff --git a/kt/aoc2024-lib/src/commonTest/kotlin/com/github/ephemient/aoc2024/Day4Test.kt b/kt/aoc2024-lib/src/commonTest/kotlin/com/github/ephemient/aoc2024/Day4Test.kt new file mode 100644 index 0000000..0a8d440 --- /dev/null +++ b/kt/aoc2024-lib/src/commonTest/kotlin/com/github/ephemient/aoc2024/Day4Test.kt @@ -0,0 +1,32 @@ +package com.github.ephemient.aoc2024 + +import kotlin.test.Test +import kotlin.test.assertEquals + +class Day4Test { + @Test + fun part1() { + assertEquals(18, Day4(example).part1()) + } + + @Test + fun part2() { + assertEquals(9, Day4(example).part2()) + } + + companion object { + private val example = + """ + |MMMSXXMASM + |MSAMXMSMSA + |AMXSXMAAMM + |MSAMASMSMX + |XMASAMXAMM + |XXAMMXXAMA + |SMSMSASXSS + |SAXAMASAAA + |MAMMMXMMMM + |MXMXAXMASX + |""".trimMargin() + } +}