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
}