Search in sources :

Example 1 with CasualtyDetails

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);
}
Also used : GameData(games.strategy.engine.data.GameData) CasualtyDetails(games.strategy.triplea.delegate.dataObjects.CasualtyDetails)

Example 2 with CasualtyDetails

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;
}
Also used : GameData(games.strategy.engine.data.GameData) ArrayList(java.util.ArrayList) Unit(games.strategy.engine.data.Unit) ITripleAPlayer(games.strategy.triplea.player.ITripleAPlayer) UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) WeakAi(games.strategy.triplea.ai.weak.WeakAi) CasualtyList(games.strategy.triplea.delegate.dataObjects.CasualtyList) UnitType(games.strategy.engine.data.UnitType) Collection(java.util.Collection) CasualtyDetails(games.strategy.triplea.delegate.dataObjects.CasualtyDetails) ArrayList(java.util.ArrayList) CasualtyList(games.strategy.triplea.delegate.dataObjects.CasualtyList) List(java.util.List)

Example 3 with CasualtyDetails

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;
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) CasualtyDetails(games.strategy.triplea.delegate.dataObjects.CasualtyDetails) Unit(games.strategy.engine.data.Unit)

Example 4 with CasualtyDetails

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;
}
Also used : ArrayList(java.util.ArrayList) Unit(games.strategy.engine.data.Unit) CasualtyDetails(games.strategy.triplea.delegate.dataObjects.CasualtyDetails)

Example 5 with CasualtyDetails

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;
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) BattleDelegate(games.strategy.triplea.delegate.BattleDelegate) GameData(games.strategy.engine.data.GameData) ArrayList(java.util.ArrayList) Unit(games.strategy.engine.data.Unit) IBattle(games.strategy.triplea.delegate.IBattle) CasualtyDetails(games.strategy.triplea.delegate.dataObjects.CasualtyDetails) List(java.util.List) ArrayList(java.util.ArrayList) CasualtyList(games.strategy.triplea.delegate.dataObjects.CasualtyList)

Aggregations

CasualtyDetails (games.strategy.triplea.delegate.dataObjects.CasualtyDetails)17 ArrayList (java.util.ArrayList)11 Unit (games.strategy.engine.data.Unit)10 GameData (games.strategy.engine.data.GameData)6 CasualtyList (games.strategy.triplea.delegate.dataObjects.CasualtyList)6 List (java.util.List)6 InvocationOnMock (org.mockito.invocation.InvocationOnMock)5 ScriptedRandomSource (games.strategy.engine.random.ScriptedRandomSource)4 Collection (java.util.Collection)4 Test (org.junit.jupiter.api.Test)4 TripleAUnit (games.strategy.triplea.TripleAUnit)3 ITestDelegateBridge (games.strategy.engine.data.ITestDelegateBridge)2 PlayerID (games.strategy.engine.data.PlayerID)2 Route (games.strategy.engine.data.Route)2 Territory (games.strategy.engine.data.Territory)2 TestMapGameData (games.strategy.triplea.xml.TestMapGameData)2 HashSet (java.util.HashSet)2 Set (java.util.Set)2 BeforeEach (org.junit.jupiter.api.BeforeEach)2 UnitType (games.strategy.engine.data.UnitType)1