2024-03-11 22:55:13 +00:00
|
|
|
package solutions
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Bag struct {
|
|
|
|
color string
|
|
|
|
contents []BagContent
|
|
|
|
containsShinyGold bool
|
2024-03-11 23:20:15 +00:00
|
|
|
contentsCount int
|
2024-03-11 22:55:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type BagContent struct {
|
|
|
|
count int
|
|
|
|
color string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given a bag statement string, returns its contents
|
|
|
|
func parseBagStatement(bagStatement string) Bag {
|
|
|
|
bagEndIndex := strings.Index(bagStatement, " bags contain ")
|
|
|
|
bagColor := bagStatement[:bagEndIndex]
|
|
|
|
bagContentsStr := bagStatement[bagEndIndex+14:]
|
|
|
|
|
|
|
|
// if no aditional bags
|
|
|
|
if bagContentsStr[:2] == "no" {
|
2024-03-11 23:20:15 +00:00
|
|
|
return Bag{color: bagColor, contents: make([]BagContent, 0), contentsCount: -1}
|
2024-03-11 22:55:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// parse remainder bags
|
|
|
|
bagsContained := strings.Split(bagContentsStr, ", ")
|
|
|
|
bagContents := make([]BagContent, len(bagsContained))
|
|
|
|
|
|
|
|
for idx, bag := range bagsContained {
|
|
|
|
bagCount, err := strconv.Atoi(bag[:1])
|
|
|
|
if err != nil {
|
|
|
|
panic("expected a number, got `" + bag + "`")
|
|
|
|
}
|
|
|
|
|
|
|
|
bagStrIndex := strings.Index(bag, " bag")
|
|
|
|
bagColor2 := bag[2:bagStrIndex]
|
|
|
|
|
|
|
|
bagContents[idx] = BagContent{count: bagCount, color: bagColor2}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
grammar for bag statements:
|
|
|
|
|
|
|
|
statement = bag color, "bags contain", (bag list | empty bag), "."
|
|
|
|
empty bag = "no other bags"
|
|
|
|
bag list = bag declaration, bag declaration+
|
|
|
|
bag declaration = number, bag color, "bag", "s"?
|
|
|
|
bag color = word, word
|
|
|
|
*/
|
|
|
|
|
2024-03-11 23:20:15 +00:00
|
|
|
return Bag{color: bagColor, contents: bagContents, contentsCount: -1}
|
2024-03-11 22:55:13 +00:00
|
|
|
}
|
|
|
|
|
2024-03-11 23:40:19 +00:00
|
|
|
func bagContainsShinyGold(bagColor string, bagMap map[string]Bag) bool {
|
|
|
|
bag := bagMap[bagColor]
|
2024-03-11 22:55:13 +00:00
|
|
|
if bag.containsShinyGold {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// recursively search
|
2024-03-11 23:40:19 +00:00
|
|
|
for _, nextBag := range bag.contents {
|
|
|
|
if nextBag.color == "shiny gold" {
|
2024-03-11 22:55:13 +00:00
|
|
|
bag.containsShinyGold = true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-03-11 23:40:19 +00:00
|
|
|
if bagContainsShinyGold(nextBag.color, bagMap) {
|
2024-03-11 22:55:13 +00:00
|
|
|
bag.containsShinyGold = true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func Day07Part01(isTest bool) int {
|
|
|
|
input := ReadInput("07", isTest)
|
|
|
|
groups := strings.Split(input, "\n")
|
|
|
|
|
|
|
|
bags := make(map[string]Bag)
|
2024-03-11 23:40:19 +00:00
|
|
|
bagColors := make([]string, len(groups))
|
2024-03-11 22:55:13 +00:00
|
|
|
|
|
|
|
// parse and collect the bags
|
2024-03-11 23:40:19 +00:00
|
|
|
for i, statement := range groups {
|
2024-03-11 22:55:13 +00:00
|
|
|
parsedBag := parseBagStatement(statement)
|
|
|
|
bags[parsedBag.color] = parsedBag
|
2024-03-11 23:40:19 +00:00
|
|
|
bagColors[i] = parsedBag.color
|
2024-03-11 22:55:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
shinyGoldContainers := 0
|
|
|
|
|
|
|
|
// process the bags
|
2024-03-11 23:40:19 +00:00
|
|
|
for _, bagColor := range bagColors {
|
|
|
|
if bagContainsShinyGold(bagColor, bags) {
|
2024-03-11 22:55:13 +00:00
|
|
|
shinyGoldContainers += 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return shinyGoldContainers
|
|
|
|
}
|
|
|
|
|
2024-03-11 23:20:15 +00:00
|
|
|
func countInnerBags(bag *Bag, bagMap map[string]Bag) int {
|
|
|
|
if bag.contentsCount != -1 {
|
|
|
|
return bag.contentsCount
|
|
|
|
}
|
|
|
|
|
|
|
|
innerBagsCount := 0
|
|
|
|
|
|
|
|
// recursively count bags
|
|
|
|
for _, nextBagStruct := range bag.contents {
|
|
|
|
nextBagCount := nextBagStruct.count
|
|
|
|
nextBag := bagMap[nextBagStruct.color]
|
|
|
|
|
|
|
|
innerBagsCount += nextBagCount + nextBagCount*countInnerBags(&nextBag, bagMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
return innerBagsCount
|
|
|
|
}
|
|
|
|
|
2024-03-11 22:55:13 +00:00
|
|
|
func Day07Part02(isTest bool) int {
|
2024-03-11 23:20:15 +00:00
|
|
|
input := ReadInput("07", isTest)
|
|
|
|
groups := strings.Split(input, "\n")
|
|
|
|
|
|
|
|
bags := make(map[string]Bag)
|
|
|
|
|
|
|
|
// parse and collect the bags
|
|
|
|
for _, statement := range groups {
|
|
|
|
parsedBag := parseBagStatement(statement)
|
|
|
|
bags[parsedBag.color] = parsedBag
|
|
|
|
}
|
|
|
|
|
|
|
|
shinyGoldBag := bags["shiny gold"]
|
|
|
|
|
|
|
|
return countInnerBags(&shinyGoldBag, bags)
|
2024-03-11 22:55:13 +00:00
|
|
|
}
|