Finished attack funnel. Now implementing defence funnel
This commit is contained in:
parent
434b534012
commit
440c9dd9d5
2 changed files with 63 additions and 6 deletions
|
@ -6,8 +6,6 @@ import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple2
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class AttackScenario {
|
||||
private static final int PROBABILITY_DENOMINATOR_D6 = 6;
|
||||
|
@ -96,13 +94,26 @@ public class AttackScenario {
|
|||
Weapon weapon = weaponData._1;
|
||||
int amountOfWeapons = weaponData._2;
|
||||
|
||||
// Attack probabilities
|
||||
// Attack funnel
|
||||
var passingHits = calculatePassingHits(weapon, amountOfWeapons);
|
||||
var passingWounds = calculatePassingWounds(weapon, amountOfWeapons, passingHits);
|
||||
var normalWounds = passingWounds._1;
|
||||
var mortalWounds = passingWounds._2;
|
||||
|
||||
// Defence funnel
|
||||
executeDefenceFunnel(weapon, normalWounds, mortalWounds);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void executeDefenceFunnel(Weapon weapon, double normalWounds, double mortalWounds) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a tuple of normal passing wounds and mortal wounds so that the caller can handle them differently.
|
||||
*/
|
||||
private Tuple2<Double, Double> calculatePassingWounds(Weapon weapon, int amountOfWeapons, double passingHits) {
|
||||
double lethalHitsCount = calculateLethalHits(weapon, amountOfWeapons);
|
||||
var normalHits = passingHits - lethalHitsCount;
|
||||
|
@ -111,8 +122,38 @@ public class AttackScenario {
|
|||
int woundRollGoal = calculateWoundRollGoal(weapon);
|
||||
int woundSuccessProbabilityNumerator = calculateProbabilityNumeratorFrom(woundRollGoal);
|
||||
|
||||
var passingWounds = normalHits * ( (double) woundSuccessProbabilityNumerator / PROBABILITY_DENOMINATOR_D6)
|
||||
var passingNormalWounds = normalHits * ( (double) woundSuccessProbabilityNumerator / PROBABILITY_DENOMINATOR_D6);
|
||||
results.setWeaponStat(weapon, WeaponStat.WOUNDS_COUNT_WITHOUT_REROLL, passingNormalWounds+lethalHitsCount);
|
||||
var woundsRerolls = prepareWoundRerolls(woundRollGoal);
|
||||
if(woundsRerolls.isPresent()) {
|
||||
var rerolls = woundsRerolls.get();
|
||||
var failedWounds = normalHits * ((double) (1 - woundSuccessProbabilityNumerator) / PROBABILITY_DENOMINATOR_D6);
|
||||
var extraWoundsFromRerolls = failedWounds * rerolls.getProbabilityNumerator() / PROBABILITY_DENOMINATOR_D6;
|
||||
results.setWeaponStat(weapon, WeaponStat.WOUNDS_COUNT_FROM_REROLL, extraWoundsFromRerolls);
|
||||
passingNormalWounds += extraWoundsFromRerolls;
|
||||
}
|
||||
|
||||
// Mortal wounds
|
||||
var passingMortalWounds = 0d;
|
||||
if(weapon.hasDevastatingWounds()) {
|
||||
var criticalWoundRollGoal = weapon.getBestAntiFor(defensiveProfile)
|
||||
.orElse(criticalWoundValue);
|
||||
var criticalProbability = calculateProbabilityNumeratorFrom(criticalWoundRollGoal);
|
||||
passingMortalWounds += (normalHits*criticalProbability/PROBABILITY_DENOMINATOR_D6);
|
||||
if(fishCriticalWounds && hasRerollWoundsAny) {
|
||||
passingMortalWounds += (normalHits * (1-criticalProbability)/PROBABILITY_DENOMINATOR_D6)*criticalProbability/PROBABILITY_DENOMINATOR_D6;
|
||||
}
|
||||
|
||||
results.setWeaponStat(weapon, WeaponStat.MORTAL_WOUNDS_COUNT, passingMortalWounds);
|
||||
// passing wounds was calculated from the whole, but we must substract the critical wounds from the whole
|
||||
// to be able to handle them separately, as mortal wounds replace normal wounds.
|
||||
passingNormalWounds -= passingMortalWounds;
|
||||
}
|
||||
|
||||
passingNormalWounds += lethalHitsCount;
|
||||
results.setWeaponStat(weapon, WeaponStat.WOUNDS_TOTAL_COUNT, passingNormalWounds+passingMortalWounds);
|
||||
|
||||
return Tuple.of(passingNormalWounds, passingMortalWounds);
|
||||
}
|
||||
|
||||
private double calculateLethalHits(Weapon weapon, int amountOfWeapons) {
|
||||
|
@ -143,7 +184,9 @@ public class AttackScenario {
|
|||
var hitRerolls = prepareHitRerolls(hitRollGoal);
|
||||
if(hitRerolls.isPresent()) {
|
||||
var rerolls = hitRerolls.get();
|
||||
passingHits += rerolls.getProbabilityNumerator() * ((double) hitSuccessProbabilityNumerator / PROBABILITY_DENOMINATOR_D6);
|
||||
var successfulHitFromRerolls = rerolls.getProbabilityNumerator() * ((double) hitSuccessProbabilityNumerator / PROBABILITY_DENOMINATOR_D6);
|
||||
results.setWeaponStat(weapon, WeaponStat.HITS_COUNT_FROM_REROLL, successfulHitFromRerolls);
|
||||
passingHits += successfulHitFromRerolls;
|
||||
}
|
||||
results.setWeaponStat(weapon, WeaponStat.HITS_TOTAL_COUNT, passingHits);
|
||||
|
||||
|
@ -161,6 +204,17 @@ public class AttackScenario {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<Reroll> prepareWoundRerolls(int woundRollGoal) {
|
||||
if(hasRerollWoundsOnes) {
|
||||
return Optional.of(Reroll.rerollOnOnes());
|
||||
}
|
||||
if(hasRerollWoundsFailures || hasRerollWoundsAny) {
|
||||
return Optional.of(Reroll.rerollFailures(woundRollGoal));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private double calculateWeaponAttacks(Weapon weapon, int amountOfWeapons) {
|
||||
double weaponAttacks = calculateBasicWeaponAttacks(weapon, amountOfWeapons);
|
||||
results.setWeaponStat(weapon, WeaponStat.POTENTIAL_HITS_COUNT, weaponAttacks);
|
||||
|
|
|
@ -5,9 +5,12 @@ import lombok.Getter;
|
|||
@Getter
|
||||
public enum WeaponStat {
|
||||
HITS_COUNT_WITHOUT_REROLL(Double.class),
|
||||
HITS_COUNT_FROM_REROLL(Double.class),
|
||||
HITS_TOTAL_COUNT(Double.class),
|
||||
POTENTIAL_HITS_COUNT(Double.class),
|
||||
WOUNDS_COUNT(Double.class),
|
||||
WOUNDS_TOTAL_COUNT(Double.class),
|
||||
WOUNDS_COUNT_WITHOUT_REROLL(Double.class),
|
||||
WOUNDS_COUNT_FROM_REROLL(Double.class),
|
||||
MORTAL_WOUNDS_COUNT(Double.class),
|
||||
TOTAL_DAMAGE(Double.class),
|
||||
LETHAL_HITS_COUNT(Double.class),
|
||||
|
|
Loading…
Add table
Reference in a new issue