package main import ( "fmt" "os" "strconv" ) const ( playerTurn = iota bossTurn ) type state struct { bossHp int bossDmg int playerHp int playerMp int playerDef int shield int poison int recharge int manaSpent int move int } type spell struct { name string mpCost int damage int heal int duration int } var minMp int var spellBook []spell var stateStack []state func main() { minMp = -1 initSpellBook() if len(os.Args) == 2 && os.Args[1] == "-help" { fmt.Println("Usage: day22 [ [ ]]") os.Exit(1) } //debugMode = true eHp := 71 eDmg := 10 if len(os.Args) >= 3 { eHp = mustAtoi(os.Args[1]) eDmg = mustAtoi(os.Args[2]) } pHp := 50 pMp := 500 if len(os.Args) == 5 { pHp = mustAtoi(os.Args[3]) pMp = mustAtoi(os.Args[4]) } s := state{ bossHp: eHp, bossDmg: eDmg, playerHp: pHp, playerMp: pMp, playerDef: 0, shield: 0, poison: 0, recharge: 0, manaSpent: 0, move: playerTurn, } stateStack = append(stateStack, s) for len(stateStack) > 0 { var wrk state wrk, stateStack = stateStack[0], stateStack[1:] // Hard Mode if wrk.move == playerTurn { wrk.playerHp-- } wrk.playerDef = 0 if wrk.shield > 0 { wrk.playerDef = 7 } if wrk.poison > 0 { wrk.bossHp -= 3 } if wrk.recharge > 0 { wrk.playerMp += 101 } wrk = decrementTimers(wrk) if wrk.playerHp <= 0 || (wrk.manaSpent > minMp && minMp != -1) { // Unsuccessful path continue } if wrk.bossHp <= 0 { // Successful path! if minMp == -1 || minMp > wrk.manaSpent { minMp = wrk.manaSpent } continue } if wrk.move == bossTurn { wrk.move = playerTurn atk := 1 if wrk.bossDmg-wrk.playerDef > 1 { atk = wrk.bossDmg - wrk.playerDef } wrk.playerHp -= atk stateStack = append([]state{wrk}, stateStack...) } else { wrk.move = bossTurn for i := range spellBook { tstSpell := spellBook[i] if tstSpell.mpCost >= wrk.playerMp { continue } nextState, ok := castSpell(wrk, tstSpell) if ok { stateStack = append([]state{nextState}, stateStack...) } } } } fmt.Printf("%d\n", minMp) } func castSpell(w state, s spell) (state, bool) { if s.name == "Magic Missile" || s.name == "Drain" { w.bossHp -= s.damage w.playerHp += s.heal } else if s.name == "Shield" { if w.shield > 0 { return w, false } w.shield = s.duration } else if s.name == "Poison" { if w.poison > 0 { return w, false } w.poison = s.duration } else if s.name == "Recharge" { if w.recharge > 0 { return w, false } w.recharge = s.duration } w.playerMp -= s.mpCost w.manaSpent += s.mpCost return w, true } func copyState(w state) state { ret := state{ bossHp: w.bossHp, bossDmg: w.bossDmg, playerHp: w.playerHp, playerMp: w.playerMp, playerDef: w.playerDef, shield: w.shield, poison: w.poison, recharge: w.recharge, manaSpent: w.manaSpent, move: w.move, } return ret } func printState(w state) { if w.move == playerTurn { fmt.Println("*** Player Turn ***") } else { fmt.Println("*** Boss Turn ***") } fmt.Printf("Player - HP(%d) MP(%d)\n", w.playerHp, w.playerMp) fmt.Printf("Boss - HP(%d) DMG(%d)\n", w.bossHp, w.bossDmg) fmt.Printf("Shield(%d); Poison(%d); Recharge(%d)\n", w.shield, w.poison, w.recharge) fmt.Printf("Mana Used: %d\n", w.manaSpent) fmt.Println() } func decrementTimers(w state) state { w.shield-- w.poison-- w.recharge-- return w } func initSpellBook() { spellBook = append(spellBook, spell{ name: "Magic Missile", mpCost: 53, damage: 4, }) spellBook = append(spellBook, spell{ name: "Drain", mpCost: 73, damage: 2, heal: 2, }) spellBook = append(spellBook, spell{ name: "Shield", mpCost: 113, duration: 6, }) spellBook = append(spellBook, spell{ name: "Poison", mpCost: 173, duration: 6, }) spellBook = append(spellBook, spell{ name: "Recharge", mpCost: 229, duration: 5, }) } func itoa(i int) string { return strconv.Itoa(i) } func mustAtoi(s string) int { var i int var err error if i, err = strconv.Atoi(s); err != nil { fmt.Println("Tried to atoi " + s) os.Exit(1) } return i }