adventofcode/2022/day19/main.go

124 lines
3.6 KiB
Go

package main
import (
"fmt"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
type blueprint struct {
id int
oreBot oreBot
clayBot clayBot
obsidianBot obsidianBot
geodeBot geodeBot
}
type oreBot struct{ oreCost int }
type clayBot struct{ oreCost int }
type obsidianBot struct{ oreCost, clayCost int }
type geodeBot struct{ oreCost, obsidianCost int }
var globalBest = 0
func main() {
blueprints := parseInput(h.StdinToStringSlice())
part1(blueprints)
part2(blueprints)
}
func part1(blueprints []blueprint) {
result := 0
for _, bp := range blueprints {
result += bp.id * search(bp, 0, 0, 0, 24, 1, 0, 0, 0, 0)
globalBest = 0
}
fmt.Println("# Part 1")
fmt.Println(result)
fmt.Println()
}
func part2(blueprints []blueprint) {
if len(blueprints) < 3 {
fmt.Println("# Part 2")
fmt.Println("Error: Not enough Blueprints")
return
}
result := 1
for i := 0; i < 3; i++ {
result *= search(blueprints[i], 0, 0, 0, 32, 1, 0, 0, 0, 0)
globalBest = 0
}
fmt.Println("# Part 2")
fmt.Println(result)
}
func search(bp blueprint, ore, clay, obs, time, oreBots, clayBots, obsidianBots, geodeBots, geodes int) int {
if time == 0 || globalBest >= geodes+rangeSum(geodeBots, geodeBots+time-1) {
return 0
}
if oreBots >= bp.geodeBot.oreCost && obsidianBots >= bp.geodeBot.obsidianCost {
return rangeSum(geodeBots, geodeBots+time-1)
}
oreLimitHit := oreBots >= h.Max(bp.geodeBot.oreCost, h.Max(bp.clayBot.oreCost, bp.obsidianBot.oreCost))
clayLimitHit := clayBots >= bp.obsidianBot.clayCost
obsLimitHit := obsidianBots >= bp.geodeBot.obsidianCost
best := 0
if !oreLimitHit {
best = h.Max(
best,
geodeBots+search(
bp, ore+oreBots, clay+clayBots, obs+obsidianBots,
time-1, oreBots, clayBots, obsidianBots, geodeBots, geodes+geodeBots))
}
if ore >= bp.oreBot.oreCost && !oreLimitHit {
best = h.Max(
best,
geodeBots+search(
bp, ore-bp.oreBot.oreCost+oreBots, clay+clayBots, obs+obsidianBots,
time-1, oreBots+1, clayBots, obsidianBots, geodeBots, geodes+geodeBots))
}
if ore >= bp.clayBot.oreCost && !clayLimitHit {
best = h.Max(
best, geodeBots+search(
bp, ore-bp.clayBot.oreCost+oreBots, clay+clayBots, obs+obsidianBots,
time-1, oreBots, clayBots+1, obsidianBots, geodeBots, geodes+geodeBots))
}
if ore >= bp.obsidianBot.oreCost && clay >= bp.obsidianBot.clayCost && !obsLimitHit {
best = h.Max(
best, geodeBots+search(
bp, ore-bp.obsidianBot.oreCost+oreBots, clay-bp.obsidianBot.clayCost+clayBots, obs+obsidianBots,
time-1, oreBots, clayBots, obsidianBots+1, geodeBots, geodes+geodeBots))
}
if ore >= bp.geodeBot.oreCost && obs >= bp.geodeBot.obsidianCost {
best = h.Max(
best, geodeBots+search(
bp, ore-bp.geodeBot.oreCost+oreBots, clay+clayBots, obs-bp.geodeBot.obsidianCost+obsidianBots,
time-1, oreBots, clayBots, obsidianBots, geodeBots+1, geodes+geodeBots))
}
globalBest = h.Max(best, globalBest)
return best
}
func rangeSum(first, last int) int {
return last*(last+1)/2 - ((first - 1) * first / 2)
}
func parseInput(inp []string) []blueprint {
var ret []blueprint
var id int
oreBot := oreBot{}
clayBot := clayBot{}
obsidianBot := obsidianBot{}
geodeBot := geodeBot{}
for i := range inp {
fmt.Sscanf(inp[i], "Blueprint %d: Each ore robot costs %d ore. Each clay robot costs %d ore. Each obsidian robot costs %d ore and %d clay. Each geode robot costs %d ore and %d obsidian.", &id, &oreBot.oreCost, &clayBot.oreCost, &obsidianBot.oreCost, &obsidianBot.clayCost, &geodeBot.oreCost, &geodeBot.obsidianCost)
ret = append(ret, blueprint{id, oreBot, clayBot, obsidianBot, geodeBot})
}
return ret
}