Search in sources :

Example 1 with IntegerMap

use of games.strategy.util.IntegerMap in project triplea by triplea-game.

the class RandomStatsDetails method getAllStats.

/**
 * Returns a JPanel displaying information about all Statistics.
 */
public JPanel getAllStats() {
    final Insets insets = new Insets(2, 2, 2, 2);
    final JPanel panel = new JPanel();
    panel.setLayout(new GridBagLayout());
    panel.setBorder(BorderFactory.createEmptyBorder());
    panel.add(getStatsDisplay(m_totalMap, m_totalStats, "Total"), new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE, insets, 0, 0));
    if (getData().containsKey(null)) {
        panel.add(getStatsDisplay(getData().get(null), m_playerStats.get(null), "Null / Other"), new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE, insets, 0, 0));
    }
    final int rows = Math.max(2, getData().size() / 6);
    int x = 0;
    for (final Entry<PlayerID, IntegerMap<Integer>> entry : getData().entrySet()) {
        if (entry.getKey() == null) {
            continue;
        }
        panel.add(getStatsDisplay(entry.getValue(), m_playerStats.get(entry.getKey()), (entry.getKey() == null ? "Null / Other" : entry.getKey().getName() + " Combat")), new GridBagConstraints((x / rows), 1 + (x % rows), 1, 1, 1, 1, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE, insets, 0, 0));
        x++;
    }
    return panel;
}
Also used : IntegerMap(games.strategy.util.IntegerMap) JPanel(javax.swing.JPanel) PlayerID(games.strategy.engine.data.PlayerID) GridBagConstraints(java.awt.GridBagConstraints) Insets(java.awt.Insets) GridBagLayout(java.awt.GridBagLayout)

Example 2 with IntegerMap

use of games.strategy.util.IntegerMap in project triplea by triplea-game.

the class BattleCalculator method sortUnitsForCasualtiesWithSupport.

/**
 * The purpose of this is to return a list in the PERFECT order of which units should be selected to die first,
 * And that means that certain units MUST BE INTERLEAVED.
 * This list assumes that you have already taken any extra hit points away from any 2 hitpoint units.
 * Example: You have a 1 attack Artillery unit that supports, and a 1 attack infantry unit that can receive support.
 * The best selection of units to die is first to take whichever unit has excess, then cut that down til they are both
 * the same size,
 * then to take 1 artillery followed by 1 infantry, followed by 1 artillery, then 1 inf, etc, until everyone is dead.
 * If you just return all infantry followed by all artillery, or the other way around, you will be missing out on some
 * important support
 * provided.
 * (Veqryn)
 */
