aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Day04.kt95
-rw-r--r--src/Utils.kt7
2 files changed, 102 insertions, 0 deletions
diff --git a/src/Day04.kt b/src/Day04.kt
new file mode 100644
index 0000000..6db98dd
--- /dev/null
+++ b/src/Day04.kt
@@ -0,0 +1,95 @@
+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 { it.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)
+ }
+ }
+ }
+ }
+
+ 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) { racc, row ->
+ racc + row.fold(0) { sacc, square ->
+ sacc + if (square is Square.Unmarked) square.number else 0
+ }
+ }
+
+ sealed class Square {
+ object Marked : Square()
+ class Unmarked(val number: Int) : Square()
+ }
+ }
+}
+
+fun main() {
+ fun part1(input: String): Int = Bingo.fromString(input).getResults().first()
+
+ fun part2(input: String): Int = Bingo.fromString(input).getResults().last()
+
+ val testInput = readInputAsString("Day04_test")
+ check(part1(testInput) == 4512)
+ check(part2(testInput) == 1924)
+
+ val input = readInputAsString("Day04")
+ println(part1(input))
+ println(part2(input))
+}
diff --git a/src/Utils.kt b/src/Utils.kt
index 27b88b5..6268ba6 100644
--- a/src/Utils.kt
+++ b/src/Utils.kt
@@ -10,6 +10,13 @@ import java.security.MessageDigest
fun readInputAsLines(name: String): List<String> = File("src", "$name.txt").readLines()
/**
+ * Returns a string of contents of the given input txt file.
+ * @param name name of the file
+ * @return contents of file as string
+ */
+fun readInputAsString(name: String): String = File("src", "$name.txt").readText()
+
+/**
* Read lines from the given input txt file and convert them to decimal numbers.
* @param name name of the file
* @return list of ints containing numbers from each of file's lines