AdventOfCode2020/07/main.go
2020-12-11 21:57:10 -05:00

180 lines
4.9 KiB
Go

package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
// Bag is a bag that contains other bags
type Bag struct {
name string
contains map[string]*Bag
containCounts map[string]int
isContainedBy map[string]*Bag
seen bool
}
func parseLine(s string) (string, map[string]int) {
bags := strings.Split(s, " bags contain ")
if bags[1] == "no other bags." {
return bags[0], map[string]int{}
}
contained := strings.Split(bags[1], ", ")
result := map[string]int{}
for _, b := range contained {
// strip off the bags?\.? and split on spaces
innerBag := strings.Split(strings.TrimRight(b, "bags."), " ")
// parse the count
i, _ := strconv.Atoi(innerBag[0])
result[innerBag[1]+" "+innerBag[2]] = i
}
return bags[0], result
}
func countUnseenParents(b *Bag) int {
sum := 0
for _, p := range b.isContainedBy {
if !p.seen {
p.seen = true
sum = sum + countUnseenParents(p) + 1
}
}
return sum
}
func countContainedBags(b *Bag) int {
sum := 0
for n, p := range b.contains {
// make sure you count the bag itself when multiplying
sum = sum + ((countContainedBags(p) + 1) * b.containCounts[n])
}
return sum
}
// You have a shiny gold bag.
// If you wanted to carry it in at least one other bag, how many different bag colors would be valid for the outermost bag?
// (In other words: how many colors can, eventually, contain at least one shiny gold bag?)
func partOne() {
f, _ := os.Open("input")
reader := bufio.NewReader(f)
scanner := bufio.NewScanner(reader)
bags := map[string]*Bag{}
for scanner.Scan() {
line := scanner.Text()
bag, contains := parseLine(line)
// create bag if it doesn't already exist
if _, ok := bags[bag]; !ok {
bags[bag] = &Bag{
name: bag,
isContainedBy: map[string]*Bag{},
contains: map[string]*Bag{},
}
}
// for the bags we now know it can contain
for c := range contains {
// if those bags already exist, update them
if _, ok := bags[c]; ok {
bags[c].isContainedBy[bag] = bags[bag]
} else {
// otherwise, create new bag with pointer to parent
bags[c] = &Bag{
name: c,
isContainedBy: map[string]*Bag{
bag: bags[bag],
},
contains: map[string]*Bag{},
}
}
// finally, update our current bag to point to the one it contains
bags[bag].contains[c] = bags[c]
}
}
// actually do the walk up the tree
fmt.Println(countUnseenParents(bags["shiny gold"]))
}
// Consider again your shiny gold bag and the rules from the above example:
// faded blue bags contain 0 other bags.
// dotted black bags contain 0 other bags.
// vibrant plum bags contain 11 other bags: 5 faded blue bags and 6 dotted black bags.
// dark olive bags contain 7 other bags: 3 faded blue bags and 4 dotted black bags.
// So, a single shiny gold bag must contain 1 dark olive bag (and the 7 bags within it) plus 2 vibrant plum bags (and the 11 bags within each of those): 1 + 1*7 + 2 + 2*11 = 32 bags!
// Of course, the actual rules have a small chance of going several levels deeper than this example;
// be sure to count all of the bags, even if the nesting becomes topologically impractical!
// Here's another example:
// shiny gold bags contain 2 dark red bags.
// dark red bags contain 2 dark orange bags.
// dark orange bags contain 2 dark yellow bags.
// dark yellow bags contain 2 dark green bags.
// dark green bags contain 2 dark blue bags.
// dark blue bags contain 2 dark violet bags.
// dark violet bags contain no other bags.
// In this example, a single shiny gold bag must contain 126 other bags.
func partTwo() {
f, _ := os.Open("input")
reader := bufio.NewReader(f)
scanner := bufio.NewScanner(reader)
bags := map[string]*Bag{}
for scanner.Scan() {
line := scanner.Text()
bag, contains := parseLine(line)
// create bag if it doesn't already exist
if _, ok := bags[bag]; !ok {
bags[bag] = &Bag{
name: bag,
isContainedBy: map[string]*Bag{},
contains: map[string]*Bag{},
containCounts: map[string]int{},
}
}
// for the bags we now know it can contain
for c, i := range contains {
// if those bags already exist, update them
if _, ok := bags[c]; ok {
bags[c].isContainedBy[bag] = bags[bag]
} else {
// otherwise, create new bag with pointer to parent
bags[c] = &Bag{
name: c,
isContainedBy: map[string]*Bag{
bag: bags[bag],
},
contains: map[string]*Bag{},
containCounts: map[string]int{},
}
}
// finally, update our current bag to point to the one it contains
// and how many it must contain of that bag
bags[bag].contains[c] = bags[c]
bags[bag].containCounts[c] = i
}
}
// actually do the walk down the tree
fmt.Println(countContainedBags(bags["shiny gold"]))
}