day22; finally
This commit is contained in:
parent
3b22bfcc37
commit
7b7f4f563b
264
day22/day.go
264
day22/day.go
@ -1,126 +1,183 @@
|
||||
package day22
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"tools"
|
||||
)
|
||||
|
||||
type Effect struct {
|
||||
damage int
|
||||
armor int
|
||||
mana int
|
||||
timer int
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
/*
|
||||
Spells:
|
||||
Magic Missile - 53 Mana - 4 Damage
|
||||
Drain - 73 Mana - 2 Damage, 2 Heal
|
||||
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
|
||||
*/
|
||||
|
||||
type Entity struct {
|
||||
hitpoints int
|
||||
mana int
|
||||
damage int
|
||||
armor int
|
||||
damage int
|
||||
}
|
||||
|
||||
func (e Entity) copy() *Entity {
|
||||
return &Entity{e.hitpoints, e.mana, e.damage, e.armor}
|
||||
var spells = map[string]string{
|
||||
"mm": "Magic Missile",
|
||||
"d": "Drain",
|
||||
"s": "Shield",
|
||||
"rc": "Recharge",
|
||||
"p": "Poison",
|
||||
}
|
||||
var minWinningMana int
|
||||
|
||||
func (e Entity) printStats() {
|
||||
fmt.Println("HP:", e.hitpoints, "Mana:", e.mana, "Damage:", e.damage, "Armor:", e.armor)
|
||||
func applyEffects(player, boss Entity, effects map[string]int) (Entity, Entity, map[string]int) {
|
||||
for e, t := range effects {
|
||||
t--
|
||||
switch e {
|
||||
case "rc":
|
||||
player.mana += 101
|
||||
// fmt.Println("Recharge provides 101 mana; it's timer is now ", t)
|
||||
case "p":
|
||||
boss.hitpoints -= 3
|
||||
// fmt.Println("Poison deals 3 damage; it's timer is now ", t)
|
||||
case "s":
|
||||
if t == 0 {
|
||||
player.armor -= 7
|
||||
}
|
||||
|
||||
var spells = map[string]Spell{
|
||||
"Magic Missile": {53, 4, 0, Effect{}},
|
||||
"Drain": {73, 2, 2, Effect{}},
|
||||
"Shield": {113, 0, 0, Effect{0, 7, 0, 6}},
|
||||
"Poison": {173, 0, 0, Effect{3, 0, 0, 6}},
|
||||
"Recharge": {229, 0, 0, Effect{0, 0, 101, 5}},
|
||||
}
|
||||
|
||||
func applyEffects(player, boss *Entity) {
|
||||
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("Regen Mana")
|
||||
player.mana += eData.mana
|
||||
case "Poison":
|
||||
fmt.Println("Apply Poison")
|
||||
boss.hitpoints -= eData.damage
|
||||
}
|
||||
activeEffects[eName].timer--
|
||||
if eData.timer == 0 {
|
||||
fmt.Println(eName, "wears off.")
|
||||
delete(activeEffects, eName)
|
||||
// fmt.Println("Shield's timer is now ", t)
|
||||
}
|
||||
if t == 0 {
|
||||
// fmt.Println(spells[e], " wears off.")
|
||||
delete(effects, e)
|
||||
} else {
|
||||
effects[e] = t
|
||||
}
|
||||
}
|
||||
|
||||
func fightRound(player, boss *Entity, spellName string) (outcome int) {
|
||||
// outcome will be -1 on boss win, 0 on both living and 1 on player win
|
||||
spell := spells[spellName]
|
||||
|
||||
applyEffects(player, boss)
|
||||
if player.mana < spell.cost {
|
||||
return -1
|
||||
}
|
||||
player.mana -= spell.cost
|
||||
player.hitpoints += spell.heal
|
||||
boss.hitpoints -= spell.damage
|
||||
if _, ok := activeEffects[spellName]; !ok && spell.applyEffect.timer > 0 {
|
||||
activeEffects[spellName] = spell.applyEffect.copy()
|
||||
return player, boss, effects
|
||||
}
|
||||
|
||||
if boss.hitpoints <= 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
applyEffects(player, boss)
|
||||
if boss.hitpoints <= 0 {
|
||||
return 1
|
||||
}
|
||||
player.hitpoints -= tools.Max(1, boss.damage-player.armor)
|
||||
func turn(player, boss Entity, effects map[string]int, spell string, hardmode bool) (Entity, Entity, map[string]int, int, int) {
|
||||
// return values: -1 player dead; 0 both alive; 1 boss dead
|
||||
/*
|
||||
fmt.Println("-- Player Turn --")
|
||||
fmt.Println("- Player has ", player.hitpoints, "hit points, ", player.armor, " armor, ", player.mana, " mana")
|
||||
fmt.Println("- Boss has ", boss.hitpoints, " hit points")
|
||||
*/
|
||||
if hardmode {
|
||||
player.hitpoints -= 1
|
||||
if player.hitpoints <= 0 {
|
||||
return -1
|
||||
return player, boss, effects, 0, -1
|
||||
}
|
||||
}
|
||||
// Apply effects, if any
|
||||
player, boss, effects = applyEffects(player, boss, effects)
|
||||
// Player cast spell -- which one?
|
||||
if _, ok := effects[spell]; ok {
|
||||
// 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
|
||||
}
|
||||
|
||||
return 0
|
||||
// Check Boss health
|
||||
if boss.hitpoints <= 0 {
|
||||
return player, boss, effects, manaUsed, 1
|
||||
}
|
||||
|
||||
func minManaToTheDeath(player, boss *Entity) int {
|
||||
manaUsed := 0
|
||||
minManaUsed := math.MaxInt64
|
||||
/*
|
||||
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 {
|
||||
return player, boss, effects, manaUsed, 1
|
||||
}
|
||||
|
||||
return manaUsed
|
||||
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 {
|
||||
return player, boss, effects, manaUsed, -1
|
||||
}
|
||||
|
||||
return player, boss, effects, manaUsed, 0
|
||||
}
|
||||
|
||||
func findLowestManaUsed(player, boss Entity, effects map[string]int, manaUsed int, spellsUsed string, hardmode bool, depth int) (int, string) {
|
||||
minManaUsed := math.MaxInt32
|
||||
var minSpellsUsed string
|
||||
for s := range spells {
|
||||
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{} {
|
||||
activeEffects = make(map[string]*Effect)
|
||||
player := Entity{50, 500, 0, 0}
|
||||
boss := Entity{0, 0, 0, 0}
|
||||
bossdata := puzzle.GetInputArray()
|
||||
for _, line := range bossdata {
|
||||
bossData := puzzle.GetInputArray()
|
||||
for _, line := range bossData {
|
||||
parts := strings.Split(line, ": ")
|
||||
switch parts[0] {
|
||||
case "Hit Points":
|
||||
@ -130,19 +187,30 @@ func Part1(puzzle tools.AoCPuzzle) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
player.printStats()
|
||||
boss.printStats()
|
||||
fmt.Println("-----")
|
||||
for _, s := range []string{"Recharge", "Shield", "Drain", "Poison", "Magic Missile"} {
|
||||
fightRound(&player, &boss, s)
|
||||
player.printStats()
|
||||
boss.printStats()
|
||||
fmt.Println("-----")
|
||||
}
|
||||
effects := make(map[string]int)
|
||||
minWinningMana = math.MaxInt32
|
||||
|
||||
return 0
|
||||
findLowestManaUsed(player, boss, effects, 0, "", false, 0)
|
||||
return minWinningMana
|
||||
}
|
||||
|
||||
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
36
day25/day.go
Normal 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"
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
Hit Points: 55
|
||||
Hit Points: 14
|
||||
Damage: 8
|
||||
2
main.go
2
main.go
@ -25,6 +25,7 @@ import (
|
||||
"aoc2015/day22"
|
||||
"aoc2015/day23"
|
||||
"aoc2015/day24"
|
||||
"aoc2015/day25"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -66,6 +67,7 @@ func initDayFunctions() {
|
||||
22: {1: day22.Part1, 2: day22.Part2},
|
||||
23: {1: day23.Part1, 2: day23.Part2},
|
||||
24: {1: day24.Part1, 2: day24.Part2},
|
||||
25: {1: day25.Part1, 2: day25.Part2},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user