aboutsummaryrefslogtreecommitdiff
path: root/aoc-2021-kotlin/src/Day04.kt
diff options
context:
space:
mode:
authortchojnacki <tomaszchojnacki2001@gmail.com>2022-08-11 19:24:23 +0200
committertchojnacki <tomaszchojnacki2001@gmail.com>2022-08-11 19:24:23 +0200
commit0f1e145b80813ae2331b7dac5ace0c589654ad2a (patch)
tree25483b8239436dd5aed2fee8811caf0ba893c0bb /aoc-2021-kotlin/src/Day04.kt
parent85fb0396bed6a2129b12392941103924b1ab55be (diff)
downloadgleam_aoc2020-0f1e145b80813ae2331b7dac5ace0c589654ad2a.tar.gz
gleam_aoc2020-0f1e145b80813ae2331b7dac5ace0c589654ad2a.zip
Move subproject to avoid IntelliJ module name issues
Diffstat (limited to 'aoc-2021-kotlin/src/Day04.kt')
-rw-r--r--aoc-2021-kotlin/src/Day04.kt97
1 files changed, 97 insertions, 0 deletions
diff --git a/aoc-2021-kotlin/src/Day04.kt b/aoc-2021-kotlin/src/Day04.kt
new file mode 100644
index 0000000..96cdf3b
--- /dev/null
+++ b/aoc-2021-kotlin/src/Day04.kt
@@ -0,0 +1,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)
+}