use of games.strategy.engine.data.GameData in project triplea by triplea-game.
the class WeakAi method populateCombatMove.
private static void populateCombatMove(final GameData data, final List<Collection<Unit>> moveUnits, final List<Route> moveRoutes, final PlayerID player) {
populateBomberCombat(data, moveUnits, moveRoutes, player);
final Collection<Unit> unitsAlreadyMoved = new HashSet<>();
// find the territories we can just walk into
final Predicate<Territory> walkInto = Matches.isTerritoryEnemyAndNotUnownedWaterOrImpassableOrRestricted(player, data).or(Matches.isTerritoryFreeNeutral(data));
final List<Territory> enemyOwned = CollectionUtils.getMatches(data.getMap().getTerritories(), walkInto);
Collections.shuffle(enemyOwned);
enemyOwned.sort((o1, o2) -> {
// -1 means o1 goes first. 1 means o2 goes first. zero means they are equal.
if (Objects.equals(o1, o2)) {
return 0;
}
if (o1 == null) {
return 1;
}
if (o2 == null) {
return -1;
}
final TerritoryAttachment ta1 = TerritoryAttachment.get(o1);
final TerritoryAttachment ta2 = TerritoryAttachment.get(o2);
if (ta1 == null && ta2 == null) {
return 0;
}
if (ta1 == null) {
return 1;
}
if (ta2 == null) {
return -1;
}
// take capitols first if we can
if (ta1.isCapital() && !ta2.isCapital()) {
return -1;
}
if (!ta1.isCapital() && ta2.isCapital()) {
return 1;
}
final boolean factoryInT1 = o1.getUnits().anyMatch(Matches.unitCanProduceUnits());
final boolean factoryInT2 = o2.getUnits().anyMatch(Matches.unitCanProduceUnits());
// next take territories which can produce
if (factoryInT1 && !factoryInT2) {
return -1;
}
if (!factoryInT1 && factoryInT2) {
return 1;
}
final boolean infrastructureInT1 = o1.getUnits().anyMatch(Matches.unitIsInfrastructure());
final boolean infrastructureInT2 = o2.getUnits().anyMatch(Matches.unitIsInfrastructure());
// next take territories with infrastructure
if (infrastructureInT1 && !infrastructureInT2) {
return -1;
}
if (!infrastructureInT1 && infrastructureInT2) {
return 1;
}
// next take territories with largest PU value
return ta2.getProduction() - ta1.getProduction();
});
final List<Territory> isWaterTerr = Utils.onlyWaterTerr(enemyOwned);
enemyOwned.removeAll(isWaterTerr);
// first find the territories we can just walk into
for (final Territory enemy : enemyOwned) {
if (AiUtils.strength(enemy.getUnits().getUnits(), false, false) == 0) {
// only take it with 1 unit
boolean taken = false;
for (final Territory attackFrom : data.getMap().getNeighbors(enemy, Matches.territoryHasLandUnitsOwnedBy(player))) {
if (taken) {
break;
}
// get the cheapest unit to move in
final List<Unit> unitsSortedByCost = new ArrayList<>(attackFrom.getUnits().getUnits());
unitsSortedByCost.sort(AiUtils.getCostComparator());
for (final Unit unit : unitsSortedByCost) {
final Predicate<Unit> match = Matches.unitIsOwnedBy(player).and(Matches.unitIsLand()).and(Matches.unitIsNotInfrastructure()).and(Matches.unitCanMove()).and(Matches.unitIsNotAa()).and(Matches.unitCanNotMoveDuringCombatMove().negate());
if (!unitsAlreadyMoved.contains(unit) && match.test(unit)) {
moveRoutes.add(data.getMap().getRoute(attackFrom, enemy));
// number of units, to leave units free to move elsewhere
if (attackFrom.isWater()) {
final List<Unit> units = attackFrom.getUnits().getMatches(Matches.unitIsLandAndOwnedBy(player));
moveUnits.add(CollectionUtils.difference(units, unitsAlreadyMoved));
unitsAlreadyMoved.addAll(units);
} else {
moveUnits.add(Collections.singleton(unit));
}
unitsAlreadyMoved.add(unit);
taken = true;
break;
}
}
}
}
}
// find the territories we can reasonably expect to take
for (final Territory enemy : enemyOwned) {
final float enemyStrength = AiUtils.strength(enemy.getUnits().getUnits(), false, false);
if (enemyStrength > 0) {
final Predicate<Unit> attackable = Matches.unitIsOwnedBy(player).and(Matches.unitIsStrategicBomber().negate()).and(o -> !unitsAlreadyMoved.contains(o)).and(Matches.unitIsNotAa()).and(Matches.unitCanMove()).and(Matches.unitIsNotInfrastructure()).and(Matches.unitCanNotMoveDuringCombatMove().negate()).and(Matches.unitIsNotSea());
final Set<Territory> dontMoveFrom = new HashSet<>();
// find our strength that we can attack with
float ourStrength = 0;
final Collection<Territory> attackFrom = data.getMap().getNeighbors(enemy, Matches.territoryHasLandUnitsOwnedBy(player));
for (final Territory owned : attackFrom) {
if (TerritoryAttachment.get(owned) != null && TerritoryAttachment.get(owned).isCapital() && (Utils.getStrengthOfPotentialAttackers(owned, data) > AiUtils.strength(owned.getUnits().getUnits(), false, false))) {
dontMoveFrom.add(owned);
continue;
}
ourStrength += AiUtils.strength(owned.getUnits().getMatches(attackable), true, false);
}
// prevents 2 infantry from attacking 1 infantry
if (ourStrength > 1.37 * enemyStrength) {
// this is all we need to take it, dont go overboard, since we may be able to use the units to attack
// somewhere else
double remainingStrengthNeeded = (2.5 * enemyStrength) + 4;
for (final Territory owned : attackFrom) {
if (dontMoveFrom.contains(owned)) {
continue;
}
List<Unit> units = owned.getUnits().getMatches(attackable);
// 2) we can potentially attack another territory
if (!owned.isWater() && data.getMap().getNeighbors(owned, Matches.territoryHasEnemyLandUnits(player, data)).size() > 1) {
units = Utils.getUnitsUpToStrength(remainingStrengthNeeded, units, false);
}
remainingStrengthNeeded -= AiUtils.strength(units, true, false);
if (units.size() > 0) {
unitsAlreadyMoved.addAll(units);
moveUnits.add(units);
moveRoutes.add(data.getMap().getRoute(owned, enemy));
}
}
}
}
}
}
use of games.strategy.engine.data.GameData in project triplea by triplea-game.
the class WeakAi method getAmphibRoute.
private static Route getAmphibRoute(final PlayerID player, final GameData data) {
if (!isAmphibAttack(player, data)) {
return null;
}
final Territory ourCapitol = TerritoryAttachment.getFirstOwnedCapitalOrFirstUnownedCapital(player, data);
final Predicate<Territory> endMatch = o -> {
final boolean impassable = TerritoryAttachment.get(o) != null && TerritoryAttachment.get(o).getIsImpassable();
return !impassable && !o.isWater() && Utils.hasLandRouteToEnemyOwnedCapitol(o, player, data);
};
final Predicate<Territory> routeCond = Matches.territoryIsWater().and(Matches.territoryHasNoEnemyUnits(player, data));
final Route withNoEnemy = Utils.findNearest(ourCapitol, endMatch, routeCond, data);
if (withNoEnemy != null && withNoEnemy.numberOfSteps() > 0) {
return withNoEnemy;
}
// this will fail if our capitol is not next to water, c'est la vie.
final Route route = Utils.findNearest(ourCapitol, endMatch, Matches.territoryIsWater(), data);
if (route != null && route.numberOfSteps() == 0) {
return null;
}
return route;
}
use of games.strategy.engine.data.GameData in project triplea by triplea-game.
the class ProPurchaseAi method bid.
/**
* Default settings for bidding:
* 1) Limit one bid unit in a territory or sea zone (until set in all territories then 2, etc).
* 2) The nation placing a unit in a territory or sea zone must have started with a unit in said territory or sea zone
* prior to placing the bid.
*/
Map<Territory, ProPurchaseTerritory> bid(final int pus, final IPurchaseDelegate purchaseDelegate, final GameData startOfTurnData) {
// Current data fields
data = ProData.getData();
this.startOfTurnData = startOfTurnData;
player = ProData.getPlayer();
resourceTracker = new ProResourceTracker(pus, data);
territoryManager = new ProTerritoryManager(calc);
isBid = true;
final ProPurchaseOptionMap purchaseOptions = ProData.purchaseOptions;
ProLogger.info("Starting bid phase with resources: " + resourceTracker);
if (!player.getUnits().getUnits().isEmpty()) {
ProLogger.info("Starting bid phase with unplaced units=" + player.getUnits().getUnits());
}
// Find all purchase/place territories
final Map<Territory, ProPurchaseTerritory> purchaseTerritories = ProPurchaseUtils.findBidTerritories(player);
int previousNumUnits = 0;
while (true) {
// Determine max enemy attack units and current allied defenders
territoryManager.populateEnemyAttackOptions(new ArrayList<>(), new ArrayList<>(purchaseTerritories.keySet()));
findDefendersInPlaceTerritories(purchaseTerritories);
// Prioritize land territories that need defended and purchase additional defenders
final List<ProPlaceTerritory> needToDefendLandTerritories = prioritizeTerritoriesToDefend(purchaseTerritories, true);
purchaseDefenders(purchaseTerritories, needToDefendLandTerritories, purchaseOptions.getLandFodderOptions(), purchaseOptions.getAirOptions(), true);
// Find strategic value for each territory
ProLogger.info("Find strategic value for place territories");
final Map<Territory, Double> territoryValueMap = ProTerritoryValueUtils.findTerritoryValues(player, new ArrayList<>(), new ArrayList<>());
for (final ProPurchaseTerritory t : purchaseTerritories.values()) {
for (final ProPlaceTerritory ppt : t.getCanPlaceTerritories()) {
ppt.setStrategicValue(territoryValueMap.get(ppt.getTerritory()));
ProLogger.debug(ppt.getTerritory() + ", strategicValue=" + territoryValueMap.get(ppt.getTerritory()));
}
}
// Prioritize land place options purchase AA then land units
final List<ProPlaceTerritory> prioritizedLandTerritories = prioritizeLandTerritories(purchaseTerritories);
purchaseAaUnits(purchaseTerritories, prioritizedLandTerritories, purchaseOptions.getAaOptions());
purchaseLandUnits(purchaseTerritories, prioritizedLandTerritories, purchaseOptions, territoryValueMap);
// Prioritize sea territories that need defended and purchase additional defenders
final List<ProPlaceTerritory> needToDefendSeaTerritories = prioritizeTerritoriesToDefend(purchaseTerritories, false);
purchaseDefenders(purchaseTerritories, needToDefendSeaTerritories, purchaseOptions.getSeaDefenseOptions(), purchaseOptions.getAirOptions(), false);
// Prioritize sea place options and purchase units
final List<ProPlaceTerritory> prioritizedSeaTerritories = prioritizeSeaTerritories(purchaseTerritories);
purchaseSeaAndAmphibUnits(purchaseTerritories, prioritizedSeaTerritories, territoryValueMap, purchaseOptions);
// Try to use any remaining PUs on high value units
purchaseUnitsWithRemainingProduction(purchaseTerritories, purchaseOptions.getLandOptions(), purchaseOptions.getAirOptions());
upgradeUnitsWithRemainingPUs(purchaseTerritories, purchaseOptions);
// Check if no remaining PUs or no unit built this iteration
final int numUnits = purchaseTerritories.values().stream().map(ProPurchaseTerritory::getCanPlaceTerritories).map(t -> t.get(0)).map(ProPlaceTerritory::getPlaceUnits).mapToInt(List::size).sum();
if (resourceTracker.isEmpty() || numUnits == previousNumUnits) {
break;
}
previousNumUnits = numUnits;
ProPurchaseUtils.incrementUnitProductionForBidTerritories(purchaseTerritories);
}
// Determine final count of each production rule
final IntegerMap<ProductionRule> purchaseMap = populateProductionRuleMap(purchaseTerritories, purchaseOptions);
// Purchase units
ProMetricUtils.collectPurchaseStats(purchaseMap);
final String error = purchaseDelegate.purchase(purchaseMap);
if (error != null) {
ProLogger.warn("Purchase error: " + error);
}
return purchaseTerritories;
}
use of games.strategy.engine.data.GameData in project triplea by triplea-game.
the class ProAi method retreatQuery.
@Override
public Territory retreatQuery(final GUID battleId, final boolean submerge, final Territory battleTerritory, final Collection<Territory> possibleTerritories, final String message) {
initializeData();
// Get battle data
final GameData data = getGameData();
final PlayerID player = getPlayerId();
final BattleDelegate delegate = DelegateFinder.battleDelegate(data);
final IBattle battle = delegate.getBattleTracker().getPendingBattle(battleId);
// If battle is null or amphibious then don't retreat
if (battle == null || battleTerritory == null || battle.isAmphibious()) {
return null;
}
// If attacker with more unit strength or strafing and isn't land battle with only air left then don't retreat
final boolean isAttacker = player.equals(battle.getAttacker());
final List<Unit> attackers = (List<Unit>) battle.getAttackingUnits();
final List<Unit> defenders = (List<Unit>) battle.getDefendingUnits();
final double strengthDifference = ProBattleUtils.estimateStrengthDifference(battleTerritory, attackers, defenders);
final boolean isStrafing = isAttacker && storedStrafingTerritories.contains(battleTerritory);
ProLogger.info(player.getName() + " checking retreat from territory " + battleTerritory + ", attackers=" + attackers.size() + ", defenders=" + defenders.size() + ", submerge=" + submerge + ", attacker=" + isAttacker + ", isStrafing=" + isStrafing);
if ((isStrafing || (isAttacker && strengthDifference > 50)) && (battleTerritory.isWater() || attackers.stream().anyMatch(Matches.unitIsLand()))) {
return null;
}
calc.setData(getGameData());
return retreatAi.retreatQuery(battleId, battleTerritory, possibleTerritories);
}
use of games.strategy.engine.data.GameData in project triplea by triplea-game.
the class ProAi method selectCasualties.
@Override
public CasualtyDetails selectCasualties(final Collection<Unit> selectFrom, final Map<Unit, Collection<Unit>> dependents, final int count, final String message, final DiceRoll dice, final PlayerID hit, final Collection<Unit> friendlyUnits, final PlayerID enemyPlayer, final Collection<Unit> enemyUnits, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers, final CasualtyList defaultCasualties, final GUID battleId, final Territory battlesite, final boolean allowMultipleHitsPerUnit) {
initializeData();
if (defaultCasualties.size() != count) {
throw new IllegalStateException("Select Casualties showing different numbers for number of hits to take vs total " + "size of default casualty selections");
}
if (defaultCasualties.getKilled().size() <= 0) {
return new CasualtyDetails(defaultCasualties, false);
}
// Consider unit cost
final CasualtyDetails myCasualties = new CasualtyDetails(false);
myCasualties.addToDamaged(defaultCasualties.getDamaged());
final List<Unit> selectFromSorted = new ArrayList<>(selectFrom);
if (enemyUnits.isEmpty()) {
selectFromSorted.sort(ProPurchaseUtils.getCostComparator());
} else {
// Get battle data
final GameData data = getGameData();
final PlayerID player = getPlayerId();
final BattleDelegate delegate = DelegateFinder.battleDelegate(data);
final IBattle battle = delegate.getBattleTracker().getPendingBattle(battleId);
// If defender and could lose battle then don't consider unit cost as just trying to survive
boolean needToCheck = true;
final boolean isAttacker = player.equals(battle.getAttacker());
if (!isAttacker) {
final List<Unit> attackers = (List<Unit>) battle.getAttackingUnits();
final List<Unit> defenders = (List<Unit>) battle.getDefendingUnits();
defenders.removeAll(defaultCasualties.getKilled());
final double strengthDifference = ProBattleUtils.estimateStrengthDifference(battlesite, attackers, defenders);
int minStrengthDifference = 60;
if (!Properties.getLowLuck(data)) {
minStrengthDifference = 55;
}
if (strengthDifference > minStrengthDifference) {
needToCheck = false;
}
}
// Use bubble sort to save expensive units
while (needToCheck) {
needToCheck = false;
for (int i = 0; i < selectFromSorted.size() - 1; i++) {
final Unit unit1 = selectFromSorted.get(i);
final Unit unit2 = selectFromSorted.get(i + 1);
final double unitCost1 = ProPurchaseUtils.getCost(unit1);
final double unitCost2 = ProPurchaseUtils.getCost(unit2);
if (unitCost1 > 1.5 * unitCost2) {
selectFromSorted.set(i, unit2);
selectFromSorted.set(i + 1, unit1);
needToCheck = true;
}
}
}
}
// Interleave carriers and planes
final List<Unit> interleavedTargetList = new ArrayList<>(ProTransportUtils.interleaveUnitsCarriersAndPlanes(selectFromSorted, 0));
for (int i = 0; i < defaultCasualties.getKilled().size(); ++i) {
myCasualties.addToKilled(interleavedTargetList.get(i));
}
if (count != myCasualties.size()) {
throw new IllegalStateException("AI chose wrong number of casualties");
}
return myCasualties;
}
Aggregations