use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class BattleCalculator method getAaCasualties.
/**
* Choose plane casualties according to specified rules.
*/
static CasualtyDetails getAaCasualties(final boolean defending, final Collection<Unit> planes, final Collection<Unit> allFriendlyUnits, final Collection<Unit> defendingAa, final Collection<Unit> allEnemyUnits, final DiceRoll dice, final IDelegateBridge bridge, final PlayerID firingPlayer, final PlayerID hitPlayer, final GUID battleId, final Territory terr, final Collection<TerritoryEffect> territoryEffects, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers) {
if (planes.isEmpty()) {
return new CasualtyDetails();
}
final GameData data = bridge.getData();
final boolean allowMultipleHitsPerUnit = !defendingAa.isEmpty() && defendingAa.stream().allMatch(Matches.unitAaShotDamageableInsteadOfKillingInstantly());
if (isChooseAa(data)) {
final String text = "Select " + dice.getHits() + " casualties from aa fire in " + terr.getName();
return selectCasualties(null, hitPlayer, planes, allFriendlyUnits, firingPlayer, allEnemyUnits, amphibious, amphibiousLandAttackers, terr, territoryEffects, bridge, text, dice, defending, battleId, false, dice.getHits(), allowMultipleHitsPerUnit);
}
if (Properties.getLowLuck(data) || Properties.getLowLuckAaOnly(data)) {
return getLowLuckAaCasualties(defending, planes, defendingAa, dice, bridge, allowMultipleHitsPerUnit);
}
// if none are set, we roll individually
if (isRollAaIndividually(data)) {
return individuallyFiredAaCasualties(defending, planes, defendingAa, dice, bridge, allowMultipleHitsPerUnit);
}
if (isRandomAaCasualties(data)) {
return randomAaCasualties(planes, dice, bridge, allowMultipleHitsPerUnit);
}
return individuallyFiredAaCasualties(defending, planes, defendingAa, dice, bridge, allowMultipleHitsPerUnit);
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class BattleCalculator method selectCasualties.
/**
* @param battleId
* may be null if we are not in a battle (eg, if this is an aa fire due to moving).
*/
public static CasualtyDetails selectCasualties(final String step, final PlayerID player, final Collection<Unit> targetsToPickFrom, final Collection<Unit> friendlyUnits, final PlayerID enemyPlayer, final Collection<Unit> enemyUnits, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers, final Territory battlesite, final Collection<TerritoryEffect> territoryEffects, final IDelegateBridge bridge, final String text, final DiceRoll dice, final boolean defending, final GUID battleId, final boolean headLess, final int extraHits, final boolean allowMultipleHitsPerUnit) {
if (targetsToPickFrom.isEmpty()) {
return new CasualtyDetails();
}
if (!friendlyUnits.containsAll(targetsToPickFrom)) {
throw new IllegalStateException("friendlyUnits should but does not contain all units from targetsToPickFrom");
}
final GameData data = bridge.getData();
final boolean isEditMode = BaseEditDelegate.getEditMode(data);
final ITripleAPlayer tripleaPlayer = player.isNull() ? new WeakAi(player.getName(), TripleA.WEAK_COMPUTER_PLAYER_TYPE) : (ITripleAPlayer) bridge.getRemotePlayer(player);
final Map<Unit, Collection<Unit>> dependents = headLess ? Collections.emptyMap() : getDependents(targetsToPickFrom);
if (isEditMode && !headLess) {
final CasualtyDetails editSelection = tripleaPlayer.selectCasualties(targetsToPickFrom, dependents, 0, text, dice, player, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, new CasualtyList(), battleId, battlesite, allowMultipleHitsPerUnit);
final List<Unit> killed = editSelection.getKilled();
// if partial retreat is possible, kill amphibious units first
if (isPartialAmphibiousRetreat(data)) {
killAmphibiousFirst(killed, targetsToPickFrom);
}
return editSelection;
}
if (dice.getHits() == 0) {
return new CasualtyDetails(Collections.emptyList(), Collections.emptyList(), true);
}
int hitsRemaining = dice.getHits();
if (isTransportCasualtiesRestricted(data)) {
hitsRemaining = extraHits;
}
if (!isEditMode && allTargetsOneTypeOneHitPoint(targetsToPickFrom, dependents)) {
final List<Unit> killed = new ArrayList<>();
final Iterator<Unit> iter = targetsToPickFrom.iterator();
for (int i = 0; i < hitsRemaining; i++) {
if (i >= targetsToPickFrom.size()) {
break;
}
killed.add(iter.next());
}
return new CasualtyDetails(killed, Collections.emptyList(), true);
}
// Create production cost map, Maybe should do this elsewhere, but in case prices change, we do it here.
final IntegerMap<UnitType> costs = TuvUtils.getCostsForTuv(player, data);
final Tuple<CasualtyList, List<Unit>> defaultCasualtiesAndSortedTargets = getDefaultCasualties(targetsToPickFrom, hitsRemaining, defending, player, enemyUnits, amphibious, amphibiousLandAttackers, battlesite, costs, territoryEffects, data, allowMultipleHitsPerUnit, true);
final CasualtyList defaultCasualties = defaultCasualtiesAndSortedTargets.getFirst();
final List<Unit> sortedTargetsToPickFrom = defaultCasualtiesAndSortedTargets.getSecond();
if (sortedTargetsToPickFrom.size() != targetsToPickFrom.size() || !targetsToPickFrom.containsAll(sortedTargetsToPickFrom) || !sortedTargetsToPickFrom.containsAll(targetsToPickFrom)) {
throw new IllegalStateException("sortedTargetsToPickFrom must contain the same units as targetsToPickFrom list");
}
final int totalHitpoints = (allowMultipleHitsPerUnit ? getTotalHitpointsLeft(sortedTargetsToPickFrom) : sortedTargetsToPickFrom.size());
final CasualtyDetails casualtySelection;
if (hitsRemaining >= totalHitpoints) {
casualtySelection = new CasualtyDetails(defaultCasualties, true);
} else {
casualtySelection = tripleaPlayer.selectCasualties(sortedTargetsToPickFrom, dependents, hitsRemaining, text, dice, player, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, defaultCasualties, battleId, battlesite, allowMultipleHitsPerUnit);
}
List<Unit> killed = casualtySelection.getKilled();
// if partial retreat is possible, kill amphibious units first
if (isPartialAmphibiousRetreat(data)) {
killed = killAmphibiousFirst(killed, sortedTargetsToPickFrom);
}
final List<Unit> damaged = casualtySelection.getDamaged();
int numhits = killed.size();
if (!allowMultipleHitsPerUnit) {
damaged.clear();
} else {
for (final Unit unit : killed) {
final UnitAttachment ua = UnitAttachment.get(unit.getType());
final int damageToUnit = Collections.frequency(damaged, unit);
// allowed damage
numhits += Math.max(0, Math.min(damageToUnit, (ua.getHitPoints() - (1 + unit.getHits()))));
// remove from damaged list, since they will die
damaged.removeIf(unit::equals);
}
}
// check right number
if (!isEditMode && !(numhits + damaged.size() == (hitsRemaining > totalHitpoints ? totalHitpoints : hitsRemaining))) {
tripleaPlayer.reportError("Wrong number of casualties selected");
if (headLess) {
System.err.println("Possible Infinite Loop: Wrong number of casualties selected: number of hits on units " + (numhits + damaged.size()) + " != number of hits to take " + (hitsRemaining > totalHitpoints ? totalHitpoints : hitsRemaining) + ", for " + casualtySelection.toString());
}
return selectCasualties(step, player, sortedTargetsToPickFrom, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, battlesite, territoryEffects, bridge, text, dice, defending, battleId, headLess, extraHits, allowMultipleHitsPerUnit);
}
// check we have enough of each type
if (!sortedTargetsToPickFrom.containsAll(killed) || !sortedTargetsToPickFrom.containsAll(damaged)) {
tripleaPlayer.reportError("Cannot remove enough units of those types");
if (headLess) {
System.err.println("Possible Infinite Loop: Cannot remove enough units of those types: targets " + MyFormatter.unitsToTextNoOwner(sortedTargetsToPickFrom) + ", for " + casualtySelection.toString());
}
return selectCasualties(step, player, sortedTargetsToPickFrom, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, battlesite, territoryEffects, bridge, text, dice, defending, battleId, headLess, extraHits, allowMultipleHitsPerUnit);
}
return casualtySelection;
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class BattleCalculator method randomAaCasualties.
/**
* Choose plane casualties randomly.
*/
public static CasualtyDetails randomAaCasualties(final Collection<Unit> planes, 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 int hitsLeft = dice.getHits();
if (hitsLeft <= 0) {
return new CasualtyDetails();
}
final CasualtyDetails finalCasualtyDetails = new CasualtyDetails();
// normal behavior is instant kill, which means planes.size()
final int planeHitPoints = (allowMultipleHitsPerUnit ? getTotalHitpointsLeft(planes) : planes.size());
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);
}
}
// We need to choose which planes die randomly
if (hitsLeft < planeHitPoints) {
// roll all at once to prevent frequent random calls, important for pbem games
final int[] hitRandom = bridge.getRandom(planeHitPoints, 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 = planesList.remove(pos % planesList.size());
if (allowMultipleHitsPerUnit && (Collections.frequency(finalCasualtyDetails.getDamaged(), unitHit) < (getTotalHitpointsLeft(unitHit) - 1))) {
finalCasualtyDetails.addToDamaged(unitHit);
} else {
finalCasualtyDetails.addToKilled(unitHit);
}
}
} else {
for (final Unit plane : planesList) {
if (finalCasualtyDetails.getKilled().contains(plane)) {
finalCasualtyDetails.addToDamaged(plane);
} else {
finalCasualtyDetails.addToKilled(plane);
}
}
}
return finalCasualtyDetails;
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class BattleCalculator method individuallyFiredAaCasualties.
/**
* Choose plane casualties based on individual AA shots at each aircraft.
*/
private static CasualtyDetails individuallyFiredAaCasualties(final boolean defending, final Collection<Unit> planes, final Collection<Unit> defendingAa, final DiceRoll dice, final IDelegateBridge bridge, final boolean allowMultipleHitsPerUnit) {
// if we have aa guns that are not infinite, then we need to randomly decide the aa casualties since there are not
// enough rolls to have
// a single roll for each aircraft, or too many rolls
// normal behavior is instant kill, which means planes.size()
final int planeHitPoints = (allowMultipleHitsPerUnit ? getTotalHitpointsLeft(planes) : planes.size());
if (DiceRoll.getTotalAAattacks(defendingAa, planes) != planeHitPoints) {
return randomAaCasualties(planes, dice, bridge, allowMultipleHitsPerUnit);
}
final Triple<Integer, Integer, Boolean> triple = DiceRoll.getTotalAaPowerThenHitsAndFillSortedDiceThenIfAllUseSameAttack(null, null, !defending, defendingAa, planes, bridge.getData(), false);
final boolean allSameAttackPower = triple.getThird();
if (!allSameAttackPower) {
return randomAaCasualties(planes, dice, bridge, allowMultipleHitsPerUnit);
}
final Tuple<Integer, Integer> attackThenDiceSides = DiceRoll.getAAattackAndMaxDiceSides(defendingAa, bridge.getData(), !defending);
final int highestAttack = attackThenDiceSides.getFirst();
// int chosenDiceSize = attackThenDiceSides[1];
final CasualtyDetails finalCasualtyDetails = new CasualtyDetails();
final int hits = dice.getHits();
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);
}
}
// We need to choose which planes die based on their position in the list and the individual AA rolls
if (hits > planeHitPoints) {
throw new IllegalStateException("Cannot have more hits than number of die rolls");
}
if (hits < planeHitPoints) {
final List<Die> rolls = dice.getRolls(highestAttack);
for (int i = 0; i < rolls.size(); i++) {
final Die die = rolls.get(i);
if (die.getType() == DieType.HIT) {
final Unit unit = planesList.get(i);
if (allowMultipleHitsPerUnit && (Collections.frequency(finalCasualtyDetails.getDamaged(), unit) < (getTotalHitpointsLeft(unit) - 1))) {
finalCasualtyDetails.addToDamaged(unit);
} else {
finalCasualtyDetails.addToKilled(unit);
}
}
}
} else {
for (final Unit plane : planesList) {
if (finalCasualtyDetails.getKilled().contains(plane)) {
finalCasualtyDetails.addToDamaged(plane);
} else {
finalCasualtyDetails.addToKilled(plane);
}
}
}
return finalCasualtyDetails;
}
use of games.strategy.triplea.delegate.dataObjects.CasualtyDetails in project triplea by triplea-game.
the class ProAi method selectCasualties.
@Override
public CasualtyDetails selectCasualties(final Collection<Unit> selectFrom, final Map<Unit, Collection<Unit>> dependents, final int count, final String message, final DiceRoll dice, final PlayerID hit, final Collection<Unit> friendlyUnits, final PlayerID enemyPlayer, final Collection<Unit> enemyUnits, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers, final CasualtyList defaultCasualties, final GUID battleId, final Territory battlesite, final boolean allowMultipleHitsPerUnit) {
initializeData();
if (defaultCasualties.size() != count) {
throw new IllegalStateException("Select Casualties showing different numbers for number of hits to take vs total " + "size of default casualty selections");
}
if (defaultCasualties.getKilled().size() <= 0) {
return new CasualtyDetails(defaultCasualties, false);
}
// Consider unit cost
final CasualtyDetails myCasualties = new CasualtyDetails(false);
myCasualties.addToDamaged(defaultCasualties.getDamaged());
final List<Unit> selectFromSorted = new ArrayList<>(selectFrom);
if (enemyUnits.isEmpty()) {
selectFromSorted.sort(ProPurchaseUtils.getCostComparator());
} else {
// Get battle data
final GameData data = getGameData();
final PlayerID player = getPlayerId();
final BattleDelegate delegate = DelegateFinder.battleDelegate(data);
final IBattle battle = delegate.getBattleTracker().getPendingBattle(battleId);
// If defender and could lose battle then don't consider unit cost as just trying to survive
boolean needToCheck = true;
final boolean isAttacker = player.equals(battle.getAttacker());
if (!isAttacker) {
final List<Unit> attackers = (List<Unit>) battle.getAttackingUnits();
final List<Unit> defenders = (List<Unit>) battle.getDefendingUnits();
defenders.removeAll(defaultCasualties.getKilled());
final double strengthDifference = ProBattleUtils.estimateStrengthDifference(battlesite, attackers, defenders);
int minStrengthDifference = 60;
if (!Properties.getLowLuck(data)) {
minStrengthDifference = 55;
}
if (strengthDifference > minStrengthDifference) {
needToCheck = false;
}
}
// Use bubble sort to save expensive units
while (needToCheck) {
needToCheck = false;
for (int i = 0; i < selectFromSorted.size() - 1; i++) {
final Unit unit1 = selectFromSorted.get(i);
final Unit unit2 = selectFromSorted.get(i + 1);
final double unitCost1 = ProPurchaseUtils.getCost(unit1);
final double unitCost2 = ProPurchaseUtils.getCost(unit2);
if (unitCost1 > 1.5 * unitCost2) {
selectFromSorted.set(i, unit2);
selectFromSorted.set(i + 1, unit1);
needToCheck = true;
}
}
}
}
// Interleave carriers and planes
final List<Unit> interleavedTargetList = new ArrayList<>(ProTransportUtils.interleaveUnitsCarriersAndPlanes(selectFromSorted, 0));
for (int i = 0; i < defaultCasualties.getKilled().size(); ++i) {
myCasualties.addToKilled(interleavedTargetList.get(i));
}
if (count != myCasualties.size()) {
throw new IllegalStateException("AI chose wrong number of casualties");
}
return myCasualties;
}
Aggregations