~macaptain/advent-of-code

8283e35d9e1a998fcbca6d8ab5890b23441e0dd7 — Michael Captain 2 years ago b87ba47
Complete 2021 Day 12
A src/main/advent2021/Day12.kt => src/main/advent2021/Day12.kt +73 -0
@@ 0,0 1,73 @@
package advent2021

data class Cave(val name: String) {
    private val connectedCaves = mutableSetOf<Cave>()
    fun addNeighbour(cave: Cave) {
        connectedCaves.add(cave)
    }

    fun isSmall() = name.lowercase() == name

    fun connected() = connectedCaves.toSet()

    companion object {
        fun systemFromLines(lines: List<String>): Set<Cave> {
            val caves = mutableSetOf<Cave>()
            lines.forEach { line ->
                val (l, r) = line.split("-")
                caves.add(Cave(l))
                caves.add(Cave(r))
                val caveL = caves.first { it.name == l }
                val caveR = caves.first { it.name == r }
                caveL.addNeighbour(caveR)
                caveR.addNeighbour(caveL)
            }
            return caves
        }
    }
}

fun numPaths(caves: Set<Cave>, smallCaveLimit: Int = 1): Int {
    val start = caves.first { it.name == "start" }
    val q = ArrayDeque<List<Cave>>()
    q.add(listOf(start))
    var pathCount = 0
    while (q.isNotEmpty()) {
        val nextPath = q.removeFirst()
        val lastCave = nextPath.last()
        if (lastCave.name == "end") {
            pathCount += 1
            continue
        }
        lastCave.connected().forEach { connectedCave ->
            if (connectedCave.name == "start") {
                return@forEach
            }
            if (connectedCave.isSmall() && connectedCave in nextPath) {
                val smallCavesVisited = nextPath.filter { it.isSmall() }
                val maxSmallCavesVisited = smallCavesVisited.maxOf { cave -> nextPath.count { it == cave } }
                if (maxSmallCavesVisited >= smallCaveLimit) {
                    return@forEach
                }
            }
            q.add(nextPath.plus(connectedCave))
        }
    }
    return pathCount
}

fun solve12a(lines: List<String>): Int {
    val caves = Cave.systemFromLines(lines)
    return numPaths(caves)
}

fun solve12b(lines: List<String>): Int {
    val caves = Cave.systemFromLines(lines)
    return numPaths(caves, smallCaveLimit = 2)
}

fun main() {
    val input = input("day12")
    println(solve12a(input)) // 4413
    println(solve12b(input)) // 118803
}
\ No newline at end of file

A src/main/resources/advent2021/day12_input.txt => src/main/resources/advent2021/day12_input.txt +23 -0
@@ 0,0 1,23 @@
pn-TY
rp-ka
az-aw
al-IV
pn-co
end-rp
aw-TY
rp-pn
al-rp
end-al
IV-co
end-TM
co-TY
TY-ka
aw-pn
aw-IV
pn-IV
IV-ka
TM-rp
aw-PD
start-IV
start-co
start-pn

A src/test/advent2021/Day12KtTest.kt => src/test/advent2021/Day12KtTest.kt +66 -0
@@ 0,0 1,66 @@
package advent2021

import org.junit.jupiter.api.Test

import org.junit.jupiter.api.Assertions.*

internal class Day12KtTest {

    val example1 = listOf(
        "start-A",
        "start-b",
        "A-c",
        "A-b",
        "b-d",
        "A-end",
        "b-end"
    )

    val example2 = listOf(
        "dc-end",
        "HN-start",
        "start-kj",
        "dc-start",
        "dc-HN",
        "LN-dc",
        "HN-end",
        "kj-sa",
        "kj-HN",
        "kj-dc"
    )

    val example3 = listOf(
        "fs-end",
        "he-DX",
        "fs-he",
        "start-DX",
        "pj-DX",
        "end-zg",
        "zg-sl",
        "zg-pj",
        "pj-he",
        "RW-he",
        "fs-DX",
        "pj-RW",
        "zg-RW",
        "start-pj",
        "he-WI",
        "zg-he",
        "pj-fs",
        "start-RW"
    )

    @Test
    fun example12a() {
        assertEquals(10, solve12a(example1))
        assertEquals(19, solve12a(example2))
        assertEquals(226, solve12a(example3))
    }

    @Test
    fun example12b() {
        assertEquals(36, solve12b(example1))
        assertEquals(103, solve12b(example2))
        assertEquals(3509, solve12b(example3))
    }
}
\ No newline at end of file