private static List<Unit> sortUnitsForCasualtiesWithSupport(final Collection<Unit> targetsToPickFrom, final boolean defending, final PlayerID player, final Collection<Unit> enemyUnits, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers, final Territory battlesite, final IntegerMap<UnitType> costs, final Collection<TerritoryEffect> territoryEffects, final GameData data, final boolean bonus) {
    // Convert unit lists to unit type lists
    final List<UnitType> targetTypes = new ArrayList<>();
    for (final Unit u : targetsToPickFrom) {
        targetTypes.add(u.getType());
    }
    final List<UnitType> amphibTypes = new ArrayList<>();
    if (amphibiousLandAttackers != null) {
        for (final Unit u : amphibiousLandAttackers) {
            amphibTypes.add(u.getType());
        }
    }
    // Calculate hashes and cache key
    int targetsHashCode = 1;
    for (final UnitType ut : targetTypes) {
        targetsHashCode += ut.hashCode();
    }
    targetsHashCode *= 31;
    int amphibHashCode = 1;
    for (final UnitType ut : amphibTypes) {
        amphibHashCode += ut.hashCode();
    }
    amphibHashCode *= 31;
    String key = player.getName() + "|" + battlesite.getName() + "|" + defending + "|" + amphibious + "|" + targetsHashCode + "|" + amphibHashCode;
    // Check OOL cache
    final List<UnitType> stored = oolCache.get(key);
    if (stored != null) {
        // System.out.println("Hit with cacheSize=" + oolCache.size() + ", key=" + key);
        final List<Unit> result = new ArrayList<>();
        final List<Unit> selectFrom = new ArrayList<>(targetsToPickFrom);
        for (final UnitType ut : stored) {
            for (final Iterator<Unit> it = selectFrom.iterator(); it.hasNext(); ) {
                final Unit u = it.next();
                if (ut.equals(u.getType())) {
                    result.add(u);
                    it.remove();
                }
            }
        }
        return result;
    }
    // System.out.println("Miss with cacheSize=" + oolCache.size() + ", key=" + key);
    // Sort enough units to kill off
    final List<Unit> sortedUnitsList = new ArrayList<>(targetsToPickFrom);
    sortedUnitsList.sort(new UnitBattleComparator(defending, costs, territoryEffects, data, bonus, false));
    // Sort units starting with strongest so that support gets added to them first
    Collections.reverse(sortedUnitsList);
    final UnitBattleComparator unitComparatorWithoutPrimaryPower = new UnitBattleComparator(defending, costs, territoryEffects, data, bonus, true);
    final Map<Unit, IntegerMap<Unit>> unitSupportPowerMap = new HashMap<>();
    final Map<Unit, IntegerMap<Unit>> unitSupportRollsMap = new HashMap<>();
    final Map<Unit, Tuple<Integer, Integer>> unitPowerAndRollsMap = DiceRoll.getUnitPowerAndRollsForNormalBattles(sortedUnitsList, new ArrayList<>(enemyUnits), defending, false, data, battlesite, territoryEffects, amphibious, amphibiousLandAttackers, unitSupportPowerMap, unitSupportRollsMap);
    // Sort units starting with weakest for finding the worst units
    Collections.reverse(sortedUnitsList);
    final List<Unit> sortedWellEnoughUnitsList = new ArrayList<>();
    for (int i = 0; i < sortedUnitsList.size(); ++i) {
        // Loop through all target units to find the best unit to take as casualty
        Unit worstUnit = null;
        int minPower = Integer.MAX_VALUE;
        final Set<UnitType> unitTypes = new HashSet<>();
        for (final Unit u : sortedUnitsList) {
            if (unitTypes.contains(u.getType())) {
                continue;
            }
            unitTypes.add(u.getType());
            // Find unit power
            final Map<Unit, Tuple<Integer, Integer>> currentUnitMap = new HashMap<>();
            currentUnitMap.put(u, unitPowerAndRollsMap.get(u));
            int power = DiceRoll.getTotalPower(currentUnitMap, data);
            // Add any support power that it provides to other units
            final IntegerMap<Unit> unitSupportPowerMapForUnit = unitSupportPowerMap.get(u);
            if (unitSupportPowerMapForUnit != null) {
                for (final Unit supportedUnit : unitSupportPowerMapForUnit.keySet()) {
                    Tuple<Integer, Integer> strengthAndRolls = unitPowerAndRollsMap.get(supportedUnit);
                    if (strengthAndRolls == null) {
                        continue;
                    }
                    // Remove any rolls provided by this support so they aren't counted twice
                    final IntegerMap<Unit> unitSupportRollsMapForUnit = unitSupportRollsMap.get(u);
                    if (unitSupportRollsMapForUnit != null) {
                        strengthAndRolls = Tuple.of(strengthAndRolls.getFirst(), strengthAndRolls.getSecond() - unitSupportRollsMapForUnit.getInt(supportedUnit));
                    }
                    // If one roll then just add the power
                    if (strengthAndRolls.getSecond() == 1) {
                        power += unitSupportPowerMapForUnit.getInt(supportedUnit);
                        continue;
                    }
                    // Find supported unit power with support
                    final Map<Unit, Tuple<Integer, Integer>> supportedUnitMap = new HashMap<>();
                    supportedUnitMap.put(supportedUnit, strengthAndRolls);
                    final int powerWithSupport = DiceRoll.getTotalPower(supportedUnitMap, data);
                    // Find supported unit power without support
                    final int strengthWithoutSupport = strengthAndRolls.getFirst() - unitSupportPowerMapForUnit.getInt(supportedUnit);
                    final Tuple<Integer, Integer> strengthAndRollsWithoutSupport = Tuple.of(strengthWithoutSupport, strengthAndRolls.getSecond());
                    supportedUnitMap.put(supportedUnit, strengthAndRollsWithoutSupport);
                    final int powerWithoutSupport = DiceRoll.getTotalPower(supportedUnitMap, data);
                    // Add the actual power provided by the support
                    final int addedPower = powerWithSupport - powerWithoutSupport;
                    power += addedPower;
                }
            }
            // Add any power from support rolls that it provides to other units
            final IntegerMap<Unit> unitSupportRollsMapForUnit = unitSupportRollsMap.get(u);
            if (unitSupportRollsMapForUnit != null) {
                for (final Unit supportedUnit : unitSupportRollsMapForUnit.keySet()) {
                    final Tuple<Integer, Integer> strengthAndRolls = unitPowerAndRollsMap.get(supportedUnit);
                    if (strengthAndRolls == null) {
                        continue;
                    }
                    // Find supported unit power with support
                    final Map<Unit, Tuple<Integer, Integer>> supportedUnitMap = new HashMap<>();
                    supportedUnitMap.put(supportedUnit, strengthAndRolls);
                    final int powerWithSupport = DiceRoll.getTotalPower(supportedUnitMap, data);
                    // Find supported unit power without support
                    final int rollsWithoutSupport = strengthAndRolls.getSecond() - unitSupportRollsMap.get(u).getInt(supportedUnit);
                    final Tuple<Integer, Integer> strengthAndRollsWithoutSupport = Tuple.of(strengthAndRolls.getFirst(), rollsWithoutSupport);
                    supportedUnitMap.put(supportedUnit, strengthAndRollsWithoutSupport);
                    final int powerWithoutSupport = DiceRoll.getTotalPower(supportedUnitMap, data);
                    // Add the actual power provided by the support
                    final int addedPower = powerWithSupport - powerWithoutSupport;
                    power += addedPower;
                }
            }
            // Check if unit has lower power
            if (power < minPower || (power == minPower && unitComparatorWithoutPrimaryPower.compare(u, worstUnit) < 0)) {
                worstUnit = u;
                minPower = power;
            }
        }
        // Add worst unit to sorted list, update any units it supported, and remove from other collections
        final IntegerMap<Unit> unitSupportPowerMapForUnit = unitSupportPowerMap.get(worstUnit);
        if (unitSupportPowerMapForUnit != null) {
            for (final Unit supportedUnit : unitSupportPowerMapForUnit.keySet()) {
                final Tuple<Integer, Integer> strengthAndRolls = unitPowerAndRollsMap.get(supportedUnit);
                if (strengthAndRolls == null) {
                    continue;
                }
                final int strengthWithoutSupport = strengthAndRolls.getFirst() - unitSupportPowerMapForUnit.getInt(supportedUnit);
                final Tuple<Integer, Integer> strengthAndRollsWithoutSupport = Tuple.of(strengthWithoutSupport, strengthAndRolls.getSecond());
                unitPowerAndRollsMap.put(supportedUnit, strengthAndRollsWithoutSupport);
                sortedUnitsList.remove(supportedUnit);
                sortedUnitsList.add(0, supportedUnit);
            }
        }
        final IntegerMap<Unit> unitSupportRollsMapForUnit = unitSupportRollsMap.get(worstUnit);
        if (unitSupportRollsMapForUnit != null) {
            for (final Unit supportedUnit : unitSupportRollsMapForUnit.keySet()) {
                final Tuple<Integer, Integer> strengthAndRolls = unitPowerAndRollsMap.get(supportedUnit);
                if (strengthAndRolls == null) {
                    continue;
                }
                final int rollsWithoutSupport = strengthAndRolls.getSecond() - unitSupportRollsMapForUnit.getInt(supportedUnit);
                final Tuple<Integer, Integer> strengthAndRollsWithoutSupport = Tuple.of(strengthAndRolls.getFirst(), rollsWithoutSupport);
                unitPowerAndRollsMap.put(supportedUnit, strengthAndRollsWithoutSupport);
                sortedUnitsList.remove(supportedUnit);
                sortedUnitsList.add(0, supportedUnit);
            }
        }
        sortedWellEnoughUnitsList.add(worstUnit);
        sortedUnitsList.remove(worstUnit);
        unitPowerAndRollsMap.remove(worstUnit);
        unitSupportPowerMap.remove(worstUnit);
        unitSupportRollsMap.remove(worstUnit);
    }
    sortedWellEnoughUnitsList.addAll(sortedUnitsList);
    // Cache result and all subsets of the result
    final List<UnitType> unitTypes = new ArrayList<>();
    for (final Unit u : sortedWellEnoughUnitsList) {
        unitTypes.add(u.getType());
    }
    for (final Iterator<UnitType> it = unitTypes.iterator(); it.hasNext(); ) {
        oolCache.put(key, new ArrayList<>(unitTypes));
        final UnitType unitTypeToRemove = it.next();
        targetTypes.remove(unitTypeToRemove);
        if (Collections.frequency(targetTypes, unitTypeToRemove) < Collections.frequency(amphibTypes, unitTypeToRemove)) {
            amphibTypes.remove(unitTypeToRemove);
        }
        targetsHashCode = 1;
        for (final UnitType ut : targetTypes) {
            targetsHashCode += ut.hashCode();
        }
        targetsHashCode *= 31;
        amphibHashCode = 1;
        for (final UnitType ut : amphibTypes) {
            amphibHashCode += ut.hashCode();
        }
        amphibHashCode *= 31;
        key = player.getName() + "|" + battlesite.getName() + "|" + defending + "|" + amphibious + "|" + targetsHashCode + "|" + amphibHashCode;
        it.remove();
    }
    return sortedWellEnoughUnitsList;
}
Also used : LinkedIntegerMap(games.strategy.util.LinkedIntegerMap) IntegerMap(games.strategy.util.IntegerMap) HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ArrayList(java.util.ArrayList) Unit(games.strategy.engine.data.Unit) UnitType(games.strategy.engine.data.UnitType) Tuple(games.strategy.util.Tuple) HashSet(java.util.HashSet)

