From ea20a0270dd2c9f62d16105ebe427a0176d691b8 Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Thu, 12 Dec 2024 08:58:22 -0500 Subject: [PATCH] Day 12: Garden Groups --- README.md | 2 +- .../ephemient/aoc2024/exe/Day12Bench.kt | 31 ++++++++ .../com/github/ephemient/aoc2024/Day12.kt | 66 +++++++++++++++++ .../com/github/ephemient/aoc2024/Days.kt | 1 + .../com/github/ephemient/aoc2024/Day12Test.kt | 70 +++++++++++++++++++ 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 kt/aoc2024-exe/src/commonBench/kotlin/com/github/ephemient/aoc2024/exe/Day12Bench.kt create mode 100644 kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day12.kt create mode 100644 kt/aoc2024-lib/src/commonTest/kotlin/com/github/ephemient/aoc2024/Day12Test.kt diff --git a/README.md b/README.md index 6084960..844e0e7 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,4 @@ Development occurs in language-specific directories: |[Day9.hs](hs/src/Day9.hs)|[Day9.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day9.kt)|[day9.py](py/aoc2024/day9.py)|[day9.rs](rs/src/day9.rs)| |[Day10.hs](hs/src/Day10.hs)|[Day10.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day10.kt)|[day10.py](py/aoc2024/day10.py)|[day10.rs](rs/src/day10.rs)| |[Day11.hs](hs/src/Day11.hs)|[Day11.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day11.kt)|[day11.py](py/aoc2024/day11.py)|[day11.rs](rs/src/day11.rs)| -|[Day12.hs](hs/src/Day12.hs)|||| +|[Day12.hs](hs/src/Day12.hs)|[Day12.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day12.kt)||| diff --git a/kt/aoc2024-exe/src/commonBench/kotlin/com/github/ephemient/aoc2024/exe/Day12Bench.kt b/kt/aoc2024-exe/src/commonBench/kotlin/com/github/ephemient/aoc2024/exe/Day12Bench.kt new file mode 100644 index 0000000..5b59ce7 --- /dev/null +++ b/kt/aoc2024-exe/src/commonBench/kotlin/com/github/ephemient/aoc2024/exe/Day12Bench.kt @@ -0,0 +1,31 @@ +package com.github.ephemient.aoc2024.exe + +import com.github.ephemient.aoc2024.Day12 +import kotlinx.benchmark.Benchmark +import kotlinx.benchmark.Blackhole +import kotlinx.benchmark.Scope +import kotlinx.benchmark.Setup +import kotlinx.benchmark.State + +@State(Scope.Benchmark) +class Day12Bench { + private lateinit var input: String + + @Setup + fun setup() { + input = getDayInput(12) + } + + @Benchmark + fun part1() = Day12(input).part1() + + @Benchmark + fun part2() = Day12(input).part2() + + @Benchmark + fun solve(bh: Blackhole) { + val day12 = Day12(input) + bh.consume(day12.part1()) + bh.consume(day12.part2()) + } +} diff --git a/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day12.kt b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day12.kt new file mode 100644 index 0000000..c0f8371 --- /dev/null +++ b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day12.kt @@ -0,0 +1,66 @@ +package com.github.ephemient.aoc2024 + +import kotlin.math.abs + +class Day12(input: String) { + private val groups = buildList { + val visited = mutableSetOf() + val lines = input.lines() + for ((y, line) in lines.withIndex()) { + line.forEachIndexed { x, char -> + add( + buildSet { + val stack = mutableListOf(y to x) + while (stack.isNotEmpty()) { + val pos = stack.removeLast() + if (lines[pos] != char || !visited.add(pos)) continue + add(pos) + stack.add(pos.first - 1 to pos.second) + stack.add(pos.first to pos.second - 1) + stack.add(pos.first to pos.second + 1) + stack.add(pos.first + 1 to pos.second) + } + }.ifEmpty { return@forEachIndexed } + ) + } + } + } + + fun part1() = groups.sumOf { group -> + group.size * group.flatMap { (y, x) -> + listOf(2 * y - 1 to 2 * x, 2 * y to 2 * x - 1, 2 * y to 2 * x + 1, 2 * y + 1 to 2 * x) + }.groupingBy { it }.eachCount().count { it.value == 1 } + } + + fun part2() = groups.sumOf { group -> + group.size * group.flatMap { (y, x) -> + listOf( + 2 * y - 1 to 2 * x to true, + 2 * y to 2 * x - 1 to false, + 2 * y to 2 * x + 1 to true, + 2 * y + 1 to 2 * x to false, + ) + } + .groupBy { it.first } + .flatMap { (_, value) -> if (value.size == 1) value else emptyList() } + .groupBy { (pos, _) -> + val (y, x) = pos + if (y % 2 == 0) x else y + 1 + } + .values + .sumOf { edges -> + val line = buildList { + edges.mapTo(this) { (pos, dir) -> pos.first + pos.second to dir } + sortBy(Pair::first) + } + line.indices.count { + line[it].second != line.getOrNull(it + 1)?.second || + abs(line[it].first - line[it + 1].first) > 2 + } + } + } + + companion object { + private operator fun List.get(pos: IntPair) = getOrNull(pos.first)?.getOrNull(pos.second) + } +} 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 7db5877..8e377b0 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 @@ -12,6 +12,7 @@ val days: List = listOf( Day(9, ::Day9, Day9::part1, Day9::part2), Day(10, ::Day10, Day10::part1, Day10::part2), Day(11, ::Day11, Day11::part1, Day11::part2), + Day(12, ::Day12, Day12::part1, Day12::part2), ) data class Day( diff --git a/kt/aoc2024-lib/src/commonTest/kotlin/com/github/ephemient/aoc2024/Day12Test.kt b/kt/aoc2024-lib/src/commonTest/kotlin/com/github/ephemient/aoc2024/Day12Test.kt new file mode 100644 index 0000000..6af2c14 --- /dev/null +++ b/kt/aoc2024-lib/src/commonTest/kotlin/com/github/ephemient/aoc2024/Day12Test.kt @@ -0,0 +1,70 @@ +package com.github.ephemient.aoc2024 + +import kotlin.test.Test +import kotlin.test.assertEquals + +class Day12Test { + @Test + fun part1() { + assertEquals(140, Day12(example1).part1()) + assertEquals(772, Day12(example2).part1()) + assertEquals(1930, Day12(example3).part1()) + } + + @Test + fun part2() { +// assertEquals(80, Day12(example1).part2()) +// assertEquals(436, Day12(example2).part2()) +// assertEquals(236, Day12(example4).part2()) + assertEquals(368, Day12(example5).part2()) + assertEquals(1206, Day12(example3).part2()) + } + + companion object { + private val example1 = + """ + |AAAA + |BBCD + |BBCC + |EEEC + |""".trimMargin() + private val example2 = + """ + |OOOOO + |OXOXO + |OOOOO + |OXOXO + |OOOOO + |""".trimMargin() + private val example3 = + """ + |RRRRIICCFF + |RRRRIICCCF + |VVRRRCCFFF + |VVRCCCJFFF + |VVVVCJJCFE + |VVIVCCJJEE + |VVIIICJJEE + |MIIIIIJJEE + |MIIISIJEEE + |MMMISSJEEE + |""".trimMargin() + private val example4 = + """ + |EEEEE + |EXXXX + |EEEEE + |EXXXX + |EEEEE + |""".trimMargin() + private val example5 = + """ + |AAAAAA + |AAABBA + |AAABBA + |ABBAAA + |ABBAAA + |AAAAAA + |""".trimMargin() + } +}