228 lines
4.2 KiB
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
|
|
}
|