From 10af09b200e323fddae6b77e8dc2af34c5f9aab2 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Fri, 8 Nov 2019 11:52:19 -0600 Subject: [PATCH] 2018 day 24 More work --- 2018/day24/army.go | 200 +++++++++++++++++++++++++++++++++++-------- 2018/day24/combat.go | 76 ++++++++++++++-- 2018/day24/day24.go | 37 ++++---- 2018/day24/testinput | 7 ++ 4 files changed, 263 insertions(+), 57 deletions(-) create mode 100644 2018/day24/testinput diff --git a/2018/day24/army.go b/2018/day24/army.go index 2dedcc9..2afa327 100644 --- a/2018/day24/army.go +++ b/2018/day24/army.go @@ -1,19 +1,33 @@ package main +import ( + "fmt" + "strings" +) + +const ( + ArmyTypeImmune = 1 << iota + ArmyTypeInfection +) + type Army struct { - tp int - units int - hp int - immunities []string - weaknesses []string - damageType string - strength int - init int + 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 int) *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 @@ -43,38 +57,154 @@ func NewArmy(inp string, tp int) *Army { 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 - } else if v == "weak" { - inImmune = false - } - 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) - } - } - } + // 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) } +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) { b[i].Power() < b[j].Power() } +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() +} diff --git a/2018/day24/combat.go b/2018/day24/combat.go index 4cd5e2a..e5248cd 100644 --- a/2018/day24/combat.go +++ b/2018/day24/combat.go @@ -1,10 +1,72 @@ package main -// All Battle Logic is in here -func Battle(army1, army2 []Army) { - allCombatants = append(army1, army2...) - sort.Sort(ByPower(allCombatants)) - for _, v := range allCombatants { - fmt.Print("%d %d %d (%d)\n", v.tp, v.units, v.hp, v.Power()) - } +import ( + "fmt" + "sort" +) + +type Battle struct { + immune []*Army + infect []*Army +} + +func NewBattle(immune, infect []*Army) *Battle { + return &Battle{ + immune: immune, + infect: infect, + } +} + +func (b *Battle) PrintStatus() { + fmt.Println("Immune System:") + for _, v := range b.immune { + fmt.Printf("Group %d contains %d units\n", v.id, v.units) + } + fmt.Println("Infection:") + for _, v := range b.infect { + fmt.Printf("Group %d contains %d units\n", v.id, v.units) + } +} + +// All Battle Logic is in here +func (b *Battle) Fight() { + allCombatants := append(b.immune, b.infect...) + sort.Sort(ByPower(allCombatants)) + for k := len(allCombatants) - 1; k >= 0; k-- { + b.FindTargetFor(allCombatants[k]) + } + if Debug { + fmt.Println("") + } + for k := len(allCombatants) - 1; k >= 0; k-- { + allCombatants[k].Fight() + } + i := 0 + for _, x := range b.immune { + if x.units > 0 { + b.immune[i] = x + i++ + } + } + b.immune = b.immune[:i] + i = 0 + for _, x := range b.infect { + if x.units > 0 { + b.infect[i] = x + i++ + } + } + b.infect = b.infect[:i] +} + +func (b *Battle) FindTargetFor(a *Army) { + if a.tp == ArmyTypeImmune { + a.FindTarget(b.infect) + } else if a.tp == ArmyTypeInfection { + a.FindTarget(b.immune) + } +} + +func (b *Battle) IsOver() bool { + return len(b.immune) == 0 || len(b.infect) == 0 } diff --git a/2018/day24/day24.go b/2018/day24/day24.go index f656dde..1cad1a4 100644 --- a/2018/day24/day24.go +++ b/2018/day24/day24.go @@ -4,43 +4,50 @@ import ( "bufio" "fmt" "os" - "strings" ) const ( - ArmyTypeImmune = 1 << iota - ArmyTypeInfection + Debug = true ) func main() { inp := StdinToStringSlice() immune, infect := ParseInput(inp) - for _, v := range immune { - fmt.Println("++ ", v.immunities) - fmt.Println("-- ", v.weaknesses) - } - _, _ = immune, infect + + b := NewBattle(immune, infect) + //for !b.IsOver() { + if Debug { + b.PrintStatus() + fmt.Println("") + } + b.Fight() + //} + + fmt.Println("") + fmt.Println("The battle is over!") + b.PrintStatus() } -func ParseInput(inp []string) ([]Army, []Army) { - var immune, infection []Army - var tp int +func ParseInput(inp []string) ([]*Army, []*Army) { + var immune, infection []*Army + var tp, id int for _, v := range inp { if v == "" { continue } if v == "Immune System:" { - tp = ArmyTypeImmune + tp, id = ArmyTypeImmune, 0 continue } else if v == "Infection:" { - tp = ArmyTypeInfection + tp, id = ArmyTypeInfection, 0 continue } + id++ switch tp { case ArmyTypeImmune: - immune = append(immune, *NewArmy(v, tp)) + immune = append(immune, NewArmy(v, tp, id)) case ArmyTypeInfection: - infection = append(infection, *NewArmy(v, tp)) + infection = append(infection, NewArmy(v, tp, id)) } } return immune, infection diff --git a/2018/day24/testinput b/2018/day24/testinput new file mode 100644 index 0000000..7a474c9 --- /dev/null +++ b/2018/day24/testinput @@ -0,0 +1,7 @@ +Immune System: +17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2 +989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3 + +Infection: +801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1 +4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4