Search in sources :

Example 1 with ITripleAPlayer

use of games.strategy.triplea.player.ITripleAPlayer 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 2 with ITripleAPlayer

use of games.strategy.triplea.player.ITripleAPlayer in project triplea by triplea-game.

the class BattleDelegate method selectBombardingBattle.

/**
 * Select which territory to bombard.
 */
private IBattle selectBombardingBattle(final Unit u, final Territory unitTerritory, final Collection<IBattle> battles) {
    final boolean bombardRestricted = isShoreBombardPerGroundUnitRestricted(getData());
    // If only one battle to select from just return that battle
    if ((battles.size() == 1)) {
        return battles.iterator().next();
    }
    final List<Territory> territories = new ArrayList<>();
    final Map<Territory, IBattle> battleTerritories = new HashMap<>();
    for (final IBattle battle : battles) {
        // If Restricted & # of bombarding units => landing units, don't add territory to list to bombard
        if (bombardRestricted) {
            if (battle.getBombardingUnits().size() < battle.getAmphibiousLandAttackers().size()) {
                territories.add(battle.getTerritory());
            }
        } else {
            territories.add(battle.getTerritory());
        }
        battleTerritories.put(battle.getTerritory(), battle);
    }
    final ITripleAPlayer remotePlayer = getRemotePlayer();
    Territory bombardingTerritory = null;
    if (!territories.isEmpty()) {
        bombardingTerritory = remotePlayer.selectBombardingTerritory(u, unitTerritory, territories, true);
    }
    if (bombardingTerritory != null) {
        return battleTerritories.get(bombardingTerritory);
    }
    // User elected not to bombard with this unit
    return null;
}
Also used : Territory(games.strategy.engine.data.Territory) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) ITripleAPlayer(games.strategy.triplea.player.ITripleAPlayer)

Example 3 with ITripleAPlayer

use of games.strategy.triplea.player.ITripleAPlayer in project triplea by triplea-game.

the class BattleDelegate method addBombardmentSources.

/**
 * Add bombardment units to battles. Made public for test purposes only.
 */
void addBombardmentSources() {
    final PlayerID attacker = bridge.getPlayerId();
    final ITripleAPlayer remotePlayer = getRemotePlayer();
    final Predicate<Unit> ownedAndCanBombard = Matches.unitCanBombard(attacker).and(Matches.unitIsOwnedBy(attacker));
    final Map<Territory, Collection<IBattle>> adjBombardment = getPossibleBombardingTerritories();
    final boolean shoreBombardPerGroundUnitRestricted = isShoreBombardPerGroundUnitRestricted(getData());
    for (final Territory t : adjBombardment.keySet()) {
        if (!battleTracker.hasPendingBattle(t, false)) {
            Collection<IBattle> battles = adjBombardment.get(t);
            battles = CollectionUtils.getMatches(battles, Matches.battleIsAmphibious());
            if (!battles.isEmpty()) {
                final Collection<Unit> bombardUnits = t.getUnits().getMatches(ownedAndCanBombard);
                final List<Unit> listedBombardUnits = new ArrayList<>(bombardUnits);
                sortUnitsToBombard(listedBombardUnits, attacker);
                if (!bombardUnits.isEmpty()) {
                    // ask if they want to bombard
                    if (!remotePlayer.selectShoreBombard(t)) {
                        continue;
                    }
                }
                for (final Unit u : listedBombardUnits) {
                    final IBattle battle = selectBombardingBattle(u, t, battles);
                    if (battle != null) {
                        if (shoreBombardPerGroundUnitRestricted) {
                            if (battle.getAmphibiousLandAttackers().size() <= battle.getBombardingUnits().size()) {
                                battles.remove(battle);
                                break;
                            }
                        }
                        battle.addBombardingUnit(u);
                    }
                }
            }
        }
    }
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) Territory(games.strategy.engine.data.Territory) ArrayList(java.util.ArrayList) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) ITripleAPlayer(games.strategy.triplea.player.ITripleAPlayer) Collection(java.util.Collection) ResourceCollection(games.strategy.engine.data.ResourceCollection)

Example 4 with ITripleAPlayer

use of games.strategy.triplea.player.ITripleAPlayer in project triplea by triplea-game.

