adventofcode/2015/day22/main.go

228 lines
4.2 KiB
Go

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 [<boss hp> <boss dmg> [<player hp> <player mp>]]")
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
}