Skip to content

Commit

Permalink
2024 - Day 12 - part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
fmmr committed Dec 13, 2024
1 parent d936a59 commit 6e2462c
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 89 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/no/rodland/advent/Dir.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package no.rodland.advent

enum class Dir(val move: (input: Pos, howMuch: Int) -> Pos) {
N({ pos, howMuch -> pos.above(howMuch) }),
N({ pos, howMuch -> pos.above(howMuch) }),
S({ pos, howMuch -> pos.below(howMuch) }),
W({ pos, howMuch -> pos.left(howMuch) }),
E({ pos, howMuch -> pos.right(howMuch) }),
Expand Down
137 changes: 52 additions & 85 deletions src/main/kotlin/no/rodland/advent_2024/Day12.kt
Original file line number Diff line number Diff line change
@@ -1,124 +1,91 @@
package no.rodland.advent_2024

import no.rodland.advent.Day
import no.rodland.advent.Direction
import no.rodland.advent.Pos
import kotlin.math.abs

// template generated: 12/12/2024
// Fredrik Rødland 2024

class Day12(val input: List<String>) : Day<Int, Int, Pair<Array<CharArray>, Array<CharArray>>> {
class Day12(val input: List<String>) : Day<Int, Int, Array<CharArray>> {

private val parsed = input.parse()
private val grid = parsed.first
private val rotated = parsed.second
private val grid = input.parse()

data class Region(val c: Char, val positions: Set<Pos>) {
operator fun contains(pos: Pos): Boolean = pos in positions
fun area() = positions.size
fun neighbours() = positions.flatMap { it.neighbourCellsUDLR() }.filterNot { it in positions }
fun perimeter(): Int = neighbours().size
}

override fun partOne(): Int {
return findAllRegions().sumOf {
it.area() * it.perimeter()
return findAllRegions().sumOf { region ->
region.area() * region.perimeter()
}
}

override fun partTwo(): Int {
return findAllRegions().sumOf { region ->
region.sides() * region.area()
}
}

private fun Region.sides(): Int {
return positions.sumOf { p -> p.corners() }
}

// got help from: https://todd.ginsberg.com/post/advent-of-code/2024/day12/
private fun Pos.corners(): Int {
val c = grid[this]
return listOf(Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH)
.zipWithNext()
.map { (first, second) ->
listOf(
grid[next(first)],
grid[next(second)],
grid[next(first).next(second)]
)
}.count { (side1, side2, corner) ->
(c != side1 && c != side2) || (side1 == c && side2 == c && corner != c)
}
}

@Suppress("ConvertCallChainIntoSequence")
private fun findAllRegions(): List<Region> {
fun getRegion(grid: Grid, pos: Pos, visited: MutableSet<Pos>): Set<Pos> {
return (setOf(pos) + pos.neighbourCellsUDLR()
.filter { it !in visited }
.filter { it in this@Day12.grid }
.filter { this@Day12.grid[it] == this@Day12.grid[pos] }
.onEach { visited.add(it) }
.flatMap { getRegion(grid, it, visited) }
.toList()).toSet()
}

val visited = mutableSetOf<Pos>()
return grid.flatMapIndexed { y, row ->
row.mapIndexed { x, c ->
val pos = Pos(x, y)
if (pos !in visited) {
Region(c, grid.getRegion(pos, visited))
Region(c, getRegion(grid, pos, visited))
} else {
null
}
}.filterNotNull()
}
}

@Suppress("ConvertCallChainIntoSequence")
private fun Array<CharArray>.getRegion(pos: Pos, visited: MutableSet<Pos>): Set<Pos> {
return (setOf(pos) + pos.neighbourCellsUDLR()
.filter { it !in visited }
.filter { it in grid }
.filter { grid[it] == grid[pos] }
.onEach { visited.add(it) }
.flatMap { getRegion(it, visited) }
.toList()).toSet()
}


override fun partTwo(): Int {
val regions = findAllRegions()
// regions.forEach { println(it.toString() + ": " + it.neighbours()) }
//
// val initMaybe = regions[0].neighbours().map { setOf(it) }
// val test0 = regions[0].neighbours().fold(initMaybe) { acc: List<Set<Pos>>, pos: Pos ->
// val (fences, rest) = acc.partition { fence -> fence.sameFence(pos) }
// if (fences.isEmpty()) {
// rest
// } else {
// (rest + fences.map { it + pos }).filterNot { it == setOf(pos) }
// }
// }.map { it.sorted() }.toSet()
return 2
}

private fun Set<Pos>.sameFence(pos: Pos): Boolean {
return if (size == 0) {
error("should not happen")
} else {
val first = first()
if (first == pos) {
true
} else if (size == 1) {
first.isAdjacent(pos)
} else if (all { it.x == first.x }) {
any { it.isAdjacentY(pos) }
} else if (all { it.y == first.y }) {
any { it.isAdjacentX(pos) }
} else {
error("should not happen either")
}
}


TODO("Not yet implemented")
}

fun Pos.isAdjacent(other: Pos) = abs(x - other.x) + abs(y - other.y) == 1
fun Pos.isAdjacentX(other: Pos) = abs(x - other.x) == 1 && y == other.y
fun Pos.isAdjacentY(other: Pos) = abs(y - other.y) == 1 && x == other.x

operator fun Grid.contains(pos: Pos): Boolean = pos.x >= 0 && pos.x < this[0].size && pos.y >= 0 && pos.y < this.size

operator fun Grid.get(pos: Pos): Char = this[pos.y][pos.x]

override fun List<String>.parse(): Pair<Array<CharArray>, Array<CharArray>> {
val upright = indices.map { y -> indices.map { x -> this[y][x] }.toCharArray() }.toTypedArray()
val rotated = indices.map { y -> indices.map { x -> this[x][y] }.toCharArray() }.toTypedArray()
return upright to rotated
}
operator fun Grid.get(pos: Pos): Char? = if (pos in this) grid[pos.y][pos.x] else null

override fun List<String>.parse(): Grid = indices.map { y -> indices.map { x -> this[y][x] }.toCharArray() }.toTypedArray<CharArray>()

override val day = "12".toInt()

// private fun Grid.fences(): Int {
// flatMap { row ->
// val windowed = row.toList().windowed(2).map { (c1, c2) ->
// if (c1 == c2) 0 else 2
// }
// windowed
// }
// return 2
// }


data class Region(val c: Char, val positions: Set<Pos>) {
operator fun contains(pos: Pos): Boolean = pos in positions
fun area() = positions.size
fun neighbours() = positions.flatMap { it.neighbourCellsUDLR() }.filterNot { it in positions }
fun perimeter(): Int = neighbours().size
}
}




6 changes: 3 additions & 3 deletions src/test/kotlin/no/rodland/advent_2024/Day12Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ internal class Day12Test {
private val test12 = "2024/input_12_test.txt".readFile()

private val resultTestOne = 1930
private val resultTestTwo = 2
private val resultTestTwo = 1206
private val resultOne = 1424472
private val resultTwo = 2
private val resultTwo = 870202

val test = defaultTestSuiteParseOnInit(
Day12(data12),
Expand All @@ -30,7 +30,7 @@ internal class Day12Test {
{ Day12(data12) },
{ Day12(test12) },
numTestPart1 = 5,
numTestPart2 = 1
numTestPart2 = 5
)

@Nested
Expand Down

0 comments on commit 6e2462c

Please sign in to comment.