172 lines
4.5 KiB
Go
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)
|
|
}
|