use of games.strategy.triplea.ai.pro.data.ProBattleResult in project triplea by triplea-game.
the class ProPurchaseAi method purchaseFactory.
private void purchaseFactory(final Map<Territory, ProPurchaseTerritory> factoryPurchaseTerritories, final Map<Territory, ProPurchaseTerritory> purchaseTerritories, final List<ProPlaceTerritory> prioritizedLandTerritories, final ProPurchaseOptionMap purchaseOptions, final boolean hasExtraPUs) {
if (resourceTracker.isEmpty()) {
return;
}
ProLogger.info("Purchase factory with resources: " + resourceTracker + ", hasExtraPUs=" + hasExtraPUs);
final ProOtherMoveOptions enemyAttackOptions = territoryManager.getEnemyAttackOptions();
// Only try to purchase a factory if all production was used in prioritized land territories
for (final ProPlaceTerritory placeTerritory : prioritizedLandTerritories) {
for (final Territory t : purchaseTerritories.keySet()) {
if (placeTerritory.getTerritory().equals(t) && purchaseTerritories.get(t).getRemainingUnitProduction() > 0) {
ProLogger.debug("Not purchasing a factory since remaining land production in " + t);
return;
}
}
}
// Find all owned land territories that weren't conquered and don't already have a factory
final List<Territory> possibleFactoryTerritories = CollectionUtils.getMatches(data.getMap().getTerritories(), ProMatches.territoryHasNoInfraFactoryAndIsNotConqueredOwnedLand(player, data));
possibleFactoryTerritories.removeAll(factoryPurchaseTerritories.keySet());
final Set<Territory> purchaseFactoryTerritories = new HashSet<>();
final List<Territory> territoriesThatCantBeHeld = new ArrayList<>();
for (final Territory t : possibleFactoryTerritories) {
// Only consider territories with production of at least 3 unless there are still remaining PUs
final int production = TerritoryAttachment.get(t).getProduction();
if ((production < 3 && !hasExtraPUs) || production < 2) {
continue;
}
// Check if no enemy attackers and that it wasn't conquered this turn
if (enemyAttackOptions.getMax(t) == null) {
purchaseFactoryTerritories.add(t);
ProLogger.trace("Possible factory since no enemy attackers: " + t.getName());
} else {
// Find current battle result
final List<Unit> defenders = t.getUnits().getMatches(Matches.isUnitAllied(player, data));
final Set<Unit> enemyAttackingUnits = new HashSet<>(enemyAttackOptions.getMax(t).getMaxUnits());
enemyAttackingUnits.addAll(enemyAttackOptions.getMax(t).getMaxAmphibUnits());
final ProBattleResult result = calc.estimateDefendBattleResults(t, new ArrayList<>(enemyAttackingUnits), defenders, enemyAttackOptions.getMax(t).getMaxBombardUnits());
// Check if it can't be held or if it can then that it wasn't conquered this turn
if (result.isHasLandUnitRemaining() || result.getTuvSwing() > 0) {
territoriesThatCantBeHeld.add(t);
ProLogger.trace("Can't hold territory: " + t.getName() + ", hasLandUnitRemaining=" + result.isHasLandUnitRemaining() + ", TUVSwing=" + result.getTuvSwing() + ", enemyAttackers=" + enemyAttackingUnits.size() + ", myDefenders=" + defenders.size());
} else {
purchaseFactoryTerritories.add(t);
ProLogger.trace("Possible factory: " + t.getName() + ", hasLandUnitRemaining=" + result.isHasLandUnitRemaining() + ", TUVSwing=" + result.getTuvSwing() + ", enemyAttackers=" + enemyAttackingUnits.size() + ", myDefenders=" + defenders.size());
}
}
}
ProLogger.debug("Possible factory territories: " + purchaseFactoryTerritories);
// Remove any territories that don't have local land superiority
if (!hasExtraPUs) {
purchaseFactoryTerritories.removeIf(t -> !ProBattleUtils.territoryHasLocalLandSuperiority(t, ProBattleUtils.MEDIUM_RANGE, player, purchaseTerritories));
ProLogger.debug("Possible factory territories that have land superiority: " + purchaseFactoryTerritories);
}
// Find strategic value for each territory
final Map<Territory, Double> territoryValueMap = ProTerritoryValueUtils.findTerritoryValues(player, territoriesThatCantBeHeld, new ArrayList<>());
double maxValue = 0.0;
Territory maxTerritory = null;
for (final Territory t : purchaseFactoryTerritories) {
final int production = TerritoryAttachment.get(t).getProduction();
final double value = territoryValueMap.get(t) * production + 0.1 * production;
final boolean isAdjacentToSea = Matches.territoryHasNeighborMatching(data, Matches.territoryIsWater()).test(t);
final Set<Territory> nearbyLandTerritories = data.getMap().getNeighbors(t, 9, ProMatches.territoryCanMoveLandUnits(player, data, false));
final int numNearbyEnemyTerritories = CollectionUtils.countMatches(nearbyLandTerritories, Matches.isTerritoryEnemy(player, data));
ProLogger.trace(t + ", strategic value=" + territoryValueMap.get(t) + ", value=" + value + ", numNearbyEnemyTerritories=" + numNearbyEnemyTerritories);
if (value > maxValue && ((numNearbyEnemyTerritories >= 4 && territoryValueMap.get(t) >= 1) || (isAdjacentToSea && hasExtraPUs))) {
maxValue = value;
maxTerritory = t;
}
}
ProLogger.debug("Try to purchase factory for territory: " + maxTerritory);
// Determine whether to purchase factory
if (maxTerritory != null) {
// Find most expensive placed land unit to consider removing for a factory
ProPurchaseOption maxPlacedOption = null;
ProPlaceTerritory maxPlacedTerritory = null;
Unit maxPlacedUnit = null;
for (final ProPlaceTerritory placeTerritory : prioritizedLandTerritories) {
for (final Unit u : placeTerritory.getPlaceUnits()) {
for (final ProPurchaseOption ppo : purchaseOptions.getLandOptions()) {
if (u.getType().equals(ppo.getUnitType()) && ppo.getQuantity() == 1 && (maxPlacedOption == null || ppo.getCost() >= maxPlacedOption.getCost())) {
maxPlacedOption = ppo;
maxPlacedTerritory = placeTerritory;
maxPlacedUnit = u;
}
}
}
}
// Determine units that can be produced in this territory
final List<ProPurchaseOption> purchaseOptionsForTerritory = ProPurchaseUtils.findPurchaseOptionsForTerritory(player, purchaseOptions.getFactoryOptions(), maxTerritory, isBid);
resourceTracker.removeTempPurchase(maxPlacedOption);
ProPurchaseUtils.removeInvalidPurchaseOptions(player, startOfTurnData, purchaseOptionsForTerritory, resourceTracker, 1, new ArrayList<>(), purchaseTerritories);
resourceTracker.clearTempPurchases();
// Determine most expensive factory option (currently doesn't buy mobile factories)
ProPurchaseOption bestFactoryOption = null;
double maxFactoryEfficiency = 0;
for (final ProPurchaseOption ppo : purchaseOptionsForTerritory) {
if (ppo.getMovement() == 0 && ppo.getCost() > maxFactoryEfficiency) {
bestFactoryOption = ppo;
maxFactoryEfficiency = ppo.getCost();
}
}
// Check if there are enough PUs to buy a factory
if (bestFactoryOption != null) {
ProLogger.debug("Best factory unit: " + bestFactoryOption.getUnitType().getName());
final ProPurchaseTerritory factoryPurchaseTerritory = new ProPurchaseTerritory(maxTerritory, data, player, 0);
factoryPurchaseTerritories.put(maxTerritory, factoryPurchaseTerritory);
for (final ProPlaceTerritory ppt : factoryPurchaseTerritory.getCanPlaceTerritories()) {
if (ppt.getTerritory().equals(maxTerritory)) {
final List<Unit> factory = bestFactoryOption.getUnitType().create(bestFactoryOption.getQuantity(), player, true);
ppt.getPlaceUnits().addAll(factory);
if (resourceTracker.hasEnough(bestFactoryOption)) {
resourceTracker.purchase(bestFactoryOption);
ProLogger.debug(maxTerritory + ", placedFactory=" + factory);
} else {
resourceTracker.purchase(bestFactoryOption);
resourceTracker.removePurchase(maxPlacedOption);
maxPlacedTerritory.getPlaceUnits().remove(maxPlacedUnit);
ProLogger.debug(maxTerritory + ", placedFactory=" + factory + ", removedUnit=" + maxPlacedUnit);
}
}
}
}
}
}
use of games.strategy.triplea.ai.pro.data.ProBattleResult in project triplea by triplea-game.
the class ProPurchaseAi method placeDefenders.
private void placeDefenders(final Map<Territory, ProPurchaseTerritory> placeNonConstructionTerritories, final List<ProPlaceTerritory> needToDefendTerritories, final IAbstractPlaceDelegate placeDelegate) {
ProLogger.info("Place defenders with units=" + player.getUnits().getUnits());
final ProOtherMoveOptions enemyAttackOptions = territoryManager.getEnemyAttackOptions();
// Loop through prioritized territories and purchase defenders
for (final ProPlaceTerritory placeTerritory : needToDefendTerritories) {
final Territory t = placeTerritory.getTerritory();
ProLogger.debug("Placing defenders for " + t.getName() + ", enemyAttackers=" + enemyAttackOptions.getMax(t).getMaxUnits() + ", amphibEnemyAttackers=" + enemyAttackOptions.getMax(t).getMaxAmphibUnits() + ", defenders=" + placeTerritory.getDefendingUnits());
// Check if any units can be placed
final PlaceableUnits placeableUnits = placeDelegate.getPlaceableUnits(player.getUnits().getMatches(Matches.unitIsNotConstruction()), t);
if (placeableUnits.isError()) {
ProLogger.trace(t + " can't place units with error: " + placeableUnits.getErrorMessage());
continue;
}
// Find remaining unit production
int remainingUnitProduction = placeableUnits.getMaxUnits();
if (remainingUnitProduction == -1) {
remainingUnitProduction = Integer.MAX_VALUE;
}
ProLogger.trace(t + ", remainingUnitProduction=" + remainingUnitProduction);
// Place defenders and check battle results
final List<Unit> unitsThatCanBePlaced = new ArrayList<>(placeableUnits.getUnits());
final int landPlaceCount = Math.min(remainingUnitProduction, unitsThatCanBePlaced.size());
final List<Unit> unitsToPlace = new ArrayList<>();
ProBattleResult finalResult = new ProBattleResult();
for (int i = 0; i < landPlaceCount; i++) {
// Add defender
unitsToPlace.add(unitsThatCanBePlaced.get(i));
// Find current battle result
final Set<Unit> enemyAttackingUnits = new HashSet<>(enemyAttackOptions.getMax(t).getMaxUnits());
enemyAttackingUnits.addAll(enemyAttackOptions.getMax(t).getMaxAmphibUnits());
final List<Unit> defenders = new ArrayList<>(placeTerritory.getDefendingUnits());
defenders.addAll(unitsToPlace);
finalResult = calc.calculateBattleResults(t, new ArrayList<>(enemyAttackingUnits), defenders, enemyAttackOptions.getMax(t).getMaxBombardUnits());
// Break if it can be held
if ((!t.equals(ProData.myCapital) && !finalResult.isHasLandUnitRemaining() && finalResult.getTuvSwing() <= 0) || (t.equals(ProData.myCapital) && finalResult.getWinPercentage() < (100 - ProData.winPercentage) && finalResult.getTuvSwing() <= 0)) {
break;
}
}
// Check to see if its worth trying to defend the territory
if (!finalResult.isHasLandUnitRemaining() || finalResult.getTuvSwing() < placeTerritory.getMinBattleResult().getTuvSwing() || t.equals(ProData.myCapital)) {
ProLogger.trace(t + ", placedUnits=" + unitsToPlace + ", TUVSwing=" + finalResult.getTuvSwing());
doPlace(t, unitsToPlace, placeDelegate);
} else {
setCantHoldPlaceTerritory(placeTerritory, placeNonConstructionTerritories);
ProLogger.trace(t + ", unable to defend with placedUnits=" + unitsToPlace + ", TUVSwing=" + finalResult.getTuvSwing() + ", minTUVSwing=" + placeTerritory.getMinBattleResult().getTuvSwing());
}
}
}
use of games.strategy.triplea.ai.pro.data.ProBattleResult in project triplea by triplea-game.
the class ProRetreatAi method retreatQuery.
Territory retreatQuery(final GUID battleId, final Territory battleTerritory, final Collection<Territory> possibleTerritories) {
// 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(battleTerritory, attackers, defenders, new HashSet<>());
// Determine if it has a factory
int isFactory = 0;
if (ProMatches.territoryHasInfraFactoryAndIsLand().test(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() || attackers.stream().noneMatch(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;
}
use of games.strategy.triplea.ai.pro.data.ProBattleResult in project triplea by triplea-game.
the class ProScrambleAi method scrambleUnitsQuery.
HashMap<Territory, Collection<Unit>> scrambleUnitsQuery(final Territory scrambleTo, final Map<Territory, Tuple<Collection<Unit>, Collection<Unit>>> possibleScramblers) {
// 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(scrambleTo, false, BattleType.NORMAL);
// Check if defense already wins
final List<Unit> attackers = (List<Unit>) battle.getAttackingUnits();
final List<Unit> defenders = (List<Unit>) battle.getDefendingUnits();
final Set<Unit> bombardingUnits = new HashSet<>(battle.getBombardingUnits());
final ProBattleResult minResult = calc.calculateBattleResults(scrambleTo, attackers, defenders, bombardingUnits);
ProLogger.debug(scrambleTo + ", minTUVSwing=" + minResult.getTuvSwing() + ", minWin%=" + minResult.getWinPercentage());
if (minResult.getTuvSwing() <= 0 && minResult.getWinPercentage() < (100 - ProData.minWinPercentage)) {
return null;
}
// Check if max defense is worse
final Set<Unit> allScramblers = new HashSet<>();
final Map<Territory, List<Unit>> possibleMaxScramblerMap = new HashMap<>();
for (final Territory t : possibleScramblers.keySet()) {
final int maxCanScramble = BattleDelegate.getMaxScrambleCount(possibleScramblers.get(t).getFirst());
List<Unit> canScrambleAir = new ArrayList<>(possibleScramblers.get(t).getSecond());
if (maxCanScramble < canScrambleAir.size()) {
canScrambleAir.sort(Comparator.comparingDouble(o -> ProBattleUtils.estimateStrength(scrambleTo, Collections.singletonList(o), new ArrayList<>(), false)));
canScrambleAir = canScrambleAir.subList(0, maxCanScramble);
}
allScramblers.addAll(canScrambleAir);
possibleMaxScramblerMap.put(t, canScrambleAir);
}
defenders.addAll(allScramblers);
final ProBattleResult maxResult = calc.calculateBattleResults(scrambleTo, attackers, defenders, bombardingUnits);
ProLogger.debug(scrambleTo + ", maxTUVSwing=" + maxResult.getTuvSwing() + ", maxWin%=" + maxResult.getWinPercentage());
if (maxResult.getTuvSwing() >= minResult.getTuvSwing()) {
return null;
}
// Loop through all units and determine attack options
final Map<Unit, Set<Territory>> unitDefendOptions = new HashMap<>();
for (final Territory t : possibleMaxScramblerMap.keySet()) {
final Set<Territory> possibleTerritories = data.getMap().getNeighbors(t, ProMatches.territoryCanMoveSeaUnits(player, data, true));
possibleTerritories.add(t);
final Set<Territory> battleTerritories = new HashSet<>();
for (final Territory possibleTerritory : possibleTerritories) {
final IBattle possibleBattle = delegate.getBattleTracker().getPendingBattle(possibleTerritory, false, BattleType.NORMAL);
if (possibleBattle != null) {
battleTerritories.add(possibleTerritory);
}
}
for (final Unit u : possibleMaxScramblerMap.get(t)) {
unitDefendOptions.put(u, battleTerritories);
}
}
// Sort units by number of defend options and cost
final Map<Unit, Set<Territory>> sortedUnitDefendOptions = ProSortMoveOptionsUtils.sortUnitMoveOptions(unitDefendOptions);
// Add one scramble unit at a time and check if final result is better than min result
final List<Unit> unitsToScramble = new ArrayList<>();
ProBattleResult result = minResult;
for (final Unit u : sortedUnitDefendOptions.keySet()) {
unitsToScramble.add(u);
final List<Unit> currentDefenders = (List<Unit>) battle.getDefendingUnits();
currentDefenders.addAll(unitsToScramble);
result = calc.calculateBattleResults(scrambleTo, attackers, currentDefenders, bombardingUnits);
ProLogger.debug(scrambleTo + ", TUVSwing=" + result.getTuvSwing() + ", Win%=" + result.getWinPercentage() + ", addedUnit=" + u);
if (result.getTuvSwing() <= 0 && result.getWinPercentage() < (100 - ProData.minWinPercentage)) {
break;
}
}
if (result.getTuvSwing() >= minResult.getTuvSwing()) {
return null;
}
// Return units to scramble
final HashMap<Territory, Collection<Unit>> scrambleMap = new HashMap<>();
for (final Territory t : possibleScramblers.keySet()) {
for (final Unit u : possibleScramblers.get(t).getSecond()) {
if (unitsToScramble.contains(u)) {
if (scrambleMap.containsKey(t)) {
scrambleMap.get(t).add(u);
} else {
final Collection<Unit> units = new ArrayList<>();
units.add(u);
scrambleMap.put(t, units);
}
}
}
}
return scrambleMap;
}
Aggregations