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;
}
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;
}
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);
}
}
}
}
}
}
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);
}
}
}
}
}
}
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));
}
Aggregations