Files
advent2022/solutions/day19/day19.go
2022-12-23 19:45:56 -08:00

172 lines
4.5 KiB
Go

package day19
import (
"bufio"
"fmt"
"os"
)
type Blueprint struct {
blueprintNumber int
oreRobotOreCost int
clayRobotOreCost int
obsRobotOreCost int
obsRobotClayCost int
geodeRobotOreCost int
geodeRobotObsCost int
}
type State struct {
minute int
ore int
clay int
obsidian int
geodes int
oreRobots int
clayRobots int
obsidianRobots int
geodeRobots int
}
var initialState State = State{0, 0, 0, 0, 0, 1, 0, 0, 0}
func copyState(state State) State {
return State{
state.minute,
state.ore,
state.clay,
state.obsidian,
state.geodes,
state.oreRobots,
state.clayRobots,
state.obsidianRobots,
state.geodeRobots,
}
}
func nextStates(current State, minutes int, blueprint Blueprint) []State {
if current.minute == minutes {
return []State{}
}
buyNothing := State{
current.minute + 1,
current.ore + current.oreRobots,
current.clay + current.clayRobots,
current.obsidian + current.obsidianRobots,
current.geodes + current.geodeRobots,
current.oreRobots,
current.clayRobots,
current.obsidianRobots,
current.geodeRobots,
}
retval := []State{buyNothing}
if current.ore >= blueprint.oreRobotOreCost {
buyOre := copyState(buyNothing)
buyOre.ore -= blueprint.oreRobotOreCost
buyOre.oreRobots += 1
retval = append(retval, buyOre)
}
if current.ore >= blueprint.clayRobotOreCost {
buyClay := copyState(buyNothing)
buyClay.ore -= blueprint.clayRobotOreCost
buyClay.clayRobots += 1
retval = append(retval, buyClay)
}
if current.ore >= blueprint.obsRobotOreCost && current.clay >= blueprint.obsRobotClayCost {
buyObsidian := copyState(buyNothing)
buyObsidian.ore -= blueprint.obsRobotOreCost
buyObsidian.clay -= blueprint.obsRobotClayCost
buyObsidian.obsidianRobots += 1
retval = append(retval, buyObsidian)
}
if current.ore >= blueprint.geodeRobotOreCost && current.obsidian >= blueprint.geodeRobotObsCost {
buyGeode := copyState(buyNothing)
buyGeode.ore -= blueprint.geodeRobotOreCost
buyGeode.obsidian -= blueprint.geodeRobotObsCost
buyGeode.geodeRobots += 1
retval = append(retval, buyGeode)
}
return retval
}
func runSimulation(blueprint Blueprint, minutes int) int {
visited := map[State]bool{}
queue := []State{initialState}
bestResult := 0
for len(queue) > 0 {
nextState := queue[len(queue)-1]
queue = queue[0 : len(queue)-1]
_, alreadyDone := visited[nextState]
if alreadyDone {
continue
}
visited[nextState] = true
minutesLeft := minutes - nextState.minute
bestPossibleFuture := nextState.geodes + (minutesLeft*(2*nextState.geodeRobots+(minutesLeft-1)))/2
if bestPossibleFuture < bestResult {
continue
}
newStates := nextStates(nextState, minutes, blueprint)
if len(newStates) == 0 {
if nextState.geodes > bestResult {
bestResult = nextState.geodes
}
}
queue = append(queue, newStates...)
}
return bestResult
}
func Run(filename string) {
file, err := os.Open(filename)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
blueprints := []Blueprint{}
for scanner.Scan() {
line := scanner.Text()
var blueprintNumber, oreRobotOre, clayRobotOre, obsRobotOre, obsRobotClay, geodeRobotOre, geodeRobotObs int
fmt.Sscanf(line, "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.", &blueprintNumber, &oreRobotOre, &clayRobotOre, &obsRobotOre, &obsRobotClay, &geodeRobotOre, &geodeRobotObs)
blueprints = append(blueprints, Blueprint{blueprintNumber, oreRobotOre, clayRobotOre, obsRobotOre, obsRobotClay, geodeRobotOre, geodeRobotObs})
}
file.Close()
fmt.Println("Blueprints", blueprints)
totalQualityLevel := 0
for _, blueprint := range blueprints {
geodesHarvested := runSimulation(blueprint, 24)
fmt.Println("Found", geodesHarvested, "geodes for blueprint", blueprint.blueprintNumber)
totalQualityLevel += geodesHarvested * blueprint.blueprintNumber
}
fmt.Println("Total quality level of all blueprints at 24 minutes is", totalQualityLevel)
multipliedGeodes := 1
if len(blueprints) > 3 {
blueprints = blueprints[0:3]
}
for _, blueprint := range blueprints {
geodesHarvested := runSimulation(blueprint, 32)
fmt.Println("Found", geodesHarvested, "geodes for blueprint", blueprint.blueprintNumber)
multipliedGeodes *= geodesHarvested
}
fmt.Println("Multiplied number of geodes of first three blueprints at 32 minutes is", multipliedGeodes)
}