class Day12(lines:List<String>) {
    private val paths = buildPaths(lines)
    fun solvePart1() = traverse(::part1VisitRule).size
    fun solvePart2() = traverse(::part2VisitRule).size


    private fun buildPaths(input: List<String>): Map<String, List<String>> =
        input
            .map { it.split("-") }
            .flatMap {
                listOf(
                    it.first() to it.last(),
                    it.last() to it.first()
                )
            }
            .groupBy({ it.first }, { it.second })


    private fun traverse(
        allowedToVisit: (String, List<String>) -> Boolean,
        path: List<String> = listOf("start")
    ): List<List<String>> =
        if (path.last() == "end") listOf(path)
        else paths.getValue(path.last())
            .filter { allowedToVisit(it, path) }
            .flatMap { traverse(allowedToVisit, path + it) }

    private fun part1VisitRule(name: String, path: List<String>): Boolean =
        name.isUpperCase() || name !in path

    private fun part2VisitRule(name: String, path: List<String>): Boolean =
        when {
            name.isUpperCase() -> true
            name == "start" -> false
            name !in path -> true
            else -> path
                .filterNot { it.isUpperCase() }
                .groupBy { it }
                .none { it.value.size == 2 }
        }



    private fun String.isUpperCase(): Boolean =
        all { it.isUpperCase() }
}
class Day12(lines:List<String>) {
    private val paths = buildPaths(lines)
    fun solvePart1() = traverse(::part1VisitRule).size
    fun solvePart2() = traverse(::part2VisitRule).size


    private fun buildPaths(input: List<String>): Map<String, List<String>> =
        input
            .map { it.split("-") }
            .flatMap {
                listOf(
                    it.first() to it.last(),
                    it.last() to it.first()
                )
            }
            .groupBy({ it.first }, { it.second })


    private fun traverse(
        allowedToVisit: (String, List<String>) -> Boolean,
        path: List<String> = listOf("start")
    ): List<List<String>> =
        if (path.last() == "end") listOf(path)
        else paths.getValue(path.last())
            .filter { allowedToVisit(it, path) }
            .flatMap { traverse(allowedToVisit, path + it) }

    private fun part1VisitRule(name: String, path: List<String>): Boolean =
        name.isUpperCase() || name !in path

    private fun part2VisitRule(name: String, path: List<String>): Boolean =
        when {
            name.isUpperCase() -> true
            name == "start" -> false
            name !in path -> true
            else -> path
                .filterNot { it.isUpperCase() }
                .groupBy { it }
                .none { it.value.size == 2 }
        }



    private fun String.isUpperCase(): Boolean =
        all { it.isUpperCase() }
}