generated from kotlin-hands-on/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Task.kt
113 lines (93 loc) · 3.41 KB
/
Task.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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package day04
import readInput
fun main() {
val input = readInput("day04")
println(solvePartTwo(input))
println(solvePartTwo(input))
}
fun solvePartOne(input: List<String>): Int {
val winners = calculateBingoWinners(input)
return winners.first().sum
}
fun solvePartTwo(input: List<String>): Int {
val winners = calculateBingoWinners(input)
return winners.last().sum
}
private fun calculateBingoWinners(input: List<String>): List<BingoWinner> {
val bingoInputs = input.bingoInputList()
val boards = buildBoards(input)
val indexes = buildIndexes(boards)
return buildList<BingoWinner> {
for (numberString in bingoInputs) {
val number = numberString.toInt()
val boardIndexes = indexes[number]
if (boardIndexes != null) {
for (boardIndex in boardIndexes) {
if (boardIndex.markAndCheck() && none { (index) -> index == boardIndex.board.index }) {
val index = boardIndex.board.index
val sum = boardIndex.board.calculateUnmarkedSum() * number
add(BingoWinner(index, sum))
}
}
}
}
}
}
private fun buildBoards(input: List<String>) = buildList {
val boardsInputOnly = input.drop(1)
val tempMatrix = mutableListOf<List<BingoNumber>>()
boardsInputOnly.forEachIndexed { index, value ->
val inputs = value.replace(' ', ',')
.split(',')
.filter { member -> member.isNotEmpty() }
.map { member -> member.toInt() }
if (inputs.isNotEmpty()) {
tempMatrix += inputs.map { intValue -> BingoNumber(intValue, false) }
}
if ((inputs.isEmpty() || index == boardsInputOnly.lastIndex) && tempMatrix.isNotEmpty()) {
val card = tempMatrix.toList()
tempMatrix.clear()
add(card)
}
}
}.mapIndexed { index, numbers -> BingoBoard(index, numbers) }
private fun buildIndexes(boards: List<BingoBoard>): Map<Int, List<BingoIndex>> = buildMap {
boards.forEach { board ->
board.numbers.forEachIndexed { y, row ->
row.forEachIndexed { x, (intValue) ->
val indexList = getOrPut(intValue) { emptyList() }
this[intValue] = indexList + BingoIndex(board, x, y)
}
}
}
}
private fun List<String>.bingoInputList() = first().splitToSequence(",")
private typealias BingoNumbers = List<List<BingoNumber>>
private data class BingoBoard(
val index: Int,
val numbers: BingoNumbers,
)
private fun BingoBoard.markAndCheck(x: Int, y: Int): Boolean {
get(x, y).marked = true
return checkRow(y) || checkColumn(x)
}
private fun BingoBoard.get(x: Int, y: Int) = numbers[y][x]
private fun BingoBoard.checkRow(y: Int): Boolean = numbers[y].all { number -> number.marked }
private fun BingoBoard.checkColumn(x: Int): Boolean = numbers.map { it[x] }.all { number -> number.marked }
private fun BingoBoard.calculateUnmarkedSum(): Int = numbers.sumOf { row ->
row.sumOf { number -> number.value.takeIf { !number.marked } ?: 0 }
}
private data class BingoIndex(
val board: BingoBoard,
val x: Int,
val y: Int,
)
private data class BingoWinner(
val index: Int,
val sum: Int,
)
private fun BingoIndex.markAndCheck(): Boolean = board.markAndCheck(x, y)
private data class BingoNumber(
val value: Int,
var marked: Boolean,
)