diff --git a/day19/common/factory.go b/day19/common/factory.go new file mode 100644 index 0000000..b1b931a --- /dev/null +++ b/day19/common/factory.go @@ -0,0 +1,210 @@ +package common + +import ( + "bufio" + "strconv" + "strings" +) + + +type Material byte + +const ( + Ore Material = iota + Clay + Obsidian + Geode +) + + +type Robot byte + +const ( + OreMiner Robot = iota + ClayMiner + ObsidianMiner + GeodeMiner +) + + +type Factory struct { + id int + materials map[Material]int + robots map[Robot]int + costs map[Robot]map[Material]int + minutesLeft int + builtRobots []Robot + maxProductionRequired map[Material]int +} + +func (factory *Factory) clone() *Factory { + newFactory := Factory{factory.id, make(map[Material]int), map[Robot]int{}, factory.costs, factory.minutesLeft, []Robot{}, factory.maxProductionRequired} + for m, v := range factory.materials { + newFactory.materials[m] = v + } + for r, v := range factory.robots { + newFactory.robots[r] = v + } + for i := range factory.builtRobots { + newFactory.builtRobots = append(newFactory.builtRobots, factory.builtRobots[i]) + } + return &newFactory +} + +func (factory *Factory) GenerateResources() { + for r, v := range factory.robots { + switch r { + case OreMiner: + factory.materials[Ore] += v + case ClayMiner: + factory.materials[Clay] += v + case ObsidianMiner: + factory.materials[Obsidian] += v + case GeodeMiner: + factory.materials[Geode] += v + } + } + for i := range factory.builtRobots { + factory.robots[factory.builtRobots[i]]++ + } + factory.builtRobots = []Robot{} + factory.minutesLeft-- +} + +func (factory *Factory) StartOptimizedProduction() { + factory.GenerateResources() + + newFactories := []*Factory{factory.clone(), factory.clone(), factory.clone(), factory.clone()} + for i := range newFactories { + if i == 0 { + for (newFactories[i].materials[Ore] < newFactories[i].costs[GeodeMiner][Ore] || + newFactories[i].materials[Obsidian] < newFactories[i].costs[GeodeMiner][Obsidian]) && + newFactories[i].minutesLeft > 0 { + newFactories[i].GenerateResources() + } + if newFactories[i].minutesLeft > 0 { + newFactories[i].builtRobots = []Robot{GeodeMiner} + newFactories[i].materials[Ore] -= newFactories[i].costs[GeodeMiner][Ore] + newFactories[i].materials[Obsidian] -= newFactories[i].costs[GeodeMiner][Obsidian] + newFactories[i].StartOptimizedProduction() + } + } else if i == 1 && newFactories[i].robots[ObsidianMiner] < newFactories[i].maxProductionRequired[Obsidian] { + for (newFactories[i].materials[Ore] < newFactories[i].costs[ObsidianMiner][Ore] || + newFactories[i].materials[Clay] < newFactories[i].costs[ObsidianMiner][Clay]) && + newFactories[i].minutesLeft > 0 { + newFactories[i].GenerateResources() + } + if newFactories[i].minutesLeft > 0 { + newFactories[i].builtRobots = []Robot{ObsidianMiner} + newFactories[i].materials[Ore] -= newFactories[i].costs[ObsidianMiner][Ore] + newFactories[i].materials[Clay] -= newFactories[i].costs[ObsidianMiner][Clay] + newFactories[i].StartOptimizedProduction() + } + } else if i == 2 && newFactories[i].robots[ClayMiner] < newFactories[i].maxProductionRequired[Clay] { + for newFactories[i].materials[Ore] < newFactories[i].costs[ClayMiner][Ore] && + newFactories[i].minutesLeft > 0 { + newFactories[i].GenerateResources() + } + if newFactories[i].minutesLeft > 0 { + newFactories[i].builtRobots = []Robot{ClayMiner} + newFactories[i].materials[Ore] -= newFactories[i].costs[ClayMiner][Ore] + newFactories[i].StartOptimizedProduction() + } + } else if i == 3 && newFactories[i].robots[OreMiner] < newFactories[i].maxProductionRequired[Ore] { + for newFactories[i].materials[Ore] < newFactories[i].costs[OreMiner][Ore] && + newFactories[i].minutesLeft > 0 { + newFactories[i].GenerateResources() + } + if newFactories[i].minutesLeft > 0 { + newFactories[i].builtRobots = []Robot{OreMiner} + newFactories[i].materials[Ore] -= newFactories[i].costs[OreMiner][Ore] + newFactories[i].StartOptimizedProduction() + } + } + + if newFactories[i].materials[Geode] > factory.materials[Geode] { + factory.materials = newFactories[i].materials + factory.robots = newFactories[i].robots + factory.minutesLeft = newFactories[i].minutesLeft + } + } +} + +func (factory *Factory) GetQualityLevel() int { + return factory.id * factory.materials[Geode] +} + +func (factory *Factory) GetNumberOfGeodes() int { + return factory.materials[Geode] +} + + +func Parse(scanner bufio.Scanner, limit int, minutesLeft int) []Factory { + factories := []Factory{} + + i := 0 + for scanner.Scan() { + if limit >= 0 && i >= limit { + break + } + + line := scanner.Text() + splittedLine := strings.Split(line, ".") + factory := Factory{} + + splittedLine2 := strings.Split(splittedLine[0], " ") + id, _ := strconv.Atoi(splittedLine2[1][:len(splittedLine2[1])-1]) + + factory.id = id + factory.materials = make(map[Material]int) + factory.materials[Ore] = 0 + factory.materials[Clay] = 0 + factory.materials[Obsidian] = 0 + factory.materials[Geode] = 0 + factory.robots = make(map[Robot]int) + factory.robots[OreMiner] = 1 + factory.robots[ClayMiner] = 0 + factory.robots[ObsidianMiner] = 0 + factory.robots[GeodeMiner] = 0 + factory.minutesLeft = minutesLeft + factory.maxProductionRequired = make(map[Material]int) + + factory.costs = make(map[Robot]map[Material]int) + oreCost, _ := strconv.Atoi(splittedLine2[6]) + factory.costs[OreMiner] = make(map[Material]int) + factory.costs[OreMiner][Ore] = oreCost + factory.maxProductionRequired[Ore] = oreCost + splittedLine2 = strings.Split(splittedLine[1], " ") + oreCost, _ = strconv.Atoi(splittedLine2[5]) + factory.costs[ClayMiner] = make(map[Material]int) + factory.costs[ClayMiner][Ore] = oreCost + if oreCost > factory.maxProductionRequired[Ore] { + factory.maxProductionRequired[Ore] = oreCost + } + splittedLine2 = strings.Split(splittedLine[2], " ") + oreCost, _ = strconv.Atoi(splittedLine2[5]) + clayCost, _ := strconv.Atoi(splittedLine2[8]) + factory.costs[ObsidianMiner] = make(map[Material]int) + factory.costs[ObsidianMiner][Ore] = oreCost + factory.costs[ObsidianMiner][Clay] = clayCost + if oreCost > factory.maxProductionRequired[Ore] { + factory.maxProductionRequired[Ore] = oreCost + } + factory.maxProductionRequired[Clay] = clayCost + splittedLine2 = strings.Split(splittedLine[3], " ") + oreCost, _ = strconv.Atoi(splittedLine2[5]) + obsidianCost, _ := strconv.Atoi(splittedLine2[8]) + factory.costs[GeodeMiner] = make(map[Material]int) + factory.costs[GeodeMiner][Ore] = oreCost + factory.costs[GeodeMiner][Obsidian] = obsidianCost + if oreCost > factory.maxProductionRequired[Ore] { + factory.maxProductionRequired[Ore] = oreCost + } + factory.maxProductionRequired[Obsidian] = obsidianCost + + factories = append(factories, factory) + i++ + } + + return factories +} diff --git a/day19/ex1/main.go b/day19/ex1/main.go new file mode 100644 index 0000000..021092e --- /dev/null +++ b/day19/ex1/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "aoc2022/day19/common" + "bufio" + "fmt" + "os" +) + +func main() { + factories := common.Parse(*bufio.NewScanner(os.Stdin), -1, 24) + sum := 0 + + for i := range(factories) { + factories[i].StartOptimizedProduction() + sum += factories[i].GetQualityLevel() + } + + fmt.Println(sum) +} diff --git a/day19/ex2/main.go b/day19/ex2/main.go new file mode 100644 index 0000000..0ceed69 --- /dev/null +++ b/day19/ex2/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "aoc2022/day19/common" + "bufio" + "fmt" + "os" +) + +func main() { + factories := common.Parse(*bufio.NewScanner(os.Stdin), 3, 32) + result := 1 + + for i := range(factories) { + factories[i].StartOptimizedProduction() + result *= factories[i].GetNumberOfGeodes() + } + + fmt.Println(result) +}