AOC2022/day19/day19.go

225 lines
7.6 KiB
Go
Raw Normal View History

2022-12-19 23:39:38 +01:00
package main
import (
"AOC2022/helper"
"fmt"
"strconv"
"strings"
)
type State struct {
currentRessources [4]int
currentProduction [4]int
runtime int
}
type Blueprint struct {
oreRoboterCost int
clayRoboterCost int
obsidianRobototerCost [2]int
geodeRoboterCost [2]int
}
func main() {
//args := os.Args[1:]
lines := helper.ReadTextFile("day19/input")
highestGeode := make([]int, len(lines))
for i, line := range lines {
blueprint := getBluePrint(line)
2022-12-20 02:11:26 +01:00
highestGeodeLine := 0
for j := 0; j < 50; j++ {
tmphighestGeode := getHighestGeode(blueprint, map[[4]int]State{[4]int{0, 0, 0, 0}: State{[4]int{0, 0, 0, 0}, [4]int{1, 0, 0, 0}, 0}})
if tmphighestGeode > highestGeodeLine {
highestGeodeLine = tmphighestGeode
}
}
highestGeode[i] = highestGeodeLine
2022-12-20 02:06:55 +01:00
fmt.Println(highestGeode[i])
2022-12-19 23:39:38 +01:00
}
sum := 0
for i, score := range highestGeode {
sum += (i + 1) * score
}
2022-12-20 02:06:55 +01:00
fmt.Println(highestGeode)
2022-12-19 23:39:38 +01:00
fmt.Println(sum)
}
2022-12-20 02:06:55 +01:00
func getHighestGeode(blueprint Blueprint, startStates map[[4]int]State) int {
2022-12-19 23:39:38 +01:00
activeStates := startStates
endStates := []State{}
for len(activeStates) > 0 {
stepHighestGeode(&activeStates, &endStates, &blueprint)
}
highestgeode := 0
for _, state := range endStates {
2022-12-20 02:06:55 +01:00
if state.currentRessources[3] > highestgeode {
2022-12-19 23:39:38 +01:00
highestgeode = state.currentRessources[3]
}
}
return highestgeode
}
2022-12-20 02:06:55 +01:00
func getFastestTimeToElementN(blueprint Blueprint, startStates map[[4]int]State, untilElementN int) int {
2022-12-19 23:39:38 +01:00
activeStates := startStates
fastestTimeToObsidianState := State{[4]int{0, 0, 0, 0}, [4]int{1, 0, 0, 0}, 26}
for len(activeStates) > 0 {
step(&activeStates, &fastestTimeToObsidianState, untilElementN, &blueprint)
}
return fastestTimeToObsidianState.runtime
}
2022-12-20 02:06:55 +01:00
func getAllPossibleCombinationsWithFastestTime(blueprint Blueprint, startStates map[[4]int]State, untilElementN int, fastestTime int) map[[4]int]State {
2022-12-19 23:39:38 +01:00
activeStates := startStates
2022-12-20 02:06:55 +01:00
fastestTimeToObsidianState := map[[4]int]State{}
2022-12-19 23:39:38 +01:00
for len(activeStates) > 0 {
stepFindAllFastestTime(&activeStates, &fastestTimeToObsidianState, untilElementN, fastestTime, &blueprint)
}
2022-12-20 00:26:25 +01:00
return fastestTimeToObsidianState
2022-12-19 23:39:38 +01:00
}
2022-12-20 02:06:55 +01:00
func step(activeStates *map[[4]int]State, fastestTImeToObsidian *State, untilElementN int, blueprint *Blueprint) {
2022-12-20 00:26:25 +01:00
newTmpStates := generatePossibleTmpStates(activeStates, blueprint)
2022-12-19 23:39:38 +01:00
for _, tmpState := range newTmpStates {
if tmpState.currentProduction[untilElementN] > 0 && (*fastestTImeToObsidian).runtime > tmpState.runtime {
*fastestTImeToObsidian = tmpState
}
if tmpState.currentProduction[untilElementN] == 0 && tmpState.runtime < (*fastestTImeToObsidian).runtime {
2022-12-20 02:06:55 +01:00
identifier := getIdentifier(tmpState)
elem, ok := (*activeStates)[identifier]
if !ok || elem.runtime > tmpState.runtime {
(*activeStates)[identifier] = tmpState
}
2022-12-19 23:39:38 +01:00
}
}
}
2022-12-20 02:06:55 +01:00
func stepHighestGeode(activeStates *map[[4]int]State, endStates *[]State, blueprint *Blueprint) {
2022-12-20 00:26:25 +01:00
newTmpStates := generatePossibleTmpStates(activeStates, blueprint)
2022-12-19 23:39:38 +01:00
for _, tmpState := range newTmpStates {
2022-12-20 02:06:55 +01:00
if tmpState.runtime < 24 && !checkOptimal(tmpState, blueprint) {
identifier := getIdentifier(tmpState)
elem, ok := (*activeStates)[identifier]
if !ok || elem.runtime > tmpState.runtime || (elem.runtime == tmpState.runtime && checkBetterRessources(tmpState, elem)) {
(*activeStates)[identifier] = tmpState
}
2022-12-20 00:26:25 +01:00
} else if tmpState.runtime > 24 {
fmt.Println("WTF")
2022-12-19 23:39:38 +01:00
} else {
*endStates = append(*endStates, tmpState)
}
}
}
2022-12-20 02:06:55 +01:00
func checkOptimal(state1 State, blueprint *Blueprint) bool {
return state1.currentProduction[0] >= blueprint.geodeRoboterCost[0] && state1.currentProduction[2] >= blueprint.geodeRoboterCost[1]
}
func checkBetterRessources(state1, state2 State) bool {
ressources1 := state1.currentRessources
ressources2 := state2.currentRessources
return ressources1[0] > ressources1[0] && ressources1[1] > ressources2[1] && ressources1[2] > ressources2[2] && ressources1[3] > ressources2[3]
}
func stepFindAllFastestTime(activeStates *map[[4]int]State, fastestTImeStates *map[[4]int]State, untilElementN int, fastestTime int, blueprint *Blueprint) {
2022-12-20 00:26:25 +01:00
newTmpStates := generatePossibleTmpStates(activeStates, blueprint)
2022-12-19 23:39:38 +01:00
for _, tmpState := range newTmpStates {
2022-12-20 00:26:25 +01:00
if tmpState.currentProduction[untilElementN] > 0 && fastestTime >= tmpState.runtime && tmpState.runtime < 24 {
identifier := getIdentifier(tmpState)
2022-12-19 23:39:38 +01:00
(*fastestTImeStates)[identifier] = tmpState
}
2022-12-20 00:26:25 +01:00
if tmpState.currentProduction[untilElementN] == 0 && tmpState.runtime < fastestTime && tmpState.runtime < 24 {
(*activeStates)[getIdentifier(tmpState)] = tmpState
2022-12-19 23:39:38 +01:00
}
}
}
2022-12-20 02:06:55 +01:00
func getIdentifier(tmpState State) [4]int {
identifier := [4]int{}
copy(identifier[:], tmpState.currentProduction[:4])
2022-12-20 00:26:25 +01:00
return identifier
}
2022-12-20 02:06:55 +01:00
func generatePossibleTmpStates(activeStates *map[[4]int]State, blueprint *Blueprint) map[[4]int]State {
2022-12-20 00:26:25 +01:00
key := get_some_key(*activeStates)
activeState := (*activeStates)[key]
delete(*activeStates, key)
2022-12-19 23:39:38 +01:00
possibleProductions := activeState.getPossibleProductions(blueprint)
2022-12-20 02:06:55 +01:00
newTmpStates := make(map[[4]int]State)
2022-12-19 23:39:38 +01:00
for i := -1; i < len(possibleProductions); i++ {
if i == -1 || possibleProductions[i] == 1 {
tmpState := activeState
tmpState.produceRessources()
tmpState.produceRoboter(blueprint, i)
tmpState.runtime++
2022-12-20 00:26:25 +01:00
newTmpStates[getIdentifier(tmpState)] = tmpState
2022-12-19 23:39:38 +01:00
}
}
2022-12-20 00:26:25 +01:00
return newTmpStates
2022-12-19 23:39:38 +01:00
}
func (state *State) produceRoboter(blueprint *Blueprint, roboter int) {
switch roboter {
case 0:
state.currentProduction[0]++
state.currentRessources[0] -= blueprint.oreRoboterCost
case 1:
state.currentProduction[1]++
state.currentRessources[0] -= blueprint.clayRoboterCost
case 2:
state.currentProduction[2]++
state.currentRessources[0] -= blueprint.obsidianRobototerCost[0]
state.currentRessources[1] -= blueprint.obsidianRobototerCost[1]
case 3:
state.currentProduction[3]++
state.currentRessources[0] -= blueprint.geodeRoboterCost[0]
state.currentRessources[2] -= blueprint.geodeRoboterCost[1]
default:
}
}
func (state *State) produceRessources() {
for i := 0; i < 4; i++ {
state.currentRessources[i] += state.currentProduction[i]
}
}
func (state State) getPossibleProductions(blueprint *Blueprint) [4]int {
possibleProductions := [4]int{0, 0, 0, 0}
if state.currentRessources[0] >= blueprint.oreRoboterCost {
possibleProductions[0] = 1
}
if state.currentRessources[0] >= blueprint.clayRoboterCost {
possibleProductions[1] = 1
}
if state.currentRessources[0] >= blueprint.obsidianRobototerCost[0] && state.currentRessources[1] >= blueprint.obsidianRobototerCost[1] {
possibleProductions[2] = 1
}
if state.currentRessources[0] >= blueprint.geodeRoboterCost[0] && state.currentRessources[2] >= blueprint.geodeRoboterCost[1] {
possibleProductions[3] = 1
}
return possibleProductions
}
func getBluePrint(line string) Blueprint {
productionCostStrings := strings.Split(strings.Split(line, ":")[1], ". ")
oreCostString := strings.ReplaceAll(productionCostStrings[0], " ore", "")[18:]
oreCost, _ := strconv.Atoi(oreCostString)
clayCostString := strings.ReplaceAll(productionCostStrings[1], " ore", "")[22:]
claycost, _ := strconv.Atoi(clayCostString)
obsidiancostString := strings.ReplaceAll(productionCostStrings[2], "ore and ", "")[26:]
obsidianCost := helper.StringSliceToIntSlice(strings.Split(obsidiancostString[:len(obsidiancostString)-5], " "))
geodeCostString := strings.ReplaceAll(productionCostStrings[3], "ore and ", "")[23:]
geodeCost := helper.StringSliceToIntSlice(strings.Split(geodeCostString[:len(geodeCostString)-10], " "))
return Blueprint{oreCost, claycost, [2]int{obsidianCost[0], obsidianCost[1]}, [2]int{geodeCost[0], geodeCost[1]}}
}
2022-12-20 00:26:25 +01:00
2022-12-20 02:06:55 +01:00
func get_some_key(m map[[4]int]State) [4]int {
2022-12-20 00:26:25 +01:00
for k := range m {
return k
}
2022-12-20 02:06:55 +01:00
return [4]int{}
2022-12-20 00:26:25 +01:00
}