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