the class BattleDelegate method setupUnitsInSameTerritoryBattles.

/**
 * Setup the battles where the battle occurs because units are in the
 * same territory. This happens when subs emerge (after being submerged), and
 * when naval units are placed in enemy occupied sea zones, and also
 * when political relationships change and potentially leave units in now-hostile territories.
 */
private static void setupUnitsInSameTerritoryBattles(final BattleTracker battleTracker, final IDelegateBridge bridge) {
    final PlayerID player = bridge.getPlayerId();
    final GameData data = bridge.getData();
    final boolean ignoreTransports = isIgnoreTransportInMovement(data);
    final boolean ignoreSubs = isIgnoreSubInMovement(data);
    final Predicate<Unit> seaTransports = Matches.unitIsTransportButNotCombatTransport().and(Matches.unitIsSea());
    final Predicate<Unit> seaTranportsOrSubs = seaTransports.or(Matches.unitIsSub());
    // we want to match all sea zones with our units and enemy units
    final Predicate<Territory> anyTerritoryWithOwnAndEnemy = Matches.territoryHasUnitsOwnedBy(player).and(Matches.territoryHasEnemyUnits(player, data));
    final Predicate<Territory> enemyTerritoryAndOwnUnits = Matches.isTerritoryEnemyAndNotUnownedWater(player, data).and(Matches.territoryHasUnitsOwnedBy(player));
    final Predicate<Territory> enemyUnitsOrEnemyTerritory = anyTerritoryWithOwnAndEnemy.or(enemyTerritoryAndOwnUnits);
    final List<Territory> battleTerritories = CollectionUtils.getMatches(data.getMap().getTerritories(), enemyUnitsOrEnemyTerritory);
    for (final Territory territory : battleTerritories) {
        final List<Unit> attackingUnits = territory.getUnits().getMatches(Matches.unitIsOwnedBy(player));
        // now make sure to add any units that must move with these attacking units, so that they get included as
        // dependencies
        final Map<Unit, Collection<Unit>> transportMap = TransportTracker.transporting(territory.getUnits());
        final HashSet<Unit> dependants = new HashSet<>();
        for (final Entry<Unit, Collection<Unit>> entry : transportMap.entrySet()) {
            // only consider those transports that we are attacking with. allied and enemy transports are not added.
            if (attackingUnits.contains(entry.getKey())) {
                dependants.addAll(entry.getValue());
            }
        }
        // no duplicates
        dependants.removeAll(attackingUnits);
        // add the dependants to the attacking list
        attackingUnits.addAll(dependants);
        final List<Unit> enemyUnits = territory.getUnits().getMatches(Matches.enemyUnit(player, data));
        final IBattle bombingBattle = battleTracker.getPendingBattle(territory, true, null);
        if (bombingBattle != null) {
            // we need to remove any units which are participating in bombing raids
            attackingUnits.removeAll(bombingBattle.getAttackingUnits());
        }
        if (attackingUnits.stream().allMatch(Matches.unitIsInfrastructure())) {
            continue;
        }
        IBattle battle = battleTracker.getPendingBattle(territory, false, BattleType.NORMAL);
        if (battle == null) {
            // fires)
            if (enemyUnits.stream().allMatch(Matches.unitIsInfrastructure())) {
                landParatroopers(player, territory, bridge);
            }
            bridge.getHistoryWriter().startEvent(player.getName() + " creates battle in territory " + territory.getName());
            battleTracker.addBattle(new RouteScripted(territory), attackingUnits, player, bridge, null, null);
            battle = battleTracker.getPendingBattle(territory, false, BattleType.NORMAL);
        }
        if (battle == null) {
            continue;
        }
        if (bombingBattle != null) {
            battleTracker.addDependency(battle, bombingBattle);
        }
        if (battle.isEmpty()) {
            battle.addAttackChange(new RouteScripted(territory), attackingUnits, null);
        }
        if (!battle.getAttackingUnits().containsAll(attackingUnits)) {
            List<Unit> attackingUnitsNeedToBeAdded = new ArrayList<>(attackingUnits);
            attackingUnitsNeedToBeAdded.removeAll(battle.getAttackingUnits());
            attackingUnitsNeedToBeAdded.removeAll(battle.getDependentUnits(battle.getAttackingUnits()));
            if (territory.isWater()) {
                attackingUnitsNeedToBeAdded = CollectionUtils.getMatches(attackingUnitsNeedToBeAdded, Matches.unitIsLand().negate());
            } else {
                attackingUnitsNeedToBeAdded = CollectionUtils.getMatches(attackingUnitsNeedToBeAdded, Matches.unitIsSea().negate());
            }
            if (!attackingUnitsNeedToBeAdded.isEmpty()) {
                battle.addAttackChange(new RouteScripted(territory), attackingUnitsNeedToBeAdded, null);
            }
        }
        // Reach stalemate if all attacking and defending units are transports
        if ((ignoreTransports && !attackingUnits.isEmpty() && attackingUnits.stream().allMatch(seaTransports) && !enemyUnits.isEmpty() && enemyUnits.stream().allMatch(seaTransports)) || (!attackingUnits.isEmpty() && attackingUnits.stream().allMatch(Matches.unitHasAttackValueOfAtLeast(1).negate()) && !enemyUnits.isEmpty() && enemyUnits.stream().allMatch(Matches.unitHasDefendValueOfAtLeast(1).negate()))) {
            final BattleResults results = new BattleResults(battle, WhoWon.DRAW, data);
            battleTracker.getBattleRecords().addResultToBattle(player, battle.getBattleId(), null, 0, 0, BattleRecord.BattleResultDescription.STALEMATE, results);
            battle.cancelBattle(bridge);
            battleTracker.removeBattle(battle);
            continue;
        }
        // possibility to ignore battle altogether
        if (!attackingUnits.isEmpty()) {
            final ITripleAPlayer remotePlayer = getRemotePlayer(bridge);
            if (territory.isWater() && Properties.getSeaBattlesMayBeIgnored(data)) {
                if (!remotePlayer.selectAttackUnits(territory)) {
                    final BattleResults results = new BattleResults(battle, WhoWon.NOTFINISHED, data);
                    battleTracker.getBattleRecords().addResultToBattle(player, battle.getBattleId(), null, 0, 0, BattleRecord.BattleResultDescription.NO_BATTLE, results);
                    battle.cancelBattle(bridge);
                    battleTracker.removeBattle(battle);
                }
                continue;
            }
            // Check for ignored units
            if (ignoreTransports || ignoreSubs) {
                // if only enemy transports... attack them?
                if (ignoreTransports && !enemyUnits.isEmpty() && enemyUnits.stream().allMatch(seaTransports)) {
                    if (!remotePlayer.selectAttackTransports(territory)) {
                        final BattleResults results = new BattleResults(battle, WhoWon.NOTFINISHED, data);
                        battleTracker.getBattleRecords().addResultToBattle(player, battle.getBattleId(), null, 0, 0, BattleRecord.BattleResultDescription.NO_BATTLE, results);
                        battle.cancelBattle(bridge);
                        battleTracker.removeBattle(battle);
                    }
                    continue;
                }
                // if only enemy subs... attack them?
                if (ignoreSubs && !enemyUnits.isEmpty() && enemyUnits.stream().allMatch(Matches.unitIsSub())) {
                    if (!remotePlayer.selectAttackSubs(territory)) {
                        final BattleResults results = new BattleResults(battle, WhoWon.NOTFINISHED, data);
                        battleTracker.getBattleRecords().addResultToBattle(player, battle.getBattleId(), null, 0, 0, BattleRecord.BattleResultDescription.NO_BATTLE, results);
                        battle.cancelBattle(bridge);
                        battleTracker.removeBattle(battle);
                    }
                    continue;
                }
                // if only enemy transports and subs... attack them?
                if (ignoreSubs && ignoreTransports && !enemyUnits.isEmpty() && enemyUnits.stream().allMatch(seaTranportsOrSubs)) {
                    if (!remotePlayer.selectAttackUnits(territory)) {
                        final BattleResults results = new BattleResults(battle, WhoWon.NOTFINISHED, data);
                        battleTracker.getBattleRecords().addResultToBattle(player, battle.getBattleId(), null, 0, 0, BattleRecord.BattleResultDescription.NO_BATTLE, results);
                        battle.cancelBattle(bridge);
                        battleTracker.removeBattle(battle);
                    }
                }
            }
        }
    }
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) Territory(games.strategy.engine.data.Territory) GameData(games.strategy.engine.data.GameData) BattleResults(games.strategy.triplea.oddsCalculator.ta.BattleResults) ArrayList(java.util.ArrayList) RouteScripted(games.strategy.engine.data.RouteScripted) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) ITripleAPlayer(games.strategy.triplea.player.ITripleAPlayer) Collection(java.util.Collection) ResourceCollection(games.strategy.engine.data.ResourceCollection) HashSet(java.util.HashSet)

