aboutsummaryrefslogtreecommitdiff
path: root/aoc-2021-kotlin/src/Day10.kt
blob: d6d026c69d3d4a9a376beb6d57d0b8a121768753 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
object Day10 {
    private enum class ChunkDelimiter(val open: Char, val close: Char) {
        Parentheses('(', ')'),
        Brackets('[', ']'),
        Braces('{', '}'),
        Angled('<', '>');

        companion object {
            fun isOpening(character: Char): Boolean = values().any { it.open == character }
            fun isClosing(character: Char): Boolean = values().any { it.close == character }
            fun from(character: Char): ChunkDelimiter =
                values().find { it.open == character || it.close == character }!!
        }
    }

    private sealed class SyntaxError {
        object None : SyntaxError()
        class IncompleteLine(private val stack: ArrayDeque<ChunkDelimiter>) : SyntaxError() {
            val score: Long
                get() {
                    fun delimiterValue(delimiter: ChunkDelimiter): Int = when (delimiter) {
                        ChunkDelimiter.Parentheses -> 1
                        ChunkDelimiter.Brackets -> 2
                        ChunkDelimiter.Braces -> 3
                        ChunkDelimiter.Angled -> 4
                    }

                    return stack.fold(0) { acc, elem -> acc * 5 + delimiterValue(elem) }
                }
        }

        class CorruptedLine(private val invalidDelimiter: ChunkDelimiter) : SyntaxError() {
            val score: Int
                get() = when (invalidDelimiter) {
                    ChunkDelimiter.Parentheses -> 3
                    ChunkDelimiter.Brackets -> 57
                    ChunkDelimiter.Braces -> 1197
                    ChunkDelimiter.Angled -> 25137
                }
        }
    }

    private fun parse(line: String): SyntaxError {
        val stack = ArrayDeque<ChunkDelimiter>()

        for (char in line) {
            when {
                ChunkDelimiter.isOpening(char) -> stack.addFirst(ChunkDelimiter.from(char))
                ChunkDelimiter.isClosing(char) -> {
                    val closingDelimiter = ChunkDelimiter.from(char)
                    if (stack.first() == closingDelimiter) {
                        stack.removeFirst()
                    } else {
                        return SyntaxError.CorruptedLine(closingDelimiter)
                    }
                }
            }
        }

        if (stack.isNotEmpty()) {
            return SyntaxError.IncompleteLine(stack)
        }

        return SyntaxError.None
    }

    fun part1(input: List<String>): Int = input
        .map { parse(it) }
        .filterIsInstance<SyntaxError.CorruptedLine>()
        .sumOf { it.score }

    fun part2(input: List<String>): Long =
        input
            .asSequence()
            .map { parse(it) }
            .filterIsInstance<SyntaxError.IncompleteLine>()
            .map { it.score }
            .sorted()
            .toList()
            .let { it[it.size / 2] }
}

fun main() {
    val testInput = readInputAsLines("Day10_test")
    check(Day10.part1(testInput) == 26397)
    check(Day10.part2(testInput) == 288957L)

    val input = readInputAsLines("Day10")
    println(Day10.part1(input))
    println(Day10.part2(input))
}