From 0a86a72fff5bd0133d2eb47dfc3690344ffc984f Mon Sep 17 00:00:00 2001 From: asvitkine Date: Wed, 11 May 2022 14:41:08 -0400 Subject: [PATCH] Clean up some pro AI code related to purchasing. No logic changes. (#10434) Clean up some pro AI code related to purchasing. No logic changes. Some efficiency improvements to the logic by avoiding unnecessary operations, by removing collection copies and moving code out of loop bodies. --- .../triplea/ai/pro/ProCombatMoveAi.java | 3 +- .../triplea/ai/pro/ProPurchaseAi.java | 41 +++----- .../ai/pro/data/ProTerritoryManager.java | 9 +- .../pro/util/ProPurchaseValidationUtils.java | 38 +++----- .../triplea/ai/pro/util/ProUtils.java | 97 +++++++------------ 5 files changed, 64 insertions(+), 124 deletions(-) diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProCombatMoveAi.java b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProCombatMoveAi.java index 00ca971f4fa..f04cf45fd3c 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProCombatMoveAi.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProCombatMoveAi.java @@ -105,8 +105,7 @@ Map doCombatMove(final IMoveDelegate moveDel) { } } possibleTransportTerritories.addAll(clearedTerritories); - territoryManager.populateEnemyAttackOptions( - clearedTerritories, new ArrayList<>(possibleTransportTerritories)); + territoryManager.populateEnemyAttackOptions(clearedTerritories, possibleTransportTerritories); determineTerritoriesThatCanBeHeld(attackOptions, clearedTerritories); removeTerritoriesThatArentWorthAttacking(attackOptions); diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProPurchaseAi.java b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProPurchaseAi.java index 2cc1f473717..090dd517477 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProPurchaseAi.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProPurchaseAi.java @@ -141,7 +141,6 @@ void repair( */ Map bid( final int pus, final IPurchaseDelegate purchaseDelegate, final GameState startOfTurnData) { - // Current data fields data = proData.getData(); this.startOfTurnData = startOfTurnData; @@ -162,7 +161,6 @@ Map bid( int previousNumUnits = 0; while (true) { - // Determine max enemy attack units and current allied defenders territoryManager.populateEnemyAttackOptions( new ArrayList<>(), new ArrayList<>(purchaseTerritories.keySet())); @@ -257,7 +255,6 @@ Map bid( Map purchase( final IPurchaseDelegate purchaseDelegate, final GameState startOfTurnData) { - // Current data fields data = proData.getData(); this.startOfTurnData = startOfTurnData; @@ -279,15 +276,14 @@ Map purchase( new HashSet<>( CollectionUtils.getMatches( data.getMap().getTerritoriesOwnedBy(player), Matches.territoryIsLand())); - for (final Territory t : purchaseTerritories.keySet()) { - for (final ProPlaceTerritory ppt : purchaseTerritories.get(t).getCanPlaceTerritories()) { + for (final ProPurchaseTerritory t : purchaseTerritories.values()) { + for (final ProPlaceTerritory ppt : t.getCanPlaceTerritories()) { placeTerritories.add(ppt.getTerritory()); } } // Determine max enemy attack units and current allied defenders - territoryManager.populateEnemyAttackOptions( - new ArrayList<>(), new ArrayList<>(placeTerritories)); + territoryManager.populateEnemyAttackOptions(List.of(), placeTerritories); findDefendersInPlaceTerritories(purchaseTerritories); // Prioritize land territories that need defended and purchase additional defenders @@ -660,7 +656,6 @@ private void purchaseDefenders( final List zeroMoveDefensePurchaseOptions, final List airPurchaseOptions, final boolean isLand) { - if (resourceTracker.isEmpty()) { return; } @@ -696,12 +691,10 @@ private void purchaseDefenders( + unusedLocalCarrierCapacity); // Determine if need destroyer - boolean needDestroyer = false; - if (enemyAttackOptions.getMax(t).getMaxUnits().stream() - .anyMatch(Matches.unitHasSubBattleAbilities()) - && ownedLocalUnits.stream().noneMatch(Matches.unitIsDestroyer())) { - needDestroyer = true; - } + boolean needDestroyer = + enemyAttackOptions.getMax(t).getMaxUnits().stream() + .anyMatch(Matches.unitHasSubBattleAbilities()) + && ownedLocalUnits.stream().noneMatch(Matches.unitIsDestroyer()); // Find all purchase territories for place territory final List unitsToPlace = new ArrayList<>(); @@ -709,7 +702,6 @@ private void purchaseDefenders( final List selectedPurchaseTerritories = getPurchaseTerritories(placeTerritory, purchaseTerritories); for (final ProPurchaseTerritory purchaseTerritory : selectedPurchaseTerritories) { - // Check remaining production int remainingUnitProduction = purchaseTerritory.getRemainingUnitProduction(); int remainingConstructions = @@ -741,7 +733,6 @@ private void purchaseDefenders( // Purchase necessary defenders while (true) { - // Select purchase option ProPurchaseValidationUtils.removeInvalidPurchaseOptions( player, @@ -1020,17 +1011,14 @@ private void purchaseLandUnits( // Loop through prioritized territories and purchase land units final Set territoriesToCheck = new HashSet<>(); + final Predicate canMoveLandUnits = + ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties()); + final Predicate isEnemyTerritory = + Matches.isTerritoryOwnedByAnyOf(ProUtils.getEnemyPlayers(player)); for (final ProPlaceTerritory placeTerritory : prioritizedLandTerritories) { final Set landTerritories = - data.getMap() - .getNeighbors( - placeTerritory.getTerritory(), - 9, - ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties())); - final List enemyLandTerritories = - CollectionUtils.getMatches( - landTerritories, Matches.isTerritoryOwnedByAnyOf(ProUtils.getEnemyPlayers(player))); - territoriesToCheck.addAll(enemyLandTerritories); + data.getMap().getNeighbors(placeTerritory.getTerritory(), 9, canMoveLandUnits); + territoriesToCheck.addAll(CollectionUtils.getMatches(landTerritories, isEnemyTerritory)); } final Map territoryValueMap = ProTerritoryValueUtils.findTerritoryValues( @@ -1081,7 +1069,7 @@ private void purchaseLandUnits( for (final Iterator it = unplacedUnits.iterator(); it.hasNext(); ) { final Unit u = it.next(); if (remainingUnitProduction > 0 - && ProPurchaseValidationUtils.canUnitsBePlaced(proData, List.of(u), player, t, isBid)) { + && ProPurchaseValidationUtils.canUnitBePlaced(proData, u, player, t, isBid)) { remainingUnitProduction--; unitsToPlace.add(u); it.remove(); @@ -1094,7 +1082,6 @@ private void purchaseLandUnits( double attackAndDefenseDifference = 0; boolean selectFodderUnit = true; while (true) { - // Remove options that cost too much PUs or production ProPurchaseValidationUtils.removeInvalidPurchaseOptions( player, diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/data/ProTerritoryManager.java b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/data/ProTerritoryManager.java index 686455abc59..13789e0a3e3 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/data/ProTerritoryManager.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/data/ProTerritoryManager.java @@ -121,7 +121,8 @@ public void populateDefenseOptions(final List clearedTerritories) { } public void populateEnemyAttackOptions( - final List clearedTerritories, final List territoriesToCheck) { + final Collection clearedTerritories, + final Collection territoriesToCheck) { enemyAttackOptions = findEnemyAttackOptions(proData, player, clearedTerritories, territoriesToCheck); } @@ -415,7 +416,7 @@ private static void findAttackOptions( final List transportMapList, final List enemyTerritories, final List alliedTerritories, - final List territoriesToCheck, + final Collection territoriesToCheck, final boolean isCheckingEnemyAttacks, final boolean isIgnoringRelationships) { final GameState data = proData.getData(); @@ -534,8 +535,8 @@ private ProOtherMoveOptions findAlliedAttackOptions(final GamePlayer player) { private static ProOtherMoveOptions findEnemyAttackOptions( final ProData proData, final GamePlayer player, - final List clearedTerritories, - final List territoriesToCheck) { + final Collection clearedTerritories, + final Collection territoriesToCheck) { final GameState data = proData.getData(); // Get enemy players in order of turn diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProPurchaseValidationUtils.java b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProPurchaseValidationUtils.java index f0c8bd667f0..38049989423 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProPurchaseValidationUtils.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProPurchaseValidationUtils.java @@ -18,10 +18,10 @@ import games.strategy.triplea.attachments.TerritoryAttachment; import games.strategy.triplea.delegate.AbstractPlaceDelegate; import games.strategy.triplea.delegate.Matches; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Predicate; +import java.util.stream.Collectors; import lombok.experimental.UtilityClass; import org.triplea.java.collections.CollectionUtils; @@ -49,38 +49,22 @@ public static List findPurchaseOptionsForTerritory( final Territory t, final Territory factoryTerritory, final boolean isBid) { - final List result = new ArrayList<>(); - for (final ProPurchaseOption ppo : purchaseOptions) { - if (ProPurchaseValidationUtils.canTerritoryUsePurchaseOption( - proData, player, ppo, t, factoryTerritory, isBid)) { - result.add(ppo); - } - } - return result; + final Predicate canUsePurchaseOption = + ppo -> { + final List units = ppo.getUnitType().createTemp(ppo.getQuantity(), player); + return ProPurchaseValidationUtils.canUnitsBePlaced( + proData, units, player, t, factoryTerritory, isBid); + }; + return purchaseOptions.stream().filter(canUsePurchaseOption).collect(Collectors.toList()); } - private static boolean canTerritoryUsePurchaseOption( + public static boolean canUnitBePlaced( final ProData proData, - final GamePlayer player, - final ProPurchaseOption ppo, - final Territory t, - final Territory factoryTerritory, - final boolean isBid) { - if (ppo == null) { - return false; - } - final List units = ppo.getUnitType().createTemp(ppo.getQuantity(), player); - return ProPurchaseValidationUtils.canUnitsBePlaced( - proData, units, player, t, factoryTerritory, isBid); - } - - public static boolean canUnitsBePlaced( - final ProData proData, - final List units, + final Unit unit, final GamePlayer player, final Territory t, final boolean isBid) { - return ProPurchaseValidationUtils.canUnitsBePlaced(proData, units, player, t, t, isBid); + return ProPurchaseValidationUtils.canUnitsBePlaced(proData, List.of(unit), player, t, t, isBid); } /** Check if units can be placed in given territory by specified factory. */ diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProUtils.java b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProUtils.java index 446d4602d68..d554f0e25c5 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProUtils.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/util/ProUtils.java @@ -1,21 +1,22 @@ package games.strategy.triplea.ai.pro.util; +import static java.util.function.Predicate.not; + import com.google.common.collect.Streams; import games.strategy.engine.data.GamePlayer; import games.strategy.engine.data.GameSequence; import games.strategy.engine.data.GameState; import games.strategy.engine.data.GameStep; import games.strategy.engine.data.RelationshipTracker; -import games.strategy.engine.data.RelationshipType; import games.strategy.engine.data.Territory; import games.strategy.triplea.Properties; import games.strategy.triplea.attachments.TerritoryAttachment; import games.strategy.triplea.delegate.Matches; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; import org.triplea.java.collections.CollectionUtils; @@ -25,9 +26,8 @@ private ProUtils() {} /** Returns a list of all players in turn order excluding {@code player}. */ public static List getOtherPlayersInTurnOrder(final GamePlayer player) { - final GameState data = player.getData(); final List players = new ArrayList<>(); - final GameSequence sequence = data.getSequence(); + final GameSequence sequence = player.getData().getSequence(); final int startIndex = sequence.getStepIndex(); for (int i = 0; i < sequence.size(); i++) { int currentIndex = startIndex + i; @@ -47,18 +47,16 @@ public static List getOtherPlayersInTurnOrder(final GamePlayer playe } public static List getAlliedPlayersInTurnOrder(final GamePlayer player) { - final GameState data = player.getData(); + final var relationshipTracker = player.getData().getRelationshipTracker(); final List players = getOtherPlayersInTurnOrder(player); - players.removeIf( - currentPlayer -> !data.getRelationshipTracker().isAllied(player, currentPlayer)); + players.removeIf(currentPlayer -> !relationshipTracker.isAllied(player, currentPlayer)); return players; } public static List getEnemyPlayersInTurnOrder(final GamePlayer player) { - final GameState data = player.getData(); + final var relationshipTracker = player.getData().getRelationshipTracker(); final List players = getOtherPlayersInTurnOrder(player); - players.removeIf( - currentPlayer -> data.getRelationshipTracker().isAllied(player, currentPlayer)); + players.removeIf(currentPlayer -> relationshipTracker.isAllied(player, currentPlayer)); return players; } @@ -76,52 +74,39 @@ public static boolean isPlayersTurnFirst( public static List getEnemyPlayers(final GamePlayer player) { final GameState data = player.getData(); - final List enemyPlayers = new ArrayList<>(); - for (final GamePlayer players : data.getPlayerList().getPlayers()) { - if (!data.getRelationshipTracker().isAllied(player, players)) { - enemyPlayers.add(players); - } - } - return enemyPlayers; + return getFilteredPlayers( + data, Matches.isAllied(player, data.getRelationshipTracker()).negate()); } private static List getAlliedPlayers(final GamePlayer player) { final GameState data = player.getData(); - final List alliedPlayers = new ArrayList<>(); - for (final GamePlayer players : data.getPlayerList().getPlayers()) { - if (data.getRelationshipTracker().isAllied(player, players)) { - alliedPlayers.add(players); - } - } - return alliedPlayers; + return getFilteredPlayers(data, Matches.isAllied(player, data.getRelationshipTracker())); } /** Given a player, finds all non-allied (enemy) players. */ public static List getPotentialEnemyPlayers(final GamePlayer player) { - final GameState data = player.getData(); - final List otherPlayers = data.getPlayerList().getPlayers(); - for (final Iterator it = otherPlayers.iterator(); it.hasNext(); ) { - final GamePlayer otherPlayer = it.next(); - final RelationshipType relation = - data.getRelationshipTracker().getRelationshipType(player, otherPlayer); - if (Matches.relationshipTypeIsAllied().test(relation) - || isPassiveNeutralPlayer(otherPlayer)) { - it.remove(); - } - } - return otherPlayers; + final var tracker = player.getData().getRelationshipTracker(); + // Remove allied and passive neutrals. + final Predicate potentialEnemy = + not(Matches.isAllied(player, tracker)).and(not(ProUtils::isPassiveNeutralPlayer)); + return getFilteredPlayers(player.getData(), potentialEnemy); + } + + private static List getFilteredPlayers( + final GameState data, final Predicate filter) { + return data.getPlayerList().getPlayers().stream().filter(filter).collect(Collectors.toList()); } /** Computes PU production amount a given player currently has based on a given game data. */ public static double getPlayerProduction(final GamePlayer player, final GameState data) { + final Predicate canCollectIncomeFrom = + Matches.territoryCanCollectIncomeFrom( + player, data.getProperties(), data.getRelationshipTracker()); int production = 0; for (final Territory place : data.getMap().getTerritories()) { // Match will Check if terr is a Land Convoy Route and check ownership of neighboring Sea // Zone, or if contested - if (place.isOwnedBy(player) - && Matches.territoryCanCollectIncomeFrom( - player, data.getProperties(), data.getRelationshipTracker()) - .test(place)) { + if (place.isOwnedBy(player) && canCollectIncomeFrom.test(place)) { production += TerritoryAttachment.getProduction(place); } } @@ -176,23 +161,15 @@ public static List getLiveAlliedCapitals( */ public static int getClosestEnemyLandTerritoryDistance( final GameState data, final GamePlayer player, final Territory t) { - final Set landTerritories = - data.getMap() - .getNeighbors( - t, - 9, - ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties())); + final Predicate canMoveLandUnits = + ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties()); + final Set landTerritories = data.getMap().getNeighbors(t, 9, canMoveLandUnits); final List enemyLandTerritories = CollectionUtils.getMatches( landTerritories, Matches.isTerritoryOwnedByAnyOf(getPotentialEnemyPlayers(player))); int minDistance = 10; for (final Territory enemyLandTerritory : enemyLandTerritories) { - final int distance = - data.getMap() - .getDistance( - t, - enemyLandTerritory, - ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties())); + final int distance = data.getMap().getDistance(t, enemyLandTerritory, canMoveLandUnits); if (distance < minDistance) { minDistance = distance; } @@ -210,12 +187,9 @@ public static int getClosestEnemyOrNeutralLandTerritoryDistance( final GamePlayer player, final Territory t, final Map territoryValueMap) { - final Set landTerritories = - data.getMap() - .getNeighbors( - t, - 9, - ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties())); + final Predicate canMoveLandUnits = + ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties()); + final Set landTerritories = data.getMap().getNeighbors(t, 9, canMoveLandUnits); final List enemyLandTerritories = CollectionUtils.getMatches( landTerritories, Matches.isTerritoryOwnedByAnyOf(getEnemyPlayers(player))); @@ -224,12 +198,7 @@ public static int getClosestEnemyOrNeutralLandTerritoryDistance( if (territoryValueMap.get(enemyLandTerritory) <= 0) { continue; } - int distance = - data.getMap() - .getDistance( - t, - enemyLandTerritory, - ProMatches.territoryCanPotentiallyMoveLandUnits(player, data.getProperties())); + int distance = data.getMap().getDistance(t, enemyLandTerritory, canMoveLandUnits); if (ProUtils.isNeutralLand(enemyLandTerritory)) { distance++; }