package games.strategy.triplea.ai.proAI; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import games.strategy.engine.data.GameData; import games.strategy.engine.data.PlayerID; import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; import games.strategy.net.GUID; import games.strategy.triplea.ai.proAI.data.ProBattleResult; import games.strategy.triplea.ai.proAI.logging.ProLogger; import games.strategy.triplea.ai.proAI.util.ProBattleUtils; import games.strategy.triplea.ai.proAI.util.ProMatches; import games.strategy.triplea.ai.proAI.util.ProOddsCalculator; import games.strategy.triplea.attachments.TerritoryAttachment; import games.strategy.triplea.delegate.BattleDelegate; import games.strategy.triplea.delegate.DelegateFinder; import games.strategy.triplea.delegate.IBattle; import games.strategy.triplea.delegate.Matches; import games.strategy.util.Match; /** * Pro retreat AI. * <ol> * <li>Consider whether submerging increases/decreases TUV swing</li> * <li>Consider what territory needs units when retreating</li> * </ol> * AFAIK there are 2 options available for maps (land battles): * 1. air can retreat separately on an amphib attack * 2. non-amphib land can retreat separately * So the result would be 4 situations: * 1. revised: you can't retreat anything on amphib * 2. only air can retreat on amphib * 3. only non-amphib land can retreat on amphib * 4. aa50: air and non-amphib land can retreat on amphib * Check by following TripleA.Constants -> TripleA.Properties statis get methods -> MustFightBattle * For sea battles you can have: * 1. attacker retreats all units at end of battle * 2. attacker submerges sub at start or end of battle * 3. defender submerges (or moves if Classic rules) sub at start or end of battle */ public class ProRetreatAI { private final ProOddsCalculator calc; public ProRetreatAI(final ProAI ai) { calc = ai.getCalc(); } public Territory retreatQuery(final GUID battleID, final boolean submerge, final Territory battleTerritory, final Collection<Territory> possibleTerritories, final String message) { // Get battle data final GameData data = ProData.getData(); final PlayerID player = ProData.getPlayer(); final BattleDelegate delegate = DelegateFinder.battleDelegate(data); final IBattle battle = delegate.getBattleTracker().getPendingBattle(battleID); // Get units and determine if attacker final boolean isAttacker = player.equals(battle.getAttacker()); final List<Unit> attackers = (List<Unit>) battle.getAttackingUnits(); final List<Unit> defenders = (List<Unit>) battle.getDefendingUnits(); // Calculate battle results final ProBattleResult result = calc.calculateBattleResults(player, battleTerritory, attackers, defenders, new HashSet<>(), isAttacker); // Determine if it has a factory int isFactory = 0; if (ProMatches.territoryHasInfraFactoryAndIsLand(player).match(battleTerritory)) { isFactory = 1; } // Determine production value and if it is a capital int production = 0; int isCapital = 0; final TerritoryAttachment ta = TerritoryAttachment.get(battleTerritory); if (ta != null) { production = ta.getProduction(); if (ta.isCapital()) { isCapital = 1; } } // Calculate current attack value double territoryValue = 0; if (result.isHasLandUnitRemaining() || Match.noneMatch(attackers, Matches.UnitIsAir)) { territoryValue = result.getWinPercentage() / 100 * (2 * production * (1 + isFactory) * (1 + isCapital)); } double battleValue = result.getTUVSwing() + territoryValue; if (!isAttacker) { battleValue = -battleValue; } // Decide if we should retreat if (battleValue < 0) { // Retreat to capital if available otherwise the territory with highest defense strength Territory retreatTerritory = null; double maxStrength = Double.NEGATIVE_INFINITY; final Territory myCapital = TerritoryAttachment.getFirstOwnedCapitalOrFirstUnownedCapital(player, data); for (final Territory t : possibleTerritories) { if (t.equals(myCapital)) { retreatTerritory = t; break; } final double strength = ProBattleUtils.estimateStrength(t, t.getUnits().getMatches(Matches.isUnitAllied(player, data)), new ArrayList<>(), false); if (strength > maxStrength) { retreatTerritory = t; maxStrength = strength; } } ProLogger.debug(player.getName() + " retreating from territory " + battleTerritory + " to " + retreatTerritory + " because AttackValue=" + battleValue + ", TUVSwing=" + result.getTUVSwing() + ", possibleTerritories=" + possibleTerritories.size()); return retreatTerritory; } ProLogger.debug(player.getName() + " not retreating from territory " + battleTerritory + " with AttackValue=" + battleValue + ", TUVSwing=" + result.getTUVSwing()); return null; } }