1
+ package advent2021
2
+
3
+ typealias BingoBoard = List <List <Int >>
4
+
5
+ class Day04 (input : List <String >) {
6
+
7
+ private val draws: List <Int > = input.first().split(" ," ).map { it.toInt() }
8
+ private val boards: Set <BingoBoard > = parseInput(input)
9
+
10
+ fun solvePart1 (): Int {
11
+ val drawn = draws.take(4 ).toMutableSet()
12
+ return draws.drop(4 ).firstNotNullOf { draw ->
13
+ drawn + = draw
14
+ boards.firstOrNull { it.isWinner(drawn) }?.let { winner ->
15
+ draw * winner.sumUnmarked(drawn)
16
+ }
17
+ }
18
+ }
19
+
20
+ fun solvePart2 (): Int {
21
+ val drawn = draws.toMutableSet()
22
+ return draws.reversed().firstNotNullOf { draw ->
23
+ drawn - = draw
24
+ boards.firstOrNull { ! it.isWinner(drawn) }?.let { winner ->
25
+ draw * (winner.sumUnmarked(drawn) - draw)
26
+ }
27
+ }
28
+ }
29
+
30
+ private fun BingoBoard.isWinner (draws : Set <Int >) =
31
+ this .any { row -> row.all { it in draws } } ||
32
+ (0 .. 4 ).any { col -> this .all { row -> row[col] in draws } }
33
+
34
+ private fun BingoBoard.sumUnmarked (draws : Set <Int >): Int =
35
+ this .sumOf { row ->
36
+ row.filterNot { it in draws }.sum()
37
+ }
38
+
39
+ private fun parseInput (input : List <String >): Set <BingoBoard > =
40
+ input
41
+ .asSequence()
42
+ .drop(1 )
43
+ .filter { it.isNotEmpty() }
44
+ .chunked(5 )
45
+ .map { parseSingleBoard(it) }
46
+ .toSet()
47
+
48
+ private fun parseSingleBoard (input : List <String >): BingoBoard =
49
+ input.map { row ->
50
+ row.split(" " ).filter { it.isNotEmpty() }.map { it.toInt() }
51
+ }
52
+ }
53
+
54
+ fun main () {
55
+ val input = Resources .resourceAsListOfString(" advent2021/day04.txt" )
56
+ val day = Day04 (input = input)
57
+ println (" Part 1: ${day.solvePart1()} " )
58
+ println (" Part 2: ${day.solvePart2()} " )
59
+ }
0 commit comments