package main import ( "bufio" "fmt" "log" "os" "strings" ) type Resource struct { Type string Count int } type Recipe struct { Materials []Resource Count int } func produce(recipes map[string]Recipe, resources map[string]int, target string, debug bool) int { recipe, ok := recipes[target] if !ok { log.Fatalf("%s is not part of resources", target) } if debug { fmt.Printf("producing %d %s\n", recipe.Count, target) } for _, m := range recipe.Materials { if m.Type == "ORE" { resources[m.Type] += m.Count continue } if debug { fmt.Printf("need %d %s (%d)\n", m.Count, m.Type, resources[m.Type]) } if resources[m.Type] < m.Count { var produced int for produced < m.Count-resources[m.Type] { produced += produce(recipes, resources, m.Type, debug) } resources[m.Type] += produced if debug { fmt.Printf("produced %+v %s\n", produced, m.Type) fmt.Printf("%+v\n", resources) } } resources[m.Type] -= m.Count if debug { fmt.Printf("took %+v %s\n", m.Count, m.Type) fmt.Printf("%+v\n", resources) } } return recipe.Count } func CalculateOreRequirement(recipes map[string]Recipe, debug bool) int { if debug { fmt.Println() } resources := make(map[string]int) produce(recipes, resources, "FUEL", debug) if debug { fmt.Printf("%+v\n", resources) } return resources["ORE"] } func CalculateMaxFuel(recipes map[string]Recipe, target int, debug bool) int { if debug { fmt.Println() } step := 100000 var count, used int resources := make(map[string]int) for i := range recipes { recipe := recipes[i] recipe.Count *= step recipes[i] = recipe for j := range recipes[i].Materials { recipes[i].Materials[j].Count *= step } } it := 0 for used < target { it++ if it%100 == 0 { fmt.Println(count) } rec := make(map[string]int) for i := range resources { rec[i] = resources[i] } produce(recipes, rec, "FUEL", debug) ore := rec["ORE"] if used+ore > target { div := 10 if step == 100000 { div = 100000 } step /= div if step == 0 { break } for i := range recipes { recipe := recipes[i] recipe.Count /= div recipes[i] = recipe for j := range recipes[i].Materials { recipes[i].Materials[j].Count /= div } } for i := range resources { rec[i] = resources[i] } resources["ORE"] = 0 continue } used += ore rec["ORE"] = 0 count += step for i := range rec { resources[i] = rec[i] } } return count } func LoadRecipe(line string) (string, Recipe) { var in, out string parts := strings.Split(line, "=>") in = parts[0] out = parts[1] var count int var name string fmt.Sscanf(out, "%d %s", &count, &name) recipe := Recipe{ Count: count, } for _, i := range strings.Split(in, ",") { var count int var name string fmt.Sscanf(i, "%d %s", &count, &name) recipe.Materials = append(recipe.Materials, Resource{ Count: count, Type: name, }) } return name, recipe } func main() { input, err := os.Open("input.txt") if err != nil { log.Fatal(fmt.Errorf("could not open input file: %w", err)) } defer input.Close() scanner := bufio.NewScanner(input) scanner.Split(bufio.ScanLines) recipes := make(map[string]Recipe) for scanner.Scan() { name, recipe := LoadRecipe(strings.TrimSpace(scanner.Text())) recipes[name] = recipe } fmt.Printf("Part 1: %d\n", CalculateOreRequirement(recipes, false)) fmt.Printf("Part 2: %d\n", CalculateMaxFuel(recipes, 1000000000000, false)) }