Example 3 with IntegerMap

use of games.strategy.util.IntegerMap in project triplea by triplea-game.

the class BattleDelegate method doKamikazeSuicideAttacks.

/**
 * KamikazeSuicideAttacks are attacks that are made during an Opponent's turn, using Resources that you own that have
 * been designated.
 * The resources are designated in PlayerAttachment, and hold information like the attack power of the resource.
 * KamikazeSuicideAttacks are done in any territory that is a kamikazeZone, and the attacks are done by the original
 * owner of that
 * territory.
 * The user has the option not to do any attacks, and they make target any number of units with any number of resource
 * tokens.
 * The units are then attacked individually by each resource token (meaning that casualties do not get selected
 * because the attacks are
 * targeted).
 * The enemies of current player should decide all their attacks before the attacks are rolled.
 */
private void doKamikazeSuicideAttacks() {
    final GameData data = getData();
    if (!Properties.getUseKamikazeSuicideAttacks(data)) {
        return;
    }
    // the current player is not the one who is doing these attacks, it is the all the enemies of this player who will
    // do attacks
    final Collection<PlayerID> enemies = CollectionUtils.getMatches(data.getPlayerList().getPlayers(), Matches.isAtWar(player, data));
    if (enemies.isEmpty()) {
        return;
    }
    final Predicate<Unit> canBeAttackedDefault = Matches.unitIsOwnedBy(player).and(Matches.unitIsSea()).and(Matches.unitIsNotTransportButCouldBeCombatTransport()).and(Matches.unitIsNotSub());
    final boolean onlyWhereThereAreBattlesOrAmphibious = Properties.getKamikazeSuicideAttacksOnlyWhereBattlesAre(data);
    final Collection<Territory> pendingBattles = battleTracker.getPendingBattleSites(false);
    // create a list of all kamikaze zones, listed by enemy
    final Map<PlayerID, Collection<Territory>> kamikazeZonesByEnemy = new HashMap<>();
    for (final Territory t : data.getMap().getTerritories()) {
        final TerritoryAttachment ta = TerritoryAttachment.get(t);
        if (ta == null || !ta.getKamikazeZone()) {
            continue;
        }
        final PlayerID owner = !Properties.getKamikazeSuicideAttacksDoneByCurrentTerritoryOwner(data) ? ta.getOriginalOwner() : t.getOwner();
        if (owner == null) {
            continue;
        }
        if (enemies.contains(owner)) {
            if (t.getUnits().getUnits().stream().noneMatch(Matches.unitIsOwnedBy(player))) {
                continue;
            }
            if (onlyWhereThereAreBattlesOrAmphibious) {
                // if no battle or amphibious from here, ignore it
                if (!pendingBattles.contains(t)) {
                    if (!Matches.territoryIsWater().test(t)) {
                        continue;
                    }
                    boolean amphib = false;
                    final Collection<Territory> landNeighbors = data.getMap().getNeighbors(t, Matches.territoryIsLand());
                    for (final Territory neighbor : landNeighbors) {
                        final IBattle battle = battleTracker.getPendingBattle(neighbor, false, BattleType.NORMAL);
                        if (battle == null) {
                            final Map<Territory, Collection<Unit>> whereFrom = battleTracker.getFinishedBattlesUnitAttackFromMap().get(neighbor);
                            if (whereFrom != null && whereFrom.containsKey(t)) {
                                amphib = true;
                                break;
                            }
                            continue;
                        }
                        if (battle.isAmphibious() && ((battle instanceof MustFightBattle && ((MustFightBattle) battle).getAmphibiousAttackTerritories().contains(t)) || (battle instanceof NonFightingBattle && ((NonFightingBattle) battle).getAmphibiousAttackTerritories().contains(t)))) {
                            amphib = true;
                            break;
                        }
                    }
                    if (!amphib) {
                        continue;
                    }
                }
            }
            final Collection<Territory> currentTerrs = kamikazeZonesByEnemy.getOrDefault(owner, new ArrayList<>());
            currentTerrs.add(t);
            kamikazeZonesByEnemy.put(owner, currentTerrs);
        }
    }
    if (kamikazeZonesByEnemy.isEmpty()) {
        return;
    }
    for (final Entry<PlayerID, Collection<Territory>> entry : kamikazeZonesByEnemy.entrySet()) {
        final PlayerID currentEnemy = entry.getKey();
        final PlayerAttachment pa = PlayerAttachment.get(currentEnemy);
        if (pa == null) {
            continue;
        }
        Predicate<Unit> canBeAttacked = canBeAttackedDefault;
        final Set<UnitType> suicideAttackTargets = pa.getSuicideAttackTargets();
        if (suicideAttackTargets != null) {
            canBeAttacked = Matches.unitIsOwnedBy(player).and(Matches.unitIsOfTypes(suicideAttackTargets));
        }
        // See if the player has any attack tokens
        final IntegerMap<Resource> resourcesAndAttackValues = pa.getSuicideAttackResources();
        if (resourcesAndAttackValues.size() <= 0) {
            continue;
        }
        final IntegerMap<Resource> playerResourceCollection = currentEnemy.getResources().getResourcesCopy();
        final IntegerMap<Resource> attackTokens = new IntegerMap<>();
        for (final Resource possible : resourcesAndAttackValues.keySet()) {
            final int amount = playerResourceCollection.getInt(possible);
            if (amount > 0) {
                attackTokens.put(possible, amount);
            }
        }
        if (attackTokens.size() <= 0) {
            continue;
        }
        // now let the enemy decide if they will do attacks
        final Collection<Territory> kamikazeZones = entry.getValue();
        final HashMap<Territory, Collection<Unit>> possibleUnitsToAttack = new HashMap<>();
        for (final Territory t : kamikazeZones) {
            final List<Unit> validTargets = t.getUnits().getMatches(canBeAttacked);
            if (!validTargets.isEmpty()) {
                possibleUnitsToAttack.put(t, validTargets);
            }
        }
        final Map<Territory, HashMap<Unit, IntegerMap<Resource>>> attacks = getRemotePlayer(currentEnemy).selectKamikazeSuicideAttacks(possibleUnitsToAttack);
        if (attacks == null || attacks.isEmpty()) {
            continue;
        }
        // now validate that we have the resources and those units are valid targets
        for (final Entry<Territory, HashMap<Unit, IntegerMap<Resource>>> territoryEntry : attacks.entrySet()) {
            final Territory t = territoryEntry.getKey();
            final Collection<Unit> possibleUnits = possibleUnitsToAttack.get(t);
            if (possibleUnits == null || !possibleUnits.containsAll(territoryEntry.getValue().keySet())) {
                throw new IllegalStateException("Player has chosen illegal units during Kamikaze Suicide Attacks");
            }
            for (final IntegerMap<Resource> resourceMap : territoryEntry.getValue().values()) {
                attackTokens.subtract(resourceMap);
            }
        }
        if (!attackTokens.isPositive()) {
            throw new IllegalStateException("Player has chosen illegal resource during Kamikaze Suicide Attacks");
        }
        for (final Entry<Territory, HashMap<Unit, IntegerMap<Resource>>> territoryEntry : attacks.entrySet()) {
            final Territory location = territoryEntry.getKey();
            for (final Entry<Unit, IntegerMap<Resource>> unitEntry : territoryEntry.getValue().entrySet()) {
                final Unit unitUnderFire = unitEntry.getKey();
                final IntegerMap<Resource> numberOfAttacks = unitEntry.getValue();
                if (numberOfAttacks != null && numberOfAttacks.size() > 0 && numberOfAttacks.totalValues() > 0) {
                    fireKamikazeSuicideAttacks(unitUnderFire, numberOfAttacks, resourcesAndAttackValues, currentEnemy, location);
                }
            }
        }
    }
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) GameData(games.strategy.engine.data.GameData) HashMap(java.util.HashMap) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) PlayerAttachment(games.strategy.triplea.attachments.PlayerAttachment) UnitType(games.strategy.engine.data.UnitType) IntegerMap(games.strategy.util.IntegerMap) Territory(games.strategy.engine.data.Territory) TerritoryAttachment(games.strategy.triplea.attachments.TerritoryAttachment) Resource(games.strategy.engine.data.Resource) Collection(java.util.Collection) ResourceCollection(games.strategy.engine.data.ResourceCollection)