Example 5 with ITripleAPlayer

use of games.strategy.triplea.player.ITripleAPlayer in project triplea by triplea-game.

the class WW2V3Year42Test method testBombAndAttackEmptyTerritory.

@Test
public void testBombAndAttackEmptyTerritory() {
    final Territory karrelia = territory("Karelia S.S.R.", gameData);
    final Territory baltic = territory("Baltic States", gameData);
    final Territory sz5 = territory("5 Sea Zone", gameData);
    final Territory germany = territory("Germany", gameData);
    final PlayerID germans = germans(gameData);
    final MoveDelegate moveDelegate = (MoveDelegate) gameData.getDelegateList().getDelegate("move");
    final ITestDelegateBridge bridge = getDelegateBridge(germans);
    bridge.setStepName("CombatMove");
    moveDelegate.setDelegateBridgeAndPlayer(bridge);
    moveDelegate.start();
    final ITripleAPlayer dummyPlayer = mock(ITripleAPlayer.class);
    when(dummyPlayer.shouldBomberBomb(any())).thenReturn(true);
    bridge.setRemote(dummyPlayer);
    // remove the russian units
    removeFrom(karrelia, karrelia.getUnits().getMatches(Matches.unitCanBeDamaged().negate()));
    // move the bomber to attack
    move(germany.getUnits().getMatches(Matches.unitIsStrategicBomber()), new Route(germany, sz5, karrelia));
    // move an infantry to invade
    move(baltic.getUnits().getMatches(Matches.unitIsLandTransportable()), new Route(baltic, karrelia));
    final BattleTracker battleTracker = MoveDelegate.getBattleTracker(gameData);
    // we should have a pending land battle, and a pending bombing raid
    assertNotNull(battleTracker.getPendingBattle(karrelia, false, null));
    assertNotNull(battleTracker.getPendingBattle(karrelia, true, null));
    // the territory should not be conquered
    assertEquals(karrelia.getOwner(), russians(gameData));
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) Territory(games.strategy.engine.data.Territory) ITestDelegateBridge(games.strategy.engine.data.ITestDelegateBridge) ITripleAPlayer(games.strategy.triplea.player.ITripleAPlayer) Route(games.strategy.engine.data.Route) Test(org.junit.jupiter.api.Test)

