Day 2: Cube Conundrum
by @bishabosha
Puzzle description
https://adventofcode.com/2023/day/2
Solution Summary
- Iterate over each line of the input.
- Parse each line into a game
- Summarise each game (using the appropriate summary function for
part1
orpart2
)
part1
requires to check first if any hand in a game (by removing cubes) will cause a negative cube count, compared to the initial configuration of "possible" cubes. If there are no negative counts, then the game is possible and summarise as the game's id, otherwise summarise as zero.part2
requires to find the maximum cube count of each color in any given hand, and then summarise as the product of those cube counts.
- Sum the total of summaries
Part 1
Framework
The main driver for solving will be the solution
function.
In a single pass over the puzzle input
it will:
- iterate through each line,
parse
each line into a game,summarise
each game as anInt
,sum
the total of summaries.
case class Colors(color: String, count: Int)
case class Game(id: Int, hands: List[List[Colors]])
type Summary = Game => Int
def solution(input: String, summarise: Summary): Int =
input.linesIterator.map(parse andThen summarise).sum
def parse(line: String): Game = ???
part1
and part2
will use this framework, plugging in the appropriate summarise
function.
Parsing
Let's fill in the parse
function as follows:
def parseColors(pair: String): Colors =
val (s"$value $name") = pair: @unchecked
Colors(color = name, count = value.toInt)
def parse(line: String): Game =
val (s"Game $id: $hands0") = line: @unchecked
val hands1 = hands0.split("; ").toList
val hands2 = hands1.map(_.split(", ").toList.map(parseColors))
Game(id = id.toInt, hands = hands2)
Summary
As described above, to summarise each game, we evaluate it as a possibleGame
, where if it is a validGame
summarise as the game's id
, otherwise 0
.
A game is valid if for all hands
in the game, all the colors in each hand has a count
that is less-than or equal-to the count of same color from the possibleCubes
configuration.
val possibleCubes = Map(
"red" -> 12,
"green" -> 13,
"blue" -> 14,
)
def validGame(game: Game): Boolean =
game.hands.forall: hand =>
hand.forall:
case Colors(color, count) =>
count <= possibleCubes.getOrElse(color, 0)
val possibleGame: Summary =
case game if validGame(game) => game.id
case _ => 0
def part1(input: String): Int = solution(input, possibleGame)
Part 2
Summary
In part 2, the summary of a game requires us to find the minimumCubes
necessary to make a possible game.
What this means is for any given game, across all hands calculating the maximum cubes drawn for each color.
In Scala we can accumulate the maximum counts for each cube in a Map
from color to count.
Take the initial maximums as all zero:
val initial = Seq("red", "green", "blue").map(_ -> 0).toMap
Then for each game we can compute the maximum cubes drawn in each game as follows
def minimumCubes(game: Game): Int =
var maximums = initial
for
hand <- game.hands
Colors(color, count) <- hand
do
maximums += (color -> (maximums(color) `max` count))
maximums.values.product
Finally we can complete the solution by using minimumCubes
to summarise each game:
def part2(input: String): Int = solution(input, minimumCubes)
Final Code
case class Colors(color: String, count: Int)
case class Game(id: Int, hands: List[List[Colors]])
type Summary = Game => Int
def parseColors(pair: String): Colors =
val (s"$value $name") = pair: @unchecked
Colors(color = name, count = value.toInt)
def parse(line: String): Game =
val (s"Game $id: $hands0") = line: @unchecked
val hands1 = hands0.split("; ").toList
val hands2 = hands1.map(_.split(", ").toList.map(parseColors))
Game(id = id.toInt, hands = hands2)
def solution(input: String, summarise: Summary): Int =
input.linesIterator.map(parse andThen summarise).sum
val possibleCubes = Map(
"red" -> 12,
"green" -> 13,
"blue" -> 14,
)
def validGame(game: Game): Boolean =
game.hands.forall: hand =>
hand.forall:
case Colors(color, count) =>
count <= possibleCubes.getOrElse(color, 0)
val possibleGame: Summary =
case game if validGame(game) => game.id
case _ => 0
def part1(input: String): Int = solution(input, possibleGame)
val initial = Seq("red", "green", "blue").map(_ -> 0).toMap
def minimumCubes(game: Game): Int =
var maximums = initial
for
hand <- game.hands
Colors(color, count) <- hand
do
maximums += (color -> (maximums(color) `max` count))
maximums.values.product
def part2(input: String): Int = solution(input, minimumCubes)
Run it in the browser
Part 1
Part 2
Solutions from the community
- Solution by Alexandru Nedelcu
- Solution by Seth Tisue
- Solution by CJ Smith
- Solution by Niels Prins
- Solution by Philippus Baalman
- Solution by Karl Bielefeldt
- Solution by Vail Markoukin
- Solution by jnclt
- Solution by Spamegg
- Solution by Yann Moisan
- Solution by Guillaume Vandecasteele
- Solution by Karthick Pachiappan
- Solution by Chidi Nweke
- Solution of Jan Boerman.
- Solution of Brian Xiang.
- Solution of Raymond Dodge.
- Solution of Joel Edwards
- Solution by Will Billingsley
- Solution by Michael Pilquist
- Solution by g.berezin
- Solution by Marconi Lanna
- Solution by Rui Alves
- Solution by Paweł Cembaluk
Share your solution to the Scala community by editing this page.