Example 4 with IntegerMap

use of games.strategy.util.IntegerMap in project triplea by triplea-game.

the class BattleDelegate method markDamaged.

static void markDamaged(final Collection<Unit> damaged, final IDelegateBridge bridge) {
    if (damaged.size() == 0) {
        return;
    }
    final IntegerMap<Unit> damagedMap = new IntegerMap<>();
    for (final Unit u : damaged) {
        damagedMap.add(u, 1);
    }
    bridge.addChange(createDamageChange(damagedMap, bridge));
}
Also used : IntegerMap(games.strategy.util.IntegerMap) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit)

Example 5 with IntegerMap

use of games.strategy.util.IntegerMap in project triplea by triplea-game.

the class BattleTracker method captureOrDestroyUnits.

/**
 * Called when a territory is conquered to determine if remaining enemy units should be
 * captured, destroyed, or take damage.
 */
public static void captureOrDestroyUnits(final Territory territory, final PlayerID id, final PlayerID newOwner, final IDelegateBridge bridge, final UndoableMove changeTracker) {
    final GameData data = bridge.getData();
    // destroy any units that should be destroyed on capture
    if (Properties.getUnitsCanBeDestroyedInsteadOfCaptured(data)) {
        final Predicate<Unit> enemyToBeDestroyed = Matches.enemyUnit(id, data).and(Matches.unitDestroyedWhenCapturedByOrFrom(id));
        final Collection<Unit> destroyed = territory.getUnits().getMatches(enemyToBeDestroyed);
        if (!destroyed.isEmpty()) {
            final Change destroyUnits = ChangeFactory.removeUnits(territory, destroyed);
            bridge.getHistoryWriter().addChildToEvent("Some non-combat units are destroyed: ", destroyed);
            bridge.addChange(destroyUnits);
            if (changeTracker != null) {
                changeTracker.addChange(destroyUnits);
            }
        }
    }
    // destroy any capture on entering units, IF the property to destroy them instead of capture is turned on
    if (Properties.getOnEnteringUnitsDestroyedInsteadOfCaptured(data)) {
        final Collection<Unit> destroyed = territory.getUnits().getMatches(Matches.unitCanBeCapturedOnEnteringToInThisTerritory(id, territory, data));
        if (!destroyed.isEmpty()) {
            final Change destroyUnits = ChangeFactory.removeUnits(territory, destroyed);
            bridge.getHistoryWriter().addChildToEvent(id.getName() + " destroys some units instead of capturing them", destroyed);
            bridge.addChange(destroyUnits);
            if (changeTracker != null) {
                changeTracker.addChange(destroyUnits);
            }
        }
    }
    // destroy any disabled units owned by the enemy that are NOT infrastructure or factories
    final Predicate<Unit> enemyToBeDestroyed = Matches.enemyUnit(id, data).and(Matches.unitIsDisabled()).and(Matches.unitIsInfrastructure().negate());
    final Collection<Unit> destroyed = territory.getUnits().getMatches(enemyToBeDestroyed);
    if (!destroyed.isEmpty()) {
        final Change destroyUnits = ChangeFactory.removeUnits(territory, destroyed);
        bridge.getHistoryWriter().addChildToEvent(id.getName() + " destroys some disabled combat units", destroyed);
        bridge.addChange(destroyUnits);
        if (changeTracker != null) {
            changeTracker.addChange(destroyUnits);
        }
    }
    // take over non combatants
    final Predicate<Unit> enemyNonCom = Matches.enemyUnit(id, data).and(Matches.unitIsInfrastructure());
    final Predicate<Unit> willBeCaptured = enemyNonCom.or(Matches.unitCanBeCapturedOnEnteringToInThisTerritory(id, territory, data));
    final Collection<Unit> nonCom = territory.getUnits().getMatches(willBeCaptured);
    // change any units that change unit types on capture
    if (Properties.getUnitsCanBeChangedOnCapture(data)) {
        final Collection<Unit> toReplace = CollectionUtils.getMatches(nonCom, Matches.unitWhenCapturedChangesIntoDifferentUnitType());
        for (final Unit u : toReplace) {
            final Map<String, Tuple<String, IntegerMap<UnitType>>> map = UnitAttachment.get(u.getType()).getWhenCapturedChangesInto();
            final PlayerID currentOwner = u.getOwner();
            for (final String value : map.keySet()) {
                final String[] s = value.split(":");
                if (!(s[0].equals("any") || data.getPlayerList().getPlayerId(s[0]).equals(currentOwner))) {
                    continue;
                }
                // we could use "id" or "newOwner" here... not sure which to use
                if (!(s[1].equals("any") || data.getPlayerList().getPlayerId(s[1]).equals(id))) {
                    continue;
                }
                final CompositeChange changes = new CompositeChange();
                final Collection<Unit> toAdd = new ArrayList<>();
                final Tuple<String, IntegerMap<UnitType>> toCreate = map.get(value);
                final boolean translateAttributes = toCreate.getFirst().equalsIgnoreCase("true");
                for (final UnitType ut : toCreate.getSecond().keySet()) {
                    toAdd.addAll(ut.create(toCreate.getSecond().getInt(ut), newOwner));
                }
                if (!toAdd.isEmpty()) {
                    if (translateAttributes) {
                        final Change translate = TripleAUnit.translateAttributesToOtherUnits(u, toAdd, territory);
                        if (!translate.isEmpty()) {
                            changes.add(translate);
                        }
                    }
                    changes.add(ChangeFactory.removeUnits(territory, Collections.singleton(u)));
                    changes.add(ChangeFactory.addUnits(territory, toAdd));
                    changes.add(ChangeFactory.markNoMovementChange(toAdd));
                    bridge.getHistoryWriter().addChildToEvent(id.getName() + " converts " + u.toStringNoOwner() + " into different units", toAdd);
                    bridge.addChange(changes);
                    if (changeTracker != null) {
                        changeTracker.addChange(changes);
                    }
                    // don't forget to remove this unit from the list
                    nonCom.remove(u);
                    break;
                }
            }
        }
    }
    if (!nonCom.isEmpty()) {
        // FYI: a dummy delegate will not do anything with this change,
        // meaning that the battle calculator will think this unit lived,
        // even though it died or was captured, etc!
        final Change capture = ChangeFactory.changeOwner(nonCom, newOwner, territory);
        bridge.addChange(capture);
        if (changeTracker != null) {
            changeTracker.addChange(capture);
        }
        final Change noMovementChange = ChangeFactory.markNoMovementChange(nonCom);
        bridge.addChange(noMovementChange);
        if (changeTracker != null) {
            changeTracker.addChange(noMovementChange);
        }
        final IntegerMap<Unit> damageMap = new IntegerMap<>();
        for (final Unit unit : CollectionUtils.getMatches(nonCom, Matches.unitWhenCapturedSustainsDamage())) {
            final TripleAUnit taUnit = (TripleAUnit) unit;
            final int damageLimit = taUnit.getHowMuchMoreDamageCanThisUnitTake(unit, territory);
            final int sustainedDamage = UnitAttachment.get(unit.getType()).getWhenCapturedSustainsDamage();
            final int actualDamage = Math.max(0, Math.min(sustainedDamage, damageLimit));
            final int totalDamage = taUnit.getUnitDamage() + actualDamage;
            damageMap.put(unit, totalDamage);
        }
        if (!damageMap.isEmpty()) {
            final Change damageChange = ChangeFactory.bombingUnitDamage(damageMap);
            bridge.addChange(damageChange);
            if (changeTracker != null) {
                changeTracker.addChange(damageChange);
            }
            // Kill any units that can die if they have reached max damage
            if (damageMap.keySet().stream().anyMatch(Matches.unitCanDieFromReachingMaxDamage())) {
                final List<Unit> unitsCanDie = CollectionUtils.getMatches(damageMap.keySet(), Matches.unitCanDieFromReachingMaxDamage());
                unitsCanDie.retainAll(CollectionUtils.getMatches(unitsCanDie, Matches.unitIsAtMaxDamageOrNotCanBeDamaged(territory)));
                if (!unitsCanDie.isEmpty()) {
                    final Change removeDead = ChangeFactory.removeUnits(territory, unitsCanDie);
                    bridge.addChange(removeDead);
                    if (changeTracker != null) {
                        changeTracker.addChange(removeDead);
                    }
                }
            }
        }
    }
}
Also used : IntegerMap(games.strategy.util.IntegerMap) PlayerID(games.strategy.engine.data.PlayerID) GameData(games.strategy.engine.data.GameData) ArrayList(java.util.ArrayList) CompositeChange(games.strategy.engine.data.CompositeChange) Change(games.strategy.engine.data.Change) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) TripleAUnit(games.strategy.triplea.TripleAUnit) UnitType(games.strategy.engine.data.UnitType) CompositeChange(games.strategy.engine.data.CompositeChange) Tuple(games.strategy.util.Tuple)

Aggregations

IntegerMap (games.strategy.util.IntegerMap)132 UnitType (games.strategy.engine.data.UnitType)87 Test (org.junit.jupiter.api.Test)73 Route (games.strategy.engine.data.Route)66 Unit (games.strategy.engine.data.Unit)53 Territory (games.strategy.engine.data.Territory)39 ArrayList (java.util.ArrayList)35 PlayerID (games.strategy.engine.data.PlayerID)26 TripleAUnit (games.strategy.triplea.TripleAUnit)24 HashMap (java.util.HashMap)23 HashSet (java.util.HashSet)19 Resource (games.strategy.engine.data.Resource)16 GameData (games.strategy.engine.data.GameData)15 ProductionRule (games.strategy.engine.data.ProductionRule)14 Collection (java.util.Collection)12 List (java.util.List)12 UnitAttachment (games.strategy.triplea.attachments.UnitAttachment)10 Set (java.util.Set)10 RepairRule (games.strategy.engine.data.RepairRule)9 NamedAttachable (games.strategy.engine.data.NamedAttachable)7