use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class Fire method selectCasualties.
private void selectCasualties(final IDelegateBridge bridge) {
final int hitCount = m_dice.getHits();
AbstractBattle.getDisplay(bridge).notifyDice(m_dice, m_stepName);
final int countTransports = CollectionUtils.countMatches(m_attackableUnits, Matches.unitIsTransport().and(Matches.unitIsSea()));
if (countTransports > 0 && isTransportCasualtiesRestricted(bridge.getData())) {
final CasualtyDetails message;
final Collection<Unit> nonTransports = CollectionUtils.getMatches(m_attackableUnits, Matches.unitIsNotTransportButCouldBeCombatTransport().or(Matches.unitIsNotSea()));
final Collection<Unit> transportsOnly = CollectionUtils.getMatches(m_attackableUnits, Matches.unitIsTransportButNotCombatTransport().and(Matches.unitIsSea()));
final int numPossibleHits = AbstractBattle.getMaxHits(nonTransports);
// more hits than combat units
if (hitCount > numPossibleHits) {
int extraHits = hitCount - numPossibleHits;
final Collection<PlayerID> alliedHitPlayer = new ArrayList<>();
// find the players who have transports in the attackable pile
for (final Unit unit : transportsOnly) {
if (!alliedHitPlayer.contains(unit.getOwner())) {
alliedHitPlayer.add(unit.getOwner());
}
}
// Leave enough transports for each defender for overflows so they can select who loses them.
for (final PlayerID player : alliedHitPlayer) {
final Predicate<Unit> match = Matches.unitIsTransportButNotCombatTransport().and(Matches.unitIsOwnedBy(player));
final Collection<Unit> playerTransports = CollectionUtils.getMatches(transportsOnly, match);
final int transportsToRemove = Math.max(0, playerTransports.size() - extraHits);
transportsOnly.removeAll(CollectionUtils.getNMatches(playerTransports, transportsToRemove, Matches.unitIsTransportButNotCombatTransport()));
}
m_killed = nonTransports;
m_damaged = Collections.emptyList();
if (extraHits > transportsOnly.size()) {
extraHits = transportsOnly.size();
}
message = BattleCalculator.selectCasualties(m_stepName, m_hitPlayer, transportsOnly, m_allEnemyUnitsNotIncludingWaitingToDie, m_firingPlayer, m_allFriendlyUnitsNotIncludingWaitingToDie, m_isAmphibious, m_amphibiousLandAttackers, m_battleSite, m_territoryEffects, bridge, m_text, m_dice, !m_defending, m_battleID, m_isHeadless, extraHits, true);
m_killed.addAll(message.getKilled());
m_confirmOwnCasualties = true;
} else if (hitCount == numPossibleHits) {
// exact number of combat units
m_killed = nonTransports;
m_damaged = Collections.emptyList();
m_confirmOwnCasualties = true;
} else {
// less than possible number
message = BattleCalculator.selectCasualties(m_stepName, m_hitPlayer, nonTransports, m_allEnemyUnitsNotIncludingWaitingToDie, m_firingPlayer, m_allFriendlyUnitsNotIncludingWaitingToDie, m_isAmphibious, m_amphibiousLandAttackers, m_battleSite, m_territoryEffects, bridge, m_text, m_dice, !m_defending, m_battleID, m_isHeadless, m_dice.getHits(), true);
m_killed = message.getKilled();
m_damaged = message.getDamaged();
m_confirmOwnCasualties = message.getAutoCalculated();
}
} else {
// they all die
if (hitCount >= AbstractBattle.getMaxHits(m_attackableUnits)) {
m_killed = m_attackableUnits;
m_damaged = Collections.emptyList();
// everything died, so we need to confirm
m_confirmOwnCasualties = true;
} else {
// Choose casualties
final CasualtyDetails message;
message = BattleCalculator.selectCasualties(m_stepName, m_hitPlayer, m_attackableUnits, m_allEnemyUnitsNotIncludingWaitingToDie, m_firingPlayer, m_allFriendlyUnitsNotIncludingWaitingToDie, m_isAmphibious, m_amphibiousLandAttackers, m_battleSite, m_territoryEffects, bridge, m_text, m_dice, !m_defending, m_battleID, m_isHeadless, m_dice.getHits(), true);
m_killed = message.getKilled();
m_damaged = message.getDamaged();
m_confirmOwnCasualties = message.getAutoCalculated();
}
}
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class BattleCalculator method getLowLuckAaCasualties.
private static CasualtyDetails getLowLuckAaCasualties(final boolean defending, final Collection<Unit> planes, final Collection<Unit> defendingAa, final DiceRoll dice, final IDelegateBridge bridge, final boolean allowMultipleHitsPerUnit) {
{
final Set<Unit> duplicatesCheckSet1 = new HashSet<>(planes);
if (planes.size() != duplicatesCheckSet1.size()) {
throw new IllegalStateException("Duplicate Units Detected: Original List:" + planes + " HashSet:" + duplicatesCheckSet1);
}
final Set<Unit> duplicatesCheckSet2 = new HashSet<>(defendingAa);
if (defendingAa.size() != duplicatesCheckSet2.size()) {
throw new IllegalStateException("Duplicate Units Detected: Original List:" + defendingAa + " HashSet:" + duplicatesCheckSet2);
}
}
int hitsLeft = dice.getHits();
if (hitsLeft <= 0) {
return new CasualtyDetails();
}
// if we can damage units, do it now
final CasualtyDetails finalCasualtyDetails = new CasualtyDetails();
final GameData data = bridge.getData();
final Tuple<Integer, Integer> attackThenDiceSides = DiceRoll.getAAattackAndMaxDiceSides(defendingAa, data, !defending);
final int highestAttack = attackThenDiceSides.getFirst();
if (highestAttack < 1) {
return new CasualtyDetails();
}
final int chosenDiceSize = attackThenDiceSides.getSecond();
final Triple<Integer, Integer, Boolean> triple = DiceRoll.getTotalAaPowerThenHitsAndFillSortedDiceThenIfAllUseSameAttack(null, null, !defending, defendingAa, planes, data, false);
final boolean allSameAttackPower = triple.getThird();
// multiple HP units need to be counted multiple times:
final List<Unit> planesList = new ArrayList<>();
for (final Unit plane : planes) {
final int hpLeft = allowMultipleHitsPerUnit ? (UnitAttachment.get(plane.getType()).getHitPoints() - plane.getHits()) : (Math.min(1, UnitAttachment.get(plane.getType()).getHitPoints() - plane.getHits()));
for (int hp = 0; hp < hpLeft; ++hp) {
// if allowMultipleHitsPerUnit, then because the number of rolls exactly equals the hitpoints of all units,
// we roll multiple times for any unit with multiple hitpoints
planesList.add(plane);
}
}
// killing the air by groups does not work if the the attack power is different for some of the rolls
// also, killing by groups does not work if some of the aa guns have 'MayOverStackAA' and we have more hits than the
// total number of
// groups (including the remainder group)
// (when i mean, 'does not work', i mean that it is no longer a mathematically fair way to find casualties)
// find group size (if no groups, do dice sides)
final int groupSize;
if (allSameAttackPower) {
groupSize = chosenDiceSize / highestAttack;
} else {
groupSize = chosenDiceSize;
}
final int numberOfGroupsByDiceSides = (int) Math.ceil((double) planesList.size() / (double) groupSize);
final boolean tooManyHitsToDoGroups = hitsLeft > numberOfGroupsByDiceSides;
if (!allSameAttackPower || tooManyHitsToDoGroups || chosenDiceSize % highestAttack != 0) {
// we have too many hits, so just pick randomly
return randomAaCasualties(planes, dice, bridge, allowMultipleHitsPerUnit);
}
// if we have a group of 6 fighters and 2 bombers, and dicesides is 6, and attack was 1, then we would want 1
// fighter to die for sure.
// this is what groupsize is for.
// if the attack is greater than 1 though, and all use the same attack power, then the group size can be smaller
// (ie: attack is 2, and
// we have 3 fighters and 2 bombers, we would want 1 fighter to die for sure).
// categorize with groupSize
final Tuple<List<List<Unit>>, List<Unit>> airSplit = categorizeLowLuckAirUnits(planesList, groupSize);
// because we might have missed the dice roll to hit the remainder)
if (hitsLeft < (airSplit.getFirst().size() + ((int) Math.ceil((double) airSplit.getSecond().size() / (double) groupSize)))) {
// fewer hits than groups.
final List<Unit> tempPossibleHitUnits = new ArrayList<>();
for (final List<Unit> group : airSplit.getFirst()) {
tempPossibleHitUnits.add(group.get(0));
}
if (airSplit.getSecond().size() > 0) {
// if we have a remainder group, we need to add some of them into the mix
// but we have to do so randomly.
final List<Unit> remainders = new ArrayList<>(airSplit.getSecond());
if (remainders.size() == 1) {
tempPossibleHitUnits.add(remainders.remove(0));
} else {
final int numberOfRemainderGroups = (int) Math.ceil((double) remainders.size() / (double) groupSize);
final int[] randomRemainder = bridge.getRandom(remainders.size(), numberOfRemainderGroups, null, DiceType.ENGINE, "Deciding which planes should die due to AA fire");
int pos2 = 0;
for (final int element : randomRemainder) {
pos2 += element;
tempPossibleHitUnits.add(remainders.remove(pos2 % remainders.size()));
}
}
}
final int[] hitRandom = bridge.getRandom(tempPossibleHitUnits.size(), hitsLeft, null, DiceType.ENGINE, "Deciding which planes should die due to AA fire");
// now we find the
int pos = 0;
for (final int element : hitRandom) {
pos += element;
final Unit unitHit = tempPossibleHitUnits.remove(pos % tempPossibleHitUnits.size());
if (allowMultipleHitsPerUnit && (Collections.frequency(finalCasualtyDetails.getDamaged(), unitHit) < (getTotalHitpointsLeft(unitHit) - 1))) {
finalCasualtyDetails.addToDamaged(unitHit);
} else {
finalCasualtyDetails.addToKilled(unitHit);
}
}
} else {
// kill one in every group
for (final List<Unit> group : airSplit.getFirst()) {
final Unit unitHit = group.get(0);
if (allowMultipleHitsPerUnit && (Collections.frequency(finalCasualtyDetails.getDamaged(), unitHit) < (getTotalHitpointsLeft(unitHit) - 1))) {
finalCasualtyDetails.addToDamaged(unitHit);
} else {
finalCasualtyDetails.addToKilled(unitHit);
}
hitsLeft--;
}
// for any hits left over...
if (hitsLeft == airSplit.getSecond().size()) {
for (final Unit unitHit : airSplit.getSecond()) {
if (allowMultipleHitsPerUnit && (Collections.frequency(finalCasualtyDetails.getDamaged(), unitHit) < (getTotalHitpointsLeft(unitHit) - 1))) {
finalCasualtyDetails.addToDamaged(unitHit);
} else {
finalCasualtyDetails.addToKilled(unitHit);
}
}
} else if (hitsLeft != 0) {
// the remainder
// roll all at once to prevent frequent random calls, important for pbem games
final int[] hitRandom = bridge.getRandom(airSplit.getSecond().size(), hitsLeft, null, DiceType.ENGINE, "Deciding which planes should die due to AA fire");
int pos = 0;
for (final int element : hitRandom) {
pos += element;
final Unit unitHit = airSplit.getSecond().remove(pos % airSplit.getSecond().size());
if (allowMultipleHitsPerUnit && (Collections.frequency(finalCasualtyDetails.getDamaged(), unitHit) < (getTotalHitpointsLeft(unitHit) - 1))) {
finalCasualtyDetails.addToDamaged(unitHit);
} else {
finalCasualtyDetails.addToKilled(unitHit);
}
}
}
}
// double check
if (finalCasualtyDetails.size() != dice.getHits()) {
throw new IllegalStateException("wrong number of casulaties, expected:" + dice + " but got: " + finalCasualtyDetails);
}
return finalCasualtyDetails;
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class AAInMoveUtil method selectCasualties.
/**
* hits are removed from units. Note that units are removed in the order
* that the iterator will move through them.
*/
private void selectCasualties(final DiceRoll dice, final Collection<Unit> allFriendlyUnits, final Collection<Unit> validTargetedUnitsForThisRoll, final Collection<Unit> defendingAa, final Collection<Unit> allEnemyUnits, final Territory territory, final String currentTypeAa) {
final CasualtyDetails casualties = BattleCalculator.getAaCasualties(false, validTargetedUnitsForThisRoll, allFriendlyUnits, defendingAa, allEnemyUnits, dice, bridge, territory.getOwner(), player, null, territory, TerritoryEffectHelper.getEffects(territory), false, new ArrayList<>());
getRemotePlayer().reportMessage(casualties.size() + " " + currentTypeAa + " hits in " + territory.getName(), casualties.size() + " " + currentTypeAa + " hits in " + territory.getName());
BattleDelegate.markDamaged(new ArrayList<>(casualties.getDamaged()), bridge);
bridge.getHistoryWriter().addChildToEvent(MyFormatter.unitsToTextNoOwner(casualties.getKilled()) + " lost in " + territory.getName(), new ArrayList<>(casualties.getKilled()));
allFriendlyUnits.removeAll(casualties.getKilled());
if (m_casualties == null) {
m_casualties = new ArrayList<>(casualties.getKilled());
} else {
m_casualties.addAll(casualties.getKilled());
}
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class WW2V3Year41Test method setUp.
@BeforeEach
public void setUp() throws Exception {
when(dummyPlayer.selectCasualties(any(), any(), anyInt(), any(), any(), any(), any(), any(), any(), anyBoolean(), any(), any(), any(), any(), anyBoolean())).thenAnswer(new Answer<CasualtyDetails>() {
@Override
public CasualtyDetails answer(final InvocationOnMock invocation) {
final CasualtyList defaultCasualties = invocation.getArgument(11);
if (defaultCasualties != null) {
return new CasualtyDetails(defaultCasualties.getKilled(), defaultCasualties.getDamaged(), true);
}
return null;
}
});
gameData = TestMapGameData.WW2V3_1941.getGameData();
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class BattleCalculatorTest method testAaCasualtiesLowLuckMixedWithChooseAaCasualtiesRoll.
@Test
public void testAaCasualtiesLowLuckMixedWithChooseAaCasualtiesRoll() {
final GameData data = bridge.getData();
makeGameLowLuck(data);
setSelectAaCasualties(data, true);
// 7 bombers and 7 fighters
final Collection<Unit> planes = bomber(data).create(7, british(data));
planes.addAll(fighter(data).create(7, british(data)));
final Collection<Unit> defendingAa = territory("Germany", data).getUnits().getMatches(Matches.unitIsAaForAnything());
when(dummyPlayer.selectCasualties(any(), any(), anyInt(), any(), any(), any(), any(), any(), any(), anyBoolean(), any(), any(), any(), any(), anyBoolean())).thenAnswer(new Answer<CasualtyDetails>() {
@Override
public CasualtyDetails answer(final InvocationOnMock invocation) {
final Collection<Unit> selectFrom = invocation.getArgument(0);
final int count = invocation.getArgument(2);
final List<Unit> selected = CollectionUtils.getNMatches(selectFrom, count, Matches.unitIsStrategicBomber());
return new CasualtyDetails(selected, new ArrayList<>(), false);
}
});
bridge.setRemote(dummyPlayer);
// only 1 roll, a hit
bridge.setRandomSource(new ScriptedRandomSource(new int[] { 0, ScriptedRandomSource.ERROR }));
final DiceRoll roll = DiceRoll.rollAa(CollectionUtils.getMatches(planes, Matches.unitIsOfTypes(UnitAttachment.get(defendingAa.iterator().next().getType()).getTargetsAa(data))), defendingAa, bridge, territory("Germany", data), true);
final Collection<Unit> casualties = BattleCalculator.getAaCasualties(false, planes, planes, defendingAa, defendingAa, roll, bridge, germans(data), british(data), null, territory("Germany", data), null, false, null).getKilled();
assertEquals(3, casualties.size());
// we selected all bombers
assertEquals(3, CollectionUtils.countMatches(casualties, Matches.unitIsStrategicBomber()));
assertEquals(0, CollectionUtils.countMatches(casualties, Matches.unitIsStrategicBomber().negate()));
}
Aggregations