mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-06-07 20:53:30 +02:00
look at this ugly function with 9 params :)
This commit is contained in:
+143
-22
@@ -3,9 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/alexchao26/advent-of-code-go/cast"
|
"github.com/alexchao26/advent-of-code-go/cast"
|
||||||
|
"github.com/alexchao26/advent-of-code-go/mathy"
|
||||||
"github.com/alexchao26/advent-of-code-go/util"
|
"github.com/alexchao26/advent-of-code-go/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,32 +17,151 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
fmt.Println("Running part", part)
|
fmt.Println("Running part", part)
|
||||||
|
|
||||||
if part == 1 {
|
ans := part1(util.ReadFile("./input.txt"), 50, 500, part)
|
||||||
ans := part1(util.ReadFile("./input.txt"))
|
|
||||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
|
||||||
fmt.Println("Output:", ans)
|
fmt.Println("Output:", ans)
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(input string, myHP, myMana, part int) int {
|
||||||
|
bossHP, bossDamage := parseInput(input)
|
||||||
|
_, _, _, _ = bossHP, bossDamage, myHP, myMana
|
||||||
|
return backtrackBattleSim(myHP, myMana, bossHP, bossDamage, [5]int{}, true, 0, map[string]int{}, part)
|
||||||
|
}
|
||||||
|
|
||||||
|
type spell struct {
|
||||||
|
name string
|
||||||
|
index int
|
||||||
|
cost int
|
||||||
|
effectLength int
|
||||||
|
instantDamage int
|
||||||
|
instantHeal int
|
||||||
|
effectDamage int
|
||||||
|
heal int
|
||||||
|
armorBuff int
|
||||||
|
manaRecharge int
|
||||||
|
}
|
||||||
|
|
||||||
|
var spellsMap = map[string]spell{
|
||||||
|
"Magic Missile": {
|
||||||
|
name: "Magic Missile",
|
||||||
|
index: 0,
|
||||||
|
cost: 53,
|
||||||
|
instantDamage: 4,
|
||||||
|
},
|
||||||
|
"Drain": {
|
||||||
|
name: "Drain",
|
||||||
|
index: 1,
|
||||||
|
cost: 73,
|
||||||
|
instantDamage: 2,
|
||||||
|
instantHeal: 2,
|
||||||
|
},
|
||||||
|
"Shield": {
|
||||||
|
name: "Shield",
|
||||||
|
index: 2,
|
||||||
|
cost: 113,
|
||||||
|
effectLength: 6,
|
||||||
|
armorBuff: 7, // does not stack for each turn
|
||||||
|
},
|
||||||
|
"Poison": {
|
||||||
|
name: "Poison",
|
||||||
|
index: 3,
|
||||||
|
cost: 173,
|
||||||
|
effectLength: 6,
|
||||||
|
effectDamage: 3,
|
||||||
|
},
|
||||||
|
"Recharge": {
|
||||||
|
name: "Recharge",
|
||||||
|
index: 4,
|
||||||
|
cost: 229,
|
||||||
|
effectLength: 5,
|
||||||
|
manaRecharge: 101,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashState(myHP, myMana, bossHP int, effectDurations [5]int, isMyTurn bool) string {
|
||||||
|
return fmt.Sprintf("%d_%d_%d_%v_%v", myHP, myMana, bossHP, effectDurations, isMyTurn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func backtrackBattleSim(myHP, myMana, bossHP, bossDamage int, effectDurations [5]int, isMyTurn bool, depth int, memo map[string]int, part int) (minMana int) {
|
||||||
|
// fmt.Printf("\nDEPTH: %d %v\nMe %d Mana: %d; Boss %d Dmg: %d\n DURATIONS: %v\n", depth, isMyTurn, myHP, myMana, bossHP, bossDamage, effectDurations)
|
||||||
|
|
||||||
|
hash := hashState(myHP, myMana, bossHP, effectDurations, isMyTurn)
|
||||||
|
if val, ok := memo[hash]; ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
if part == 2 && isMyTurn {
|
||||||
|
myHP--
|
||||||
|
}
|
||||||
|
if myHP <= 0 {
|
||||||
|
// fmt.Println("BOSS WINS")
|
||||||
|
return math.MaxInt32
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply any active spell effects
|
||||||
|
var myArmor int
|
||||||
|
for _, sp := range spellsMap {
|
||||||
|
if effectDurations[sp.index] > 0 {
|
||||||
|
effectDurations[sp.index]--
|
||||||
|
// many of values will be zero for any given spell
|
||||||
|
bossHP -= sp.effectDamage
|
||||||
|
myHP += sp.heal
|
||||||
|
myArmor += sp.armorBuff
|
||||||
|
myMana += sp.manaRecharge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf(" Post Effects: Me %d Mana: %d Def: %d; Boss %d\n", myHP, myMana, myArmor, bossHP)
|
||||||
|
|
||||||
|
if bossHP <= 0 {
|
||||||
|
// fmt.Println("PLAYER WINS")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
minMana = math.MaxInt32
|
||||||
|
if isMyTurn {
|
||||||
|
// iterate through spells, create a recursive call for each spell that
|
||||||
|
// can be called (i.e. its effectDuration index is zero)
|
||||||
|
var spellCasted bool
|
||||||
|
for _, spName := range []string{"Recharge", "Poison", "Shield", "Drain", "Magic Missile"} {
|
||||||
|
sp := spellsMap[spName]
|
||||||
|
if effectDurations[sp.index] == 0 {
|
||||||
|
if myMana >= sp.cost {
|
||||||
|
spellCasted = true
|
||||||
|
// make new durations array & add effect duration for this spell
|
||||||
|
newDurations := effectDurations
|
||||||
|
newDurations[sp.index] += sp.effectLength
|
||||||
|
// fmt.Printf(" dp %d, casting %s\n", depth, sp.name)
|
||||||
|
castResult := sp.cost + backtrackBattleSim(myHP+sp.instantHeal, myMana-sp.cost, bossHP-sp.instantDamage, bossDamage, newDurations, false, depth+1, memo, part)
|
||||||
|
// fmt.Printf(" result from casting %s @ depth %d: %d\n", sp.name, depth, castResult)
|
||||||
|
|
||||||
|
minMana = mathy.MinInt(minMana, castResult)
|
||||||
|
}
|
||||||
|
// } else {
|
||||||
|
// fmt.Println(" CANNOT cast", sp.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if cannot cast spell, lose
|
||||||
|
if !spellCasted {
|
||||||
|
// fmt.Println(" CANNOT CAST SPELL, LOST")
|
||||||
|
return math.MaxInt32
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ans := part2(util.ReadFile("./input.txt"))
|
// boss attacks, minimum 1 damage
|
||||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
attackDamage := mathy.MaxInt(1, bossDamage-myArmor)
|
||||||
fmt.Println("Output:", ans)
|
// recurse
|
||||||
|
bossAttackResult := backtrackBattleSim(myHP-attackDamage, myMana, bossHP, bossDamage, effectDurations, true, depth+1, memo, part)
|
||||||
|
|
||||||
|
minMana = mathy.MinInt(minMana, bossAttackResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fmt.Println(" from", depth, minMana)
|
||||||
|
memo[hash] = minMana
|
||||||
|
return minMana
|
||||||
}
|
}
|
||||||
|
|
||||||
func part1(input string) int {
|
func parseInput(input string) (bossHP, bossDamage int) {
|
||||||
parsed := parseInput(input)
|
|
||||||
_ = parsed
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func part2(input string) int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInput(input string) (ans []int) {
|
|
||||||
lines := strings.Split(input, "\n")
|
lines := strings.Split(input, "\n")
|
||||||
for _, l := range lines {
|
bossHP = cast.ToInt(strings.Split(lines[0], ": ")[1])
|
||||||
ans = append(ans, cast.ToInt(l))
|
bossDamage = cast.ToInt(strings.Split(lines[1], ": ")[1])
|
||||||
}
|
return bossHP, bossDamage
|
||||||
return ans
|
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-20
@@ -2,38 +2,35 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/alexchao26/advent-of-code-go/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_part1(t *testing.T) {
|
func Test_part1(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
input string
|
||||||
|
myHP int
|
||||||
|
myMana int
|
||||||
|
part int
|
||||||
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input string
|
args args
|
||||||
want int
|
want int
|
||||||
}{
|
}{
|
||||||
// {"actual", util.ReadFile("input.txt"), ACTUAL_ANSWER},
|
{
|
||||||
|
name: "example",
|
||||||
|
args: args{"Hit Points: 13\nDamage: 8", 10, 250, 1},
|
||||||
|
want: spellsMap["Poison"].cost + spellsMap["Magic Missile"].cost,
|
||||||
|
},
|
||||||
|
{"actual", args{util.ReadFile("input.txt"), 50, 500, 1}, 953},
|
||||||
|
{"actual", args{util.ReadFile("input.txt"), 50, 500, 2}, 1289},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := part1(tt.input); got != tt.want {
|
if got := part1(tt.args.input, tt.args.myHP, tt.args.myMana, tt.args.part); got != tt.want {
|
||||||
t.Errorf("part1() = %v, want %v", got, tt.want)
|
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_part2(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
want int
|
|
||||||
}{
|
|
||||||
// {"actual", util.ReadFile("input.txt"), ACTUAL_ANSWER},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := part2(tt.input); got != tt.want {
|
|
||||||
t.Errorf("part2() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user