adventofcode/2019/day14/main.go

213 lines
4.5 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"fmt"
"math"
"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) {
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) {
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)
}