adventofcode-2020/day7/part1/main.go

152 lines
3.3 KiB
Go
Raw Normal View History

2020-12-07 08:10:50 +00:00
package main
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"sort"
"strconv"
"strings"
)
type BagRegistry struct {
knownBags map[string]*Bag
}
func (registry *BagRegistry) Add(bag *Bag) {
if _, ok := registry.knownBags[bag.Color]; ok {
panic("Bag of that color was already registered")
}
registry.knownBags[bag.Color] = bag
}
func (registry *BagRegistry) GetBagByColor(color string) (bag *Bag, ok bool) {
bag, ok = registry.knownBags[color]
return
}
func (registry *BagRegistry) GetAllColors() []string {
colors := []string{}
for color := range registry.knownBags {
colors = append(colors, color)
}
sort.Strings(colors)
return colors
}
func (registry *BagRegistry) GetColorsThatContain(innerColor string) (result []string) {
result = []string{}
for _, color := range registry.GetAllColors() {
bag := registry.knownBags[color]
if bag.GetMinimumAmountOf(innerColor, registry) > 0 {
result = append(result, color)
}
}
return
}
type Bag struct {
Color string
Rules []*BagRule
}
func (bag *Bag) GetMinimumAmountOf(innerBagColor string, registry *BagRegistry) int {
minAmount := 0
for _, rule := range bag.Rules {
if rule.Bag == innerBagColor {
if minAmount == 0 || minAmount > rule.Amount {
minAmount = rule.Amount
}
}
innerBag, ok := registry.GetBagByColor(rule.Bag)
if !ok {
panic("Invalid rule, bag of this color not found")
}
innerAmount := innerBag.GetMinimumAmountOf(innerBagColor, registry)
if innerAmount > 0 && (minAmount == 0 || minAmount > innerAmount) {
minAmount = innerAmount
}
}
return minAmount
}
type BagRule struct {
Bag string
Amount int
}
var rxBagDescription = regexp.MustCompile(`^(.+) bags contain (.+)\.?$`)
var rxBagRule = regexp.MustCompile(`^(\d+) (.+) bags?$`)
func ParseRule(line string) *Bag {
substrings := rxBagDescription.FindStringSubmatch(line)
if substrings == nil {
panic("Bag rule can not be parsed")
}
substrings[2] = strings.TrimRight(substrings[2], "., ")
containedBags := strings.Split(substrings[2], ", ")
bag := &Bag{
Color: substrings[1],
Rules: []*BagRule{},
}
// Does rule only say no other bags inside?
// Example: "light red bags contain no other bags"
if len(containedBags) == 1 && containedBags[0] == "no other bags" {
return bag
}
// Can contain other bags.
// Example: "light red bags contain 1 bright white bag, 2 muted yellow bags."
for _, containedBag := range containedBags {
substrings = rxBagRule.FindStringSubmatch(containedBag)
if substrings == nil {
panic("Could not parse description: " + containedBag)
}
containedBagAmount, err := strconv.Atoi(substrings[1])
if err != nil {
panic("Could not parse number from contained bag rule")
}
containedBagColor := substrings[2]
bag.Rules = append(bag.Rules, &BagRule{
Bag: containedBagColor,
Amount: containedBagAmount,
})
}
return bag
}
func main() {
f, err := os.Open("input")
if err != nil {
panic(err)
}
r := bufio.NewReader(f)
registry := &BagRegistry{
knownBags: map[string]*Bag{},
}
for {
line, err := r.ReadString('\n')
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
line = strings.TrimSpace(line)
bag := ParseRule(line)
registry.Add(bag)
}
// Find out how many bags can contain a shiny gold bag
fmt.Printf("%d bags can contain shiny gold bag\n", len(registry.GetColorsThatContain("shiny gold")))
}