Skip to content

Commit

Permalink
Solve Day 12
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulWoitaschek committed Dec 12, 2024
1 parent 9d55b16 commit 1582c56
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
70 changes: 70 additions & 0 deletions 2024/src/main/kotlin/aoc/year2024/Day12.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package aoc.year2024

import aoc.library.Direction
import aoc.library.Point
import aoc.library.Puzzle
import aoc.library.move

object Day12 : Puzzle<Int, Int>(12) {

override fun solvePart2(input: String): Int = solve(input, ::perimeterV2)

override fun solvePart1(input: String): Int = solve(input, ::perimeterV1)

private fun solve(
input: String,
perimeter: (Set<Point>) -> Int,
): Int {
val garden = parse(input)
return clusterByRegions(garden).sumOf {
val pointsInRegion = it.points
perimeter(pointsInRegion) * pointsInRegion.size
}
}

private fun clusterByRegions(garden: Map<Point, Char>): List<Region> {
val regions = mutableListOf<Region>()
val visited = mutableSetOf<Point>()
garden.keys.forEach { start ->
if (start !in visited) {
val groupChar = garden.getValue(start)
val group = mutableSetOf<Point>()
fun visit(point: Point) {
if (!group.add(point)) return
point.adjacentOrthogonal()
.forEach { adjacent ->
val char = garden[adjacent]
if (char == groupChar) {
visit(adjacent)
}
}
}
visit(start)
regions += Region(groupChar, group)
visited.addAll(group)
}
}
return regions
}

private fun parse(input: String): Map<Point, Char> = input.lines().flatMapIndexed { y, line ->
line.mapIndexed { x, char ->
Point(x, y) to char
}
}.toMap()

private fun perimeterV1(points: Set<Point>): Int = points.sumOf { point ->
point.adjacentOrthogonal().count { it !in points }
}

private fun perimeterV2(points: Set<Point>): Int {
fun Point.hasFence(direction: Direction): Boolean = this in points && move(direction) !in points
return points.sumOf { point ->
Direction.entries.count { direction ->
point.hasFence(direction) && !point.move(direction.clockwise()).hasFence(direction)
}
}
}

private data class Region(val char: Char, val points: Set<Point>)
}
79 changes: 79 additions & 0 deletions 2024/src/test/kotlin/aoc/year2024/Day12Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package aoc.year2024

import aoc.library.solvePart1
import aoc.library.solvePart2
import io.kotest.matchers.ints.shouldBeExactly
import org.junit.jupiter.api.Test

class Day12Test {

@Test
fun part1TestInput() {
Day12.solvePart1(
"""
AAAA
BBCD
BBCC
EEEC
""".trimIndent(),
) shouldBeExactly 140
}

@Test
fun part1TestInput2() {
Day12.solvePart1(
"""
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
""".trimIndent(),
) shouldBeExactly 1930
}

@Test
fun part1() {
Day12.solvePart1() shouldBeExactly 1434856
}

@Test
fun part2TestInput() {
Day12.solvePart2(
"""
AAAA
BBCD
BBCC
EEEC
""".trimIndent(),
) shouldBeExactly 80
}

@Test
fun part2TestInput2() {
Day12.solvePart2(
"""
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
""".trimIndent(),
) shouldBeExactly 1206
}

@Test
fun part2() {
Day12.solvePart2() shouldBeExactly 891106
}
}

0 comments on commit 1582c56

Please sign in to comment.