adventofcode/2018/day24/army.go

211 lines
4.1 KiB
Go

package main
import (
"fmt"
"strings"
)
const (
ArmyTypeImmune = 1 << iota
ArmyTypeInfection
)
type Army struct {
id int
tp int
units int
hp int
immunities []string
weaknesses []string
damageType string
strength int
init int
target *Army
targettedBy *Army
}
func NewArmy(inp string, tp, id int) *Army {
a := new(Army)
a.tp = tp
a.id = id
// Pull the parenthetical out, if one is there
var prnth, other string
var inPrnth bool
var ptsOfOther int
for _, v := range strings.Fields(inp) {
if len(v) > 0 && v[len(v)-1] == ')' {
prnth = prnth + " " + v
inPrnth = false
continue
} else if len(v) > 0 && v[0] == '(' {
inPrnth = true
prnth = v
continue
}
if inPrnth {
prnth = prnth + " " + v
} else {
if len(other) > 0 {
other = other + " "
}
other = other + v
ptsOfOther++
}
}
_, err := fmt.Sscanf(other, "%d units each with %d hit points with an attack that does %d %s damage at initiative %d", &a.units, &a.hp, &a.strength, &a.damageType, &a.init)
if err != nil {
panic(err)
}
// Now parse out immunities and weaknesses
if len(prnth) > 3 {
prnth = prnth[1 : len(prnth)-1]
var inImmune bool
for _, v := range strings.Fields(prnth) {
if v == "immune" {
inImmune = true
continue
} else if v == "weak" {
inImmune = false
continue
}
if v == "to" {
continue
}
if v[len(v)-1] == ';' || v[len(v)-1] == ',' {
v = v[:len(v)-1]
}
if inImmune {
a.immunities = append(a.immunities, v)
} else {
a.weaknesses = append(a.weaknesses, v)
}
}
}
return a
}
func (a *Army) IsImmuneTo(val string) bool {
for _, v := range a.immunities {
if v == val {
return true
}
}
return false
}
func (a *Army) IsWeakTo(val string) bool {
for _, v := range a.weaknesses {
if v == val {
return true
}
}
return false
}
func (a *Army) Power() int {
return a.units * a.strength
}
func (a *Army) FindTarget(group []*Army) {
var tgt *Army
var tgtDmg int
for _, v := range group {
if v.targettedBy != nil {
continue
}
wrkDmg := a.CalculateDamage(v)
if tgt == nil || wrkDmg > tgtDmg {
tgt = v
tgtDmg = wrkDmg
} else if wrkDmg == tgtDmg {
if v.Power() > tgt.Power() {
tgt = v
tgtDmg = wrkDmg
} else if v.Power() == tgt.Power() {
if v.init > tgt.init {
tgt = v
tgtDmg = wrkDmg
}
}
}
if Debug {
var tpTxt string
if a.tp == ArmyTypeImmune {
tpTxt = "Immune System"
} else {
tpTxt = "Infection"
}
fmt.Printf("%s group %d would deal defending group %d %d damage\n", tpTxt, a.id, tgt.id, tgtDmg)
}
}
if tgt != nil {
a.target = tgt
tgt.targettedBy = a
}
}
func (a *Army) CalculateDamage(b *Army) int {
if b.IsWeakTo(a.damageType) {
return a.Power() * 2
} else if b.IsImmuneTo(a.damageType) {
return 0
}
return a.Power()
}
func (a *Army) Damage(b *Army) int {
var unitsDefeated int
dmg := a.CalculateDamage(b)
for dmg > 0 {
dmg -= b.hp
if dmg > 0 && b.units > 0 {
b.units--
unitsDefeated++
}
}
b.targettedBy = nil
return unitsDefeated
}
func (a *Army) Fight() {
if a.target != nil {
var tpTxt string
if a.tp == ArmyTypeImmune {
tpTxt = "Immune System"
} else if a.tp == ArmyTypeInfection {
tpTxt = "Infection"
}
dmg := a.Damage(a.target)
if Debug {
fmt.Printf("%s group %d attacks defending group %d, killing %d units\n", tpTxt, a.id, a.target.id, dmg)
}
}
}
func (a Army) String() string {
ret := fmt.Sprintf("%d units each with %d hit points ", a.units, a.hp)
if len(a.weaknesses) > 0 {
ret = ret + fmt.Sprintf("(weaknesses: %s) ", a.weaknesses)
}
if len(a.immunities) > 0 {
ret = ret + fmt.Sprintf("(immunities: %s) ", a.immunities)
}
ret = ret + fmt.Sprintf("with an attack that does %d %s damage at initiative %d\n",
a.strength, a.damageType, a.init,
)
return ret
}
// Army Sorting
type ByPower []*Army
func (b ByPower) Len() int { return len(b) }
func (b ByPower) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b ByPower) Less(i, j int) bool {
if b[i].Power() == b[j].Power() {
return b[i].init < b[j].init
}
return b[i].Power() < b[j].Power()
}