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) }