Aggregations

ITripleAPlayer (games.strategy.triplea.player.ITripleAPlayer)14 GameData (games.strategy.engine.data.GameData)8 PlayerID (games.strategy.engine.data.PlayerID)8 Territory (games.strategy.engine.data.Territory)8 Unit (games.strategy.engine.data.Unit)8 ArrayList (java.util.ArrayList)8 TripleAUnit (games.strategy.triplea.TripleAUnit)7 Change (games.strategy.engine.data.Change)6 UnitType (games.strategy.engine.data.UnitType)6 UnitAttachment (games.strategy.triplea.attachments.UnitAttachment)6 ITestDelegateBridge (games.strategy.engine.data.ITestDelegateBridge)5 Route (games.strategy.engine.data.Route)5 List (java.util.List)5 Test (org.junit.jupiter.api.Test)5 ChangeFactory (games.strategy.engine.data.changefactory.ChangeFactory)4 ScriptedRandomSource (games.strategy.engine.random.ScriptedRandomSource)4 Constants (games.strategy.triplea.Constants)4 GameDataTestUtil.addTo (games.strategy.triplea.delegate.GameDataTestUtil.addTo)4 TestMapGameData (games.strategy.triplea.xml.TestMapGameData)4 Proxy (java.lang.reflect.Proxy)4