It's another iterative simulation with a bunch of details to implement. I chose to do it completely functionally with immutable data, with the overall battle state updated on each step and threaded through the entire calculation. As before I simulated the whole battle as a sequence of states, and the part 1 result can be obtained from the last in this sequence:

The fight() function must therefore compute the next state for the battle. First it does target selection, the result of which is a map of attacking groups IDs to defending groups IDs. I used IDs as the data objects are immutable and will be getting replaced with modified versions as the battle proceeds.

Target selection needs a set of unselected groups and the set of selections to be threaded through the computation so I wrapped both in a container data structure and gave it a helper function for adding new selections:

Once we've done the target selection we run the fight. We take the target selection, order the attackers by initiative and put them back into a (now ordered) list of attacker, defender pairs:

Running the attacks is pretty simple but we have to be careful to use the current state at each step. groups is the set of Army Groups at the beginning of the fight round, groups_ is the set at the current point in the attack sequence. One nice aspect of this style of design is that by swapping those around it's trivial to change the behaviour from sequential to parallel (i.e. all attacks happen at once), should that requirement change.

orderedAttackIds.fold(groups){groups_,(attackingGroupId,defendingGroupId)->valattackingGroup=groups_.find(attackingGroupId)valdefendingGroup=groups_.find(defendingGroupId)if(attackingGroup==null||defendingGroup==null)// might be eliminatedgroups_else{valotherGroups=groups_.filterNot{g->g.id==defendingGroupId}.toSet()valdamage=defendingGroup.damageFrom(attackingGroup.unit.attack)*attackingGroup.countvalunitsKilled=minOf(damage/defendingGroup.unit.hitPoints,defendingGroup.count)if(unitsKilled==defendingGroup.count)otherGroupselseotherGroups+(defendingGroup-unitsKilled)}}

## re: AoC Day 24: Immune System Simulator 20XX VIEW POST

TOP OF THREAD FULL DISCUSSION## Part 1

It's another iterative simulation with a bunch of details to implement. I chose to do it completely functionally with immutable data, with the overall battle state updated on each step and threaded through the entire calculation. As before I simulated the whole battle as a sequence of states, and the part 1 result can be obtained from the last in this sequence:

The

`fight()`

function must therefore compute the next state for the battle. First it does target selection, the result of which is a map of attacking groups IDs to defending groups IDs. I used IDs as the data objects are immutable and will be getting replaced with modified versions as the battle proceeds.Target selection needs a set of unselected groups and the set of selections to be threaded through the computation so I wrapped both in a container data structure and gave it a helper function for adding new selections:

Selecting targets and fighting involves a few sorts and orderings so I used Kotlin Comparators again:

These depends on a couple of simple helpers which capture the business logic quite neatly:

Building the target selection now becomes:

Once we've done the target selection we run the fight. We take the target selection, order the attackers by initiative and put them back into a (now ordered) list of attacker, defender pairs:

Running the attacks is pretty simple but we have to be careful to use the

current stateat each step.`groups`

is the set of Army Groups at the beginning of the fight round,`groups_`

is the set at the current point in the attack sequence. One nice aspect of this style of design is that by swapping those around it's trivial to change the behaviour from sequential to parallel (i.e. all attacks happen at once), should that requirement change.