aboutsummaryrefslogtreecommitdiff
path: root/src/Day10.kt
blob: 99850141de3f65a360b0b9d16a8746feba0a619c (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
enum class ChunkDelimeter(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): ChunkDelimeter = values().find { it.open == character || it.close == character }!!
    }
}

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

                return stack.fold(0) { acc, elem -> acc * 5 + delimeterValue(elem) }
            }
    }
    class CorruptedLine(private val invalidDelimeter: ChunkDelimeter) : SyntaxError() {
        val score: Int
            get() = when (invalidDelimeter) {
                ChunkDelimeter.Parentheses -> 3
                ChunkDelimeter.Brackets -> 57
                ChunkDelimeter.Braces -> 1197
                ChunkDelimeter.Angled -> 25137
            }
    }
}

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

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

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

    return SyntaxError.None
}

fun main() {
    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] }


    val testInput = readInputAsLines("Day10_test")
    check(part1(testInput) == 26397)
    check(part2(testInput) == 288957L)

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