-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day4.kt
51 lines (43 loc) · 2.5 KB
/
Day4.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
package aoc.puzzles
/**
* @author Kris | 05/12/2021
*/
object Day4 : Puzzle<Bingo, Long>(4) {
private const val DIMENSION = 5
private val RANGE = 0 until DIMENSION
private fun List<List<String>>.getBingoBoards() = map { it.convertToIntList() }.map(::BingoBoard)
private fun List<String>.convertToIntList() = flatMap(String::lines).joinToString(separator = " ").trim().split(Regex("""\s+""")).map(String::toInt)
override fun List<String>.parse(): Bingo {
val list = toMutableList()
list.removeAll(String::isEmpty)
val winningNumbers = list.removeFirst().split(',').map(String::toInt)
val bingoBoards = list.chunked(DIMENSION).getBingoBoards()
return Bingo(winningNumbers.withIndex().associate { it.value to winningNumbers.subList(0, it.index + 1).toSet() }, bingoBoards)
}
override fun Bingo.solvePartOne(): Long = getWinningBoards().first().getScore()
override fun Bingo.solvePartTwo(): Long = getWinningBoards().last().getScore()
private fun Bingo.getWinningBoards(): List<WinningBingoDraw> {
val remainingBoards = boards.toMutableList()
val winningDraws = mutableListOf<WinningBingoDraw>()
for ((drawnNumber, drawnNumbers) in draws) {
remainingBoards.filter { it.hasWon(drawnNumber, drawnNumbers) }.forEach { board ->
remainingBoards -= board
winningDraws += WinningBingoDraw(drawnNumber, board, drawnNumbers)
}
}
require(winningDraws.isNotEmpty())
return winningDraws
}
private fun WinningBingoDraw.getScore(): Long {
val sumOfUnmarkedNumbers = board.sumOf { if (it !in drawnNumbers) it else 0 }.toLong()
return sumOfUnmarkedNumbers * winningNumber
}
private fun BingoBoard.hasWon(drawnNumber: Int, drawnNumbers: Set<Int>): Boolean =
drawnNumber in board && RANGE.any { index -> allInRow(index, drawnNumbers) || allInColumn(index, drawnNumbers) }
private fun BingoBoard.allInRow(row: Int, numbers: Set<Int>) = RANGE.all { col -> this[row, col] in numbers }
private fun BingoBoard.allInColumn(col: Int, numbers: Set<Int>) = RANGE.all { row -> this[row, col] in numbers }
private operator fun BingoBoard.get(row: Int, column: Int): Int = get((row * DIMENSION) + column)
}
data class Bingo(val draws: Map<Int, Set<Int>>, val boards: List<BingoBoard>)
data class BingoBoard(val board: List<Int>) : List<Int> by board
data class WinningBingoDraw(val winningNumber: Int, val board: BingoBoard, val drawnNumbers: Set<Int>)