aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortchojnacki <tomaszchojnacki2001@gmail.com>2021-12-04 13:24:49 +0100
committertchojnacki <tomaszchojnacki2001@gmail.com>2021-12-04 13:24:49 +0100
commitd38649aa2e9e79f8c5bbabef3e39de38342702e1 (patch)
treebe2354432747a68c4290d675f1f346143df9ac5d /src
parentd5057fc4d578fba728a3ee70d9ba73e84a8a6772 (diff)
downloadgleam_aoc2020-d38649aa2e9e79f8c5bbabef3e39de38342702e1.tar.gz
gleam_aoc2020-d38649aa2e9e79f8c5bbabef3e39de38342702e1.zip
Complete day 4
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