152 lines
3.3 KiB
Go
152 lines
3.3 KiB
Go
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")))
|
|
}
|