Day 7: Handy Haversacks
Puzzle Description
Today was a Haxe day but my solution from 2021 was incompetent, and honestly I'm tired of rewriting all my things, especially when the Scala is so elegant.
Solution Summary
- Parse input into a
Map[String, Set[BagDesc]]
-
Solve
- For
part1
, count the amount of bags that contain the shiny gold bag. - For
part2
, count the amount of bags inside the shiny gold bag.
- For
Part 1
Let's first define our types.
case class BagDesc(color: String, amount: Int)
type Rules = Map[String, Set[BagDesc]]
Let's then parse our input. For some reason the input is "complex" compared to other inputs - it follows some rules of english syntax.
Let's parse it:
def parse(str: String): Rules =
str.linesIterator.map:
case s"$color bags contain $rest." =>
color -> rest.split(',').foldLeft(Set.empty[BagDesc]):
case (acc, r) =>
r.trim match
case s"no other bags" => acc
case s"$n $color2 bag$_" => acc + BagDesc(color2, n.toInt)
.toMap
We have to make sure to only parse the s
sometimes, as it is omitted if n
is 1
. Otherwise the parsing
isn't too bad.
Part 1 is counting the amount of bags that contain the shiny gold bag. A recursive function works fine here:
def part1(input: Rules): Int =
def containsShinyGold(bag: String): Boolean =
val rule = input(bag)
rule.exists(_.color == "shiny gold") || rule.exists(it => containsShinyGold(it.color))
input.removed("shiny gold").count(it => containsShinyGold(it._1))
Part 2
Part 2 is finding the amount of bags inside the shiny gold bag. Another recursive function works here:
def part2(input: Rules): Int =
def getBagAmount(bag: String): Int =
val rule = input(bag)
rule.foldLeft(1):
case (acc, bag) =>
acc + (bag.amount * getBagAmount(bag.color))
getBagAmount("shiny gold") - 1
Each recursion gives at least 1 bag (the current bag). At the end we have to subtract 1 to remove the shiny gold bag from the count.
Final Code
case class BagDesc(color: String, amount: Int)
type Rules = Map[String, Set[BagDesc]]
def parse(str: String): Rules =
str.linesIterator.map:
case s"$color bags contain $rest." =>
color -> rest.split(',').foldLeft(Set.empty[BagDesc]):
case (acc, r) =>
r.trim match
case s"no other bags" => acc
case s"$n $color2 bag$_" => acc + BagDesc(color2, n.toInt)
.toMap
def part1(input: Rules): Int =
def containsShinyGold(bag: String): Boolean =
val rule = input(bag)
rule.exists(_.color == "shiny gold") || rule.exists(it => containsShinyGold(it.color))
input.removed("shiny gold").count(it => containsShinyGold(it._1))
def part2(input: Rules): Int =
def getBagAmount(bag: String): Int =
val rule = input(bag)
rule.foldLeft(1):
case (acc, bag) =>
acc + (bag.amount * getBagAmount(bag.color))
getBagAmount("shiny gold") - 1
Benchmark
Part 1
Mean |
Error |
|
---|---|---|
JVM |
11.984 ms |
+/- 2.288 ms |
JS |
36.119 ms |
+/- 1.036 ms |
Native |
9.816 ms |
+/- 0.010 ms |
Part 2
Mean |
Error |
|
---|---|---|
JVM |
9.417 ms |
+/- 1.338 ms |
JS |
20.582 ms |
+/- 2.881 ms |
Native |
5.117 ms |
+/- 0.208 ms |