blob: 96cdf3b12c55c7ea092958f2d1816a4c071c21aa (
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
92
93
94
95
96
97
|
object Day04 {
private class Bingo(private val revealQueue: ArrayDeque<Int>, private val boards: List<Board>) {
companion object {
fun fromString(input: String): Bingo {
val sections = input.trim().split("(\\r?\\n){2}".toRegex())
val revealQueueString = sections.first()
val revealQueue = ArrayDeque(revealQueueString.split(",").map(String::toInt))
val boards = sections.drop(1).map { Board.fromMatrixString(it) }
return Bingo(revealQueue, boards)
}
}
fun getResults() = sequence {
while (revealQueue.isNotEmpty() && boards.any { !it.didWin }) {
val number = revealQueue.removeFirst()
for (board in boards.filter { !it.didWin }) {
board.reveal(number)
if (board.didWin) {
yield(board.sumOfUnmarked * number)
}
}
}
}
private class Board(private var rows: List<List<Square>>) {
companion object {
fun fromMatrixString(matrixString: String): Board = Board(
matrixString.trim().split("\\r?\\n".toRegex()).map { rowString ->
rowString.trim().split("\\s+".toRegex()).map { squareString ->
Square.Unmarked(squareString.toInt())
}
}
)
private const val SIZE = 5
private val ROWS = (0 until SIZE).map { row ->
(0 until SIZE).map { square ->
Pair(row, square)
}
}
private val COLUMNS = (0 until SIZE).map { column ->
(0 until SIZE).map { square ->
Pair(square, column)
}
}
private val WINNING_CONFIGURATIONS = ROWS + COLUMNS
}
fun reveal(number: Int) {
rows = rows.map { row ->
row.map { square ->
if (square is Square.Unmarked && square.number == number) Square.Marked else square
}
}
}
val didWin: Boolean
get() = WINNING_CONFIGURATIONS.any { configuration ->
configuration.all { (row, column) -> rows[row][column] is Square.Marked }
}
val sumOfUnmarked: Int
get() = rows.fold(0) { rowAcc, row ->
rowAcc + row.fold(0) { squareAcc, square ->
squareAcc + if (square is Square.Unmarked) square.number else 0
}
}
sealed class Square {
object Marked : Square()
class Unmarked(val number: Int) : Square()
}
}
}
fun bothParts(input: String) = Bingo.fromString(input).getResults().let { it.first() to it.last() }
}
fun main() {
val testInput = readInputAsString("Day04_test")
val testOutput = Day04.bothParts(testInput)
check(testOutput.first == 4512)
check(testOutput.second == 1924)
val input = readInputAsString("Day04")
val output = Day04.bothParts(input)
println(output.first)
println(output.second)
}
|