generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Y2021D04.kt
80 lines (68 loc) · 3.02 KB
/
Y2021D04.kt
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
package aockt.y2021
import io.github.jadarma.aockt.core.Solution
object Y2021D04 : Solution {
/**
* A 5x5 BingoBoard.
* @constructor Sets up a new bingo board with these 25 numbers, from left to right, top to bottom.
*/
private class BingoBoard(numbers: List<Int>) {
private val grid: List<BingoSquare> = numbers.map { BingoSquare(it, false) }
init {
require(numbers.size == 25) { "A bingo board has exactly 25 numbers on it." }
}
/** Simple holder for a square and its state. */
private class BingoSquare(val number: Int, var isSet: Boolean = false)
/** If the [number] appears on the board, marks it, and returns if the board is now in BINGO! */
fun callAndCheck(number: Int): Boolean {
grid.firstOrNull { it.number == number }?.apply { isSet = true }
return isBingo()
}
/** Checks if the board is in BINGO! */
fun isBingo(): Boolean {
for (x in 0..4) {
var horizontalBingo = 0
var verticalBingo = 0
for (y in 0..4) {
if (grid[x * 5 + y].isSet) horizontalBingo++
if (grid[y * 5 + x].isSet) verticalBingo++
}
if (horizontalBingo == 5 || verticalBingo == 5) return true
}
return false
}
/** Returns the sum of all the numbers that have not been marked on this board. */
fun sumOfUnsetNumbers(): Int = grid.filterNot { it.isSet }.sumOf { it.number }
}
/** Parses the input and returns the list of numbers to be played and all the available [BingoBoard]s. */
private fun parseInput(input: String): Pair<List<Int>, List<BingoBoard>> = runCatching {
val lines = input.lines().filterNot { it.isBlank() }
val numbers = lines.first().split(",").map { it.toInt() }
val bingoBoards = lines
.drop(1).chunked(5)
.map { rows -> rows.flatMap { row -> row.trim().split(Regex("""\s+""")).map(String::toInt) } }
.map(::BingoBoard)
numbers to bingoBoards
}.getOrElse { throw IllegalArgumentException("Invalid input.") }
override fun partOne(input: String): Any {
val (numbers, boards) = parseInput(input)
numbers.forEach { number ->
val bingo = boards.firstOrNull { it.callAndCheck(number) }
if (bingo != null) {
return bingo.sumOfUnsetNumbers() * number
}
}
error("No winning board found.")
}
override fun partTwo(input: String): Any {
val (numbers, boards) = parseInput(input)
val candidates = boards.toMutableList()
numbers.forEach { number ->
while (true) {
val board = candidates.firstOrNull { it.callAndCheck(number) } ?: break
candidates.remove(board)
if (candidates.isEmpty()) return board.sumOfUnsetNumbers() * number
}
}
error("There is a tie for which board wins last.")
}
}