package main
import (
"bufio"
"log"
"os"
"regexp"
"strconv"
"strings"
)
const myBag = "shiny gold"
var (
reContainer = regexp.MustCompile(`^\w+ \w+`)
reContains = regexp.MustCompile(`\d+ \w+ \w+`)
)
type Container map[string][]Contains
type Contains struct {
piece int
kind string
}
func main() {
if err := realMain(); err != nil {
log.Println(err)
}
}
func realMain() error {
data, err := parseInput()
if err != nil {
return err
}
log.Printf("%d bags could contain %q", solveFirst(data), myBag)
log.Printf("%d bags could be tucked in a %q", solveSecond(data, myBag), myBag)
return nil
}
func solveSecond(data Container, root string) int {
var sum int
for _, c := range data[root] {
sum += c.piece + c.piece*solveSecond(data, c.kind)
}
return sum
}
func solveFirst(data Container) int {
var direct []string
for d := range data {
for _, c := range data[d] {
if c.kind == myBag && c.piece > 0 {
direct = append(direct, d)
}
}
}
indirect := map[string]bool{}
for _, dr := range direct {
indirect[dr] = true
}
iteration := 0
for {
iteration++
size := len(indirect)
for indr := range indirect {
for d := range data {
for _, c := range data[d] {
if indr == c.kind && c.piece > 0 {
indirect[d] = true
}
}
}
}
if len(indirect) > size {
continue
}
break
}
return len(indirect)
}
func parseInput() (Container, error) {
fd, err := os.Open("input.txt")
if err != nil {
return nil, err
}
defer fd.Close()
ret := Container{}
buf := bufio.NewScanner(fd)
for buf.Scan() {
childs := reContains.FindAllString(buf.Text(), -1)
if len(childs) == 0 {
continue
}
root := reContainer.FindString(buf.Text())
ret[root] = []Contains{}
for _, c := range childs {
qs := strings.Split(c, " ")
q, err := strconv.Atoi(qs[0])
if err != nil {
return nil, err
}
c := Contains{}
c.piece = q
c.kind = qs[1] + " " + qs[2]
ret[root] = append(ret[root], c)
}
}
return ret, nil
}