diff options
author | tchojnacki <tomaszchojnacki2001@gmail.com> | 2021-12-04 13:24:49 +0100 |
---|---|---|
committer | tchojnacki <tomaszchojnacki2001@gmail.com> | 2021-12-04 13:24:49 +0100 |
commit | d38649aa2e9e79f8c5bbabef3e39de38342702e1 (patch) | |
tree | be2354432747a68c4290d675f1f346143df9ac5d /src | |
parent | d5057fc4d578fba728a3ee70d9ba73e84a8a6772 (diff) | |
download | gleam_aoc2020-d38649aa2e9e79f8c5bbabef3e39de38342702e1.tar.gz gleam_aoc2020-d38649aa2e9e79f8c5bbabef3e39de38342702e1.zip |
Complete day 4
Diffstat (limited to 'src')
-rw-r--r-- | src/Day04.kt | 95 | ||||
-rw-r--r-- | src/Utils.kt | 7 |
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 |