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")))
							 | 
						||
| 
								 | 
							
								}
							 |