Your query for the programming is what I suggested originally. I had previously mentioned pre-calculated results of battles, but it’s pretty simple from there to throw in some good dynamic programming that would make it even faster. I’ll describe an algorithm below.
First, the rules for LowLuck are that all units are grouped together regardless of type. So in the example you gave (7 infantry and 3 armor attacking 15 infantry) the attacker has a strength of (7+33=) 16 and the defender a strength of (152=)30, so the attacker has (16 / 6 =)2Â guaranteed hits and rolls one die to determine if he gets an additional hit if the result is (16 mod 6=)4 or less . The defender has (30/6=)5 guaranteed hits and does not need to roll any dice.
You’re right in that subs are treated special; the attacker has a choice in how he wants to use his subs in a naval battle: he can choose to either use their first strike ability or forgo it for each sub on each round. So for instance, in the example you give (3 ftrs 2 subs vs 1 BB 5 subs) the 2 attacking subs can choose to take their first strike at a strength of 4, so they would roll one die, and then the fighters would attack as normal at a strength of 9 so they get one guaranteed hit and roll one die at a strength of 3. Or they could choose to use one sub for its first strike ability at a strength of 2, and use the other sub like normal with the fighters at a strength of 11, so that way having one guaranteed hit and roll one die at a strength of 5 for another. Or they could forgo the first strike completely (for this round) and then their total strength would be 13; they would get two guaranteed hits and roll one die to hit on a 1.
Now, I found this rather interesting: I tried out some sample battles on DAAK’s LowLuck server and got some very interesting results!
Battle 1: 5 fighters vs 20 subs - Attacker has 2 guaranteed hits and rolls 1 die at 3 for another hit. The defender does not roll because his subs cannot hit back.
Battle 2: 5 fighters, 1 sub vs 20 subs (Attacker does not use first strike) - Attacker has 2 guaranteed hits, and rolls 1 die at 5 for another hit. The defender has 6 guaranteed hits and rolls 1 die at 4 for another, BUT is only able to sink the sub, so the remaining “hits” are wasted.
Battle 3: 5 fighters vs 20 subs, 1 trn - Attacker has 2 guaranteed hits and rolls 1 die at 3 for another; the subs cannot fire but the transport can, so the defender rolls 1 die at 1 (no guaranteed hits).
Battle 4: 5 fighters, 1 sub vs 20 subs, 1 trn (Attacker does not use first strike) - Now this is the intesting one! All the ones above seemed to make sense, but this one defies reason. Attacker has 2 guaranteed hits and rolls 1 die at 5 for another hit - so far so good. The defender has 6 guaranteed hits and rolls 1 die at 5 for another, AND THE HITS ARE NOT WASTED! The defender wins the battle, killing all five fighters even though it is obvious that the fighters could not have all been hit by the single transport.
Maybe this is a bug in DAAK’s LL roller : I will submit it to them and see what they say. I’m sure this situation doesn’t come up that often of course, since it’s not very likely for anyone to amass subs like that.
Anyway, let me propose to you a recursive algorithm for computing land battles without AA guns and you can generalize pretty easily I’d think. For AA guns, simply add a single round of combat in which no units fire but the AA gun does, and for naval combats, perhaps you could include a couple option boxes reflecting the strategy for how subs are to be used: “Always use first strike” “Never use first strike” “Use first strike only if there are enough subs for a guaranteed hit, and use remaining subs in regular combat” “Save enough subs for regular combat to bring the combat strength up to either a 5 or 6, and use remaining subs for first strike”.
1. Test whether the current battle is an “end case”: that is, whether the total strength of both attacker and defender is less than 6. If so, assign a precalculated probability to all outcomes that you can load from a file, and return these values.
2. Test whether this battle has already been calculated previously while running this algorithm. If so, load the results and return these values.
2. Analyze the possible outcomes of the current round of battle and assign each of the (up to 4) different outcomes a probability, so that the sum of the probabilities is 100%
3. Recursively call this algorithm on each of these outcomes, and multiply their return values for the final outcomes they predict by the probability for that individual outcome you computed in step 2.
4. Return the result, adding probabilities for any identical final outcomes.
Note: Steps 1 and 2 are distinct; step 1 refers to the 25 different “infinite” battles I referred to above, whereas step 2 is a dynamic programming method of caching already determined results that will significantly prune the tree you are searching.
Take as an example this battle: 3 inf 3 arm vs 6 inf
Note that my algorithm above corresponds more closely to a DFS approach, but I will be taking a BFS delineation of it to simplify for the reader.
The possible results after the first round are:
1 inf 3 arm vs 4 inf (100%)
There is only one result, so we recursively look at it. The possible results for the second round are:
3 arm vs 3 inf (2/9 chance)
3 arm vs 2 inf (4/9 chance)
2 arm vs 3 inf (1/9 chance)
2 arm vs 2 inf (2/9 chance)
So we recusrively look at each of these.
The outcomes of the (2/9 chance) 3 arm vs 3 inf battle are:
3 arm vs 2 inf (1/4 chance)
3 arm vs 1 inf (1/4 chance)
2 arm vs 2 inf (1/4 chance)
2 arm vs 1 inf (1/4 chance)
The outcomes of the (4/9 chance) 3 arm vs 2 inf battle are:
3 arm vs 1 inf (1/3 chance)
3 arm vs 0 inf (1/3 chance) - battle over
2 arm vs 1 inf (1/6 chance)
2 arm vs 0 inf (1/6 chance) - battle over
The outcomes of the (1/9 chance) 2 arm vs 3 inf battle are:
2 arm vs 2 inf (1/2 chance)
1 arm vs 2 inf (1/2 chance) - end case
The outcomes of the (2/9 chance) 2 arm vs 2 inf battle are:
2 arm vs 1 inf (2/3 chance)
1 arm vs 1 inf (1/3 chance) - end case
At this point, to eliminate step (2) in my algorithm, let’s simply add up the common outcomes:
3 arm vs 2 inf (1/4 * 2/9)
3 arm vs 1 inf (1/4 * 2/9) + (1/3 * 4/9)
2 arm vs 2 inf (1/4 * 2/9) + (1/2 * 1/9)
2 arm vs 1 inf (1/4 * 2/9) + (1/6 * 4/9) + (2/3 * 2/9)
1 arm vs 2 inf (1/2 * 1/9) - end case
1 arm vs 1 inf (1/3 * 2/9) - end case
3 arm vs 0 inf (1/3 * 4/9) - battle over
2 arm vs 0 inf (1/6 * 4/9) - battle over
So, you can see how this will end up, right? The two end cases present would be calculated in advance, so there is no need to calculate a potentially infinite battle. This algorithm would work to calculate any battle in a relatively short amount of time, particularly since you could even store outcomes in a file and load them in step 2 between battles and then the more you use this, the easier it becomes for the program to calculate battles!