2019-12-15 14:57:44 +00:00
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2019-12-18 19:23:26 +00:00
|
|
|
|
"math"
|
2019-12-15 14:57:44 +00:00
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
helpers "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
inp := helpers.StdinToStringSlice()
|
|
|
|
|
part1(inp)
|
|
|
|
|
part2(inp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var allReactions map[string]*Reaction
|
|
|
|
|
|
|
|
|
|
func part1(inp []string) {
|
|
|
|
|
allReactions = make(map[string]*Reaction)
|
|
|
|
|
for _, v := range inp {
|
|
|
|
|
r := NewReaction(v)
|
|
|
|
|
allReactions[r.out] = r
|
|
|
|
|
}
|
|
|
|
|
factory := NewFactory(allReactions)
|
|
|
|
|
factory.setInfinite("ORE", true)
|
|
|
|
|
factory.generate("FUEL")
|
|
|
|
|
fmt.Println("# Part 1")
|
|
|
|
|
fmt.Println("Ore Used:", factory.generated["ORE"])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func part2(inp []string) {
|
2019-12-18 19:23:26 +00:00
|
|
|
|
fuel := GetMaxFuelForOre(inp, 1000000000000)
|
|
|
|
|
fmt.Println("# Part 2")
|
|
|
|
|
fmt.Println("Fuel:", fuel)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func FindRequiredOre(inp []string, fuel int) int {
|
|
|
|
|
allReactions := make(map[string]*Reaction)
|
|
|
|
|
for _, v := range inp {
|
|
|
|
|
r := NewReaction(v)
|
|
|
|
|
allReactions[r.out] = r
|
|
|
|
|
}
|
|
|
|
|
return Produce("FUEL", fuel, allReactions, make(map[string]int))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Produce(chem string, amt int, allReactions map[string]*Reaction, excess map[string]int) int {
|
|
|
|
|
if chem == "ORE" {
|
|
|
|
|
return amt
|
|
|
|
|
}
|
|
|
|
|
if excess[chem] >= amt {
|
|
|
|
|
excess[chem] -= excess[chem]
|
|
|
|
|
excess[chem] = 0
|
|
|
|
|
}
|
|
|
|
|
recipe := allReactions[chem]
|
|
|
|
|
times := int(math.Ceil(float64(amt) / float64(recipe.outQty)))
|
|
|
|
|
ret := 0
|
|
|
|
|
for k, inAmt := range recipe.in {
|
|
|
|
|
ret += Produce(k, inAmt*times, allReactions, excess)
|
|
|
|
|
}
|
|
|
|
|
numProduced := times * recipe.outQty
|
|
|
|
|
excess[chem] += numProduced - amt
|
|
|
|
|
return ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetMaxFuelForOre(inp []string, ore int) int {
|
|
|
|
|
var start, guesses, lastGuess, fuelGuess int
|
|
|
|
|
end := ore
|
|
|
|
|
for {
|
|
|
|
|
guesses++
|
|
|
|
|
fuelGuess = (end-start)/2 + start
|
|
|
|
|
requiredOre := FindRequiredOre(inp, fuelGuess)
|
|
|
|
|
if requiredOre == ore {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if requiredOre > ore {
|
|
|
|
|
end = fuelGuess
|
|
|
|
|
} else {
|
|
|
|
|
start = fuelGuess
|
|
|
|
|
}
|
|
|
|
|
if fuelGuess == lastGuess {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
lastGuess = fuelGuess
|
|
|
|
|
}
|
|
|
|
|
return fuelGuess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this is too slow... Gonna re-engineer it...
|
|
|
|
|
func part2Slow(inp []string) {
|
2019-12-15 14:57:44 +00:00
|
|
|
|
allReactions = make(map[string]*Reaction)
|
|
|
|
|
for _, v := range inp {
|
|
|
|
|
r := NewReaction(v)
|
|
|
|
|
allReactions[r.out] = r
|
|
|
|
|
}
|
|
|
|
|
factory := NewFactory(allReactions)
|
|
|
|
|
oreStart := 1000000000000
|
|
|
|
|
factory.setStock("ORE", oreStart)
|
|
|
|
|
fmt.Println("\n# Part 2")
|
|
|
|
|
fmt.Println("Ore in stock", factory.stock["ORE"])
|
|
|
|
|
var out1 string
|
|
|
|
|
for factory.generate("FUEL") {
|
|
|
|
|
clearStr := strings.Repeat("", len(out1))
|
|
|
|
|
out1 = fmt.Sprintf("%s%3d%% (%d)", clearStr, ((factory.stock["ORE"] * 100) / oreStart), factory.generated["FUEL"])
|
|
|
|
|
fmt.Print(out1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Factory struct {
|
|
|
|
|
stock map[string]int
|
|
|
|
|
recipes map[string]*Reaction
|
|
|
|
|
generated map[string]int
|
|
|
|
|
used map[string]int
|
|
|
|
|
infinite map[string]bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewFactory(recipes map[string]*Reaction) *Factory {
|
|
|
|
|
f := Factory{
|
|
|
|
|
stock: make(map[string]int),
|
|
|
|
|
recipes: recipes,
|
|
|
|
|
generated: make(map[string]int),
|
|
|
|
|
used: make(map[string]int),
|
|
|
|
|
infinite: make(map[string]bool),
|
|
|
|
|
}
|
|
|
|
|
return &f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Factory) setStock(chem string, amt int) {
|
|
|
|
|
f.stock[chem] = amt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Factory) setInfinite(chem string, is bool) {
|
|
|
|
|
f.infinite[chem] = is
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Factory) count(chem string) int {
|
|
|
|
|
return f.stock[chem]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Factory) use(chem string, amt int) bool {
|
|
|
|
|
if f.count(chem) < amt {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
f.stock[chem] = f.stock[chem] - amt
|
|
|
|
|
f.used[chem] = f.used[chem] + amt
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Factory) generate(chem string) bool {
|
|
|
|
|
if f.infinite[chem] {
|
|
|
|
|
f.stock[chem] = f.stock[chem] + 1
|
|
|
|
|
f.generated[chem] = f.generated[chem] + 1
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
react, ok := f.recipes[chem]
|
|
|
|
|
if !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for k, v := range react.in {
|
|
|
|
|
for f.count(k) < v {
|
|
|
|
|
f.generate(k)
|
|
|
|
|
}
|
|
|
|
|
f.use(k, v)
|
|
|
|
|
}
|
|
|
|
|
f.stock[chem] = f.stock[chem] + react.outQty
|
|
|
|
|
f.generated[chem] = f.generated[chem] + react.outQty
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Reaction struct {
|
|
|
|
|
in map[string]int
|
|
|
|
|
out string
|
|
|
|
|
outQty int
|
|
|
|
|
|
|
|
|
|
ingredients []*Reaction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewReaction(inp string) *Reaction {
|
|
|
|
|
pts := strings.Split(inp, " => ")
|
|
|
|
|
if len(pts) != 2 {
|
|
|
|
|
panic("Invalid Reaction Input: " + inp)
|
|
|
|
|
}
|
|
|
|
|
ret := Reaction{
|
|
|
|
|
in: make(map[string]int),
|
|
|
|
|
}
|
|
|
|
|
inps := strings.Split(pts[0], ", ")
|
|
|
|
|
for k := range inps {
|
|
|
|
|
var qty int
|
|
|
|
|
var prod string
|
|
|
|
|
_, err := fmt.Sscanf(inps[k], "%d %s", &qty, &prod)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic("Invalid Reaction Input: " + inp)
|
|
|
|
|
}
|
|
|
|
|
ret.in[prod] = qty
|
|
|
|
|
}
|
|
|
|
|
_, err := fmt.Sscanf(pts[1], "%d %s", &ret.outQty, &ret.out)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic("Invalid Reaction Input: " + inp)
|
|
|
|
|
}
|
|
|
|
|
return &ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r Reaction) String() string {
|
|
|
|
|
var ret string
|
|
|
|
|
for k, v := range r.in {
|
|
|
|
|
if len(ret) > 0 {
|
|
|
|
|
ret = ret + ","
|
|
|
|
|
}
|
|
|
|
|
ret = fmt.Sprintf("%s %d %s", ret, v, k)
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("%s => %d %s", ret, r.outQty, r.out)
|
|
|
|
|
}
|