75 lines
2 KiB
Scala
75 lines
2 KiB
Scala
|
|
import scala.io.Source
|
||
|
|
|
||
|
|
object Day4 extends App:
|
||
|
|
|
||
|
|
val input = Source
|
||
|
|
.fromURL(getClass.getResource("day4Input.txt"))
|
||
|
|
.mkString
|
||
|
|
|
||
|
|
final case class Bingo(boards: Array[Board], playedNumbers: Seq[Int]):
|
||
|
|
def playNumber(number: Int) =
|
||
|
|
Bingo(
|
||
|
|
boards.map(board => if (board.isWinner) board else board.mark(number)),
|
||
|
|
playedNumbers.appended(number)
|
||
|
|
)
|
||
|
|
|
||
|
|
def winningBoards: Seq[Board] = boards
|
||
|
|
.filter(_.isWinner)
|
||
|
|
.map(board => (board, board.lastMarked.map(playedNumbers.indexOf(_))))
|
||
|
|
.sortBy(_._2)
|
||
|
|
.map(_._1)
|
||
|
|
|
||
|
|
val gameOver = boards.forall(_.isWinner)
|
||
|
|
|
||
|
|
object Bingo:
|
||
|
|
def parse(input: Array[String]): Bingo =
|
||
|
|
Bingo(input.map(Board.parse), Seq())
|
||
|
|
|
||
|
|
final case class Board(
|
||
|
|
val rows: Array[Array[Cell]],
|
||
|
|
lastMarked: Option[Int] = None
|
||
|
|
):
|
||
|
|
val isWinner: Boolean = rows.exists(_.forall(_.marked)) ||
|
||
|
|
rows.transpose.exists(_.forall(_.marked))
|
||
|
|
|
||
|
|
def mark(number: Int): Board =
|
||
|
|
Board(
|
||
|
|
rows.map(_.map(_.mark(number))),
|
||
|
|
Some(number)
|
||
|
|
)
|
||
|
|
|
||
|
|
def score: Int =
|
||
|
|
rows.flatMap(_.filterNot(_.marked).map(_.value)).sum *
|
||
|
|
lastMarked.getOrElse(0)
|
||
|
|
|
||
|
|
object Board:
|
||
|
|
def parse(input: String) =
|
||
|
|
Board(
|
||
|
|
input
|
||
|
|
.split('\n')
|
||
|
|
.map(
|
||
|
|
_.split(' ')
|
||
|
|
.filter(_.nonEmpty)
|
||
|
|
.map(Cell.parse)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
final case class Cell(value: Int, marked: Boolean = false):
|
||
|
|
def mark(number: Int): Cell =
|
||
|
|
if (number == value) Cell(value, true) else this
|
||
|
|
|
||
|
|
object Cell:
|
||
|
|
def parse(input: String) = Cell(input.toInt)
|
||
|
|
|
||
|
|
val numbersAndBoardsInput = input.split("\n\n")
|
||
|
|
val drawnNumbers = numbersAndBoardsInput.head.split(',').map(_.toInt)
|
||
|
|
val initialState = Bingo.parse(numbersAndBoardsInput.tail)
|
||
|
|
|
||
|
|
val finalState = drawnNumbers.foldLeft(initialState)(_.playNumber(_))
|
||
|
|
|
||
|
|
val firstWinningBoard = finalState.winningBoards.head
|
||
|
|
println(s"Part 1: ${firstWinningBoard.score}")
|
||
|
|
|
||
|
|
val lastWinningBoard = finalState.winningBoards.last
|
||
|
|
println(s"Part 2: ${lastWinningBoard.score}")
|