day22; finally

This commit is contained in:
Stefan Harmuth 2021-10-24 12:32:52 +02:00
parent 3b22bfcc37
commit 7b7f4f563b
4 changed files with 199 additions and 93 deletions

View File

@ -1,126 +1,183 @@
package day22 package day22
import ( import (
"fmt"
"math" "math"
"strconv" "strconv"
"strings" "strings"
"tools" "tools"
) )
type Effect struct { /*
damage int Spells:
armor int Magic Missile - 53 Mana - 4 Damage
mana int Drain - 73 Mana - 2 Damage, 2 Heal
timer int Shield - 113 Mana - 7 Armor for 6 turns
} Poison - 173 Mana - 3 Damage per turn for 6 turns
Recharge - 229 Mana - 101 Mana per turn for 5 turns
func (e Effect) copy() *Effect { */
return &Effect{e.damage, e.armor, e.mana, e.timer}
}
var activeEffects map[string]*Effect
type Spell struct {
cost int
damage int
heal int
applyEffect Effect
}
type Entity struct { type Entity struct {
hitpoints int hitpoints int
mana int mana int
damage int
armor int armor int
damage int
} }
func (e Entity) copy() *Entity { var spells = map[string]string{
return &Entity{e.hitpoints, e.mana, e.damage, e.armor} "mm": "Magic Missile",
"d": "Drain",
"s": "Shield",
"rc": "Recharge",
"p": "Poison",
} }
var minWinningMana int
func (e Entity) printStats() { func applyEffects(player, boss Entity, effects map[string]int) (Entity, Entity, map[string]int) {
fmt.Println("HP:", e.hitpoints, "Mana:", e.mana, "Damage:", e.damage, "Armor:", e.armor) for e, t := range effects {
} t--
switch e {
var spells = map[string]Spell{ case "rc":
"Magic Missile": {53, 4, 0, Effect{}}, player.mana += 101
"Drain": {73, 2, 2, Effect{}}, // fmt.Println("Recharge provides 101 mana; it's timer is now ", t)
"Shield": {113, 0, 0, Effect{0, 7, 0, 6}}, case "p":
"Poison": {173, 0, 0, Effect{3, 0, 0, 6}}, boss.hitpoints -= 3
"Recharge": {229, 0, 0, Effect{0, 0, 101, 5}}, // fmt.Println("Poison deals 3 damage; it's timer is now ", t)
} case "s":
if t == 0 {
func applyEffects(player, boss *Entity) { player.armor -= 7
for eName, eData := range activeEffects {
switch eName {
case "Shield":
if eData.timer == spells["Shield"].applyEffect.timer {
fmt.Println("Apply Shield")
player.armor += eData.armor
} else if eData.timer == 1 {
fmt.Println("Remove Shield")
player.armor -= eData.armor
} }
case "Recharge": // fmt.Println("Shield's timer is now ", t)
fmt.Println("Regen Mana")
player.mana += eData.mana
case "Poison":
fmt.Println("Apply Poison")
boss.hitpoints -= eData.damage
} }
activeEffects[eName].timer-- if t == 0 {
if eData.timer == 0 { // fmt.Println(spells[e], " wears off.")
fmt.Println(eName, "wears off.") delete(effects, e)
delete(activeEffects, eName) } else {
effects[e] = t
} }
} }
return player, boss, effects
} }
func fightRound(player, boss *Entity, spellName string) (outcome int) { func turn(player, boss Entity, effects map[string]int, spell string, hardmode bool) (Entity, Entity, map[string]int, int, int) {
// outcome will be -1 on boss win, 0 on both living and 1 on player win // return values: -1 player dead; 0 both alive; 1 boss dead
spell := spells[spellName] /*
fmt.Println("-- Player Turn --")
applyEffects(player, boss) fmt.Println("- Player has ", player.hitpoints, "hit points, ", player.armor, " armor, ", player.mana, " mana")
if player.mana < spell.cost { fmt.Println("- Boss has ", boss.hitpoints, " hit points")
return -1 */
if hardmode {
player.hitpoints -= 1
if player.hitpoints <= 0 {
return player, boss, effects, 0, -1
}
} }
player.mana -= spell.cost // Apply effects, if any
player.hitpoints += spell.heal player, boss, effects = applyEffects(player, boss, effects)
boss.hitpoints -= spell.damage // Player cast spell -- which one?
if _, ok := activeEffects[spellName]; !ok && spell.applyEffect.timer > 0 { if _, ok := effects[spell]; ok {
activeEffects[spellName] = spell.applyEffect.copy() // cannot cast already active effect; how to fail?
// just pretend the player died ...
return player, boss, effects, 0, -1
}
var manaUsed int
switch spell {
case "mm":
// fmt.Println("Player casts Magic Missile; dealing 4 damage.")
boss.hitpoints -= 4
manaUsed = 53
case "d":
// fmt.Println("Player casts Drain; dealing 2 damage and healing 2 hit points.")
player.hitpoints += 2
boss.hitpoints -= 2
manaUsed = 73
case "p":
// fmt.Println("Player casts Poison.")
effects[spell] = 6
manaUsed = 173
case "s":
// fmt.Println("Player casts Shield.")
player.armor += 7
manaUsed = 113
effects[spell] = 6
case "rc":
// fmt.Println("Player casts recharge.")
manaUsed = 229
effects[spell] = 5
}
player.mana -= manaUsed
if player.mana <= 0 {
return player, boss, effects, manaUsed, -1
} }
// Check Boss health
if boss.hitpoints <= 0 { if boss.hitpoints <= 0 {
return 1 return player, boss, effects, manaUsed, 1
} }
applyEffects(player, boss) /*
fmt.Println("-- Boss Turn --")
fmt.Println("- Player has ", player.hitpoints, "hit points, ", player.armor, " armor, ", player.mana, " mana")
fmt.Println("- Boss has ", boss.hitpoints, " hit points")
*/
// Apply effects, if any
player, boss, effects = applyEffects(player, boss, effects)
// Check Boss health
if boss.hitpoints <= 0 { if boss.hitpoints <= 0 {
return 1 return player, boss, effects, manaUsed, 1
} }
player.hitpoints -= tools.Max(1, boss.damage-player.armor)
bossDamage := int(math.Max(1.0, float64(boss.damage-player.armor)))
// fmt.Println("Boss attacks for ", bossDamage, " damage.")
// Check Player health
player.hitpoints -= bossDamage
if player.hitpoints <= 0 { if player.hitpoints <= 0 {
return -1 return player, boss, effects, manaUsed, -1
} }
return 0 return player, boss, effects, manaUsed, 0
} }
func minManaToTheDeath(player, boss *Entity) int { func findLowestManaUsed(player, boss Entity, effects map[string]int, manaUsed int, spellsUsed string, hardmode bool, depth int) (int, string) {
manaUsed := 0 minManaUsed := math.MaxInt32
minManaUsed := math.MaxInt64 var minSpellsUsed string
for s := range spells {
return manaUsed var thisManaUse, state int
thisPlayer := player
thisBoss := boss
thisEffects := make(map[string]int)
thisSpells := spellsUsed + "," + s
for e, t := range effects {
thisEffects[e] = t
}
thisPlayer, thisBoss, thisEffects, thisManaUse, state = turn(thisPlayer, thisBoss, thisEffects, s, hardmode)
switch state {
case -1:
continue
case 0:
if thisManaUse+manaUsed < minWinningMana {
thisManaUse, thisSpells = findLowestManaUsed(thisPlayer, thisBoss, thisEffects, thisManaUse+manaUsed, thisSpells, hardmode, depth+1)
} else {
continue
}
case 1:
if manaUsed+thisManaUse < minWinningMana {
minWinningMana = manaUsed + thisManaUse
}
}
if thisManaUse < minManaUsed {
minManaUsed = thisManaUse
minSpellsUsed = thisSpells
}
}
return minManaUsed + manaUsed, minSpellsUsed
} }
func Part1(puzzle tools.AoCPuzzle) interface{} { func Part1(puzzle tools.AoCPuzzle) interface{} {
activeEffects = make(map[string]*Effect)
player := Entity{50, 500, 0, 0} player := Entity{50, 500, 0, 0}
boss := Entity{0, 0, 0, 0} boss := Entity{0, 0, 0, 0}
bossdata := puzzle.GetInputArray() bossData := puzzle.GetInputArray()
for _, line := range bossdata { for _, line := range bossData {
parts := strings.Split(line, ": ") parts := strings.Split(line, ": ")
switch parts[0] { switch parts[0] {
case "Hit Points": case "Hit Points":
@ -130,19 +187,30 @@ func Part1(puzzle tools.AoCPuzzle) interface{} {
} }
} }
player.printStats() effects := make(map[string]int)
boss.printStats() minWinningMana = math.MaxInt32
fmt.Println("-----")
for _, s := range []string{"Recharge", "Shield", "Drain", "Poison", "Magic Missile"} {
fightRound(&player, &boss, s)
player.printStats()
boss.printStats()
fmt.Println("-----")
}
return 0 findLowestManaUsed(player, boss, effects, 0, "", false, 0)
return minWinningMana
} }
func Part2(puzzle tools.AoCPuzzle) interface{} { func Part2(puzzle tools.AoCPuzzle) interface{} {
return 0 player := Entity{50, 500, 0, 0}
boss := Entity{0, 0, 0, 0}
bossData := puzzle.GetInputArray()
for _, line := range bossData {
parts := strings.Split(line, ": ")
switch parts[0] {
case "Hit Points":
boss.hitpoints, _ = strconv.Atoi(parts[1])
case "Damage":
boss.damage, _ = strconv.Atoi(parts[1])
}
}
effects := make(map[string]int)
minWinningMana = math.MaxInt32
findLowestManaUsed(player, boss, effects, 0, "", true, 0)
return minWinningMana
} }

36
day25/day.go Normal file
View File

@ -0,0 +1,36 @@
package day25
import (
"strconv"
"strings"
"tools"
)
func Part1(puzzle tools.AoCPuzzle) interface{} {
inputParts := strings.Split(puzzle.GetInputArray()[0], " ")
wantedX, _ := strconv.Atoi(inputParts[18][:len(inputParts[18])-1])
wantedY, _ := strconv.Atoi(inputParts[16][:len(inputParts[16])-1])
codeNumber := 20151125
x := 1
y := 1
maxY := 1
for {
codeNumber = codeNumber * 252533 % 33554393
y--
x++
if y == 0 {
y = maxY + 1
maxY = y
x = 1
}
if x == wantedX && y == wantedY {
return codeNumber
}
}
}
func Part2(puzzle tools.AoCPuzzle) interface{} {
return "d25p2 always only checks if you solved everything else"
}

View File

@ -1,2 +1,2 @@
Hit Points: 55 Hit Points: 14
Damage: 8 Damage: 8

View File

@ -25,6 +25,7 @@ import (
"aoc2015/day22" "aoc2015/day22"
"aoc2015/day23" "aoc2015/day23"
"aoc2015/day24" "aoc2015/day24"
"aoc2015/day25"
"flag" "flag"
"fmt" "fmt"
"os" "os"
@ -66,6 +67,7 @@ func initDayFunctions() {
22: {1: day22.Part1, 2: day22.Part2}, 22: {1: day22.Part1, 2: day22.Part2},
23: {1: day23.Part1, 2: day23.Part2}, 23: {1: day23.Part1, 2: day23.Part2},
24: {1: day24.Part1, 2: day24.Part2}, 24: {1: day24.Part1, 2: day24.Part2},
25: {1: day25.Part1, 2: day25.Part2},
} }
} }