use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class ProPurchaseAi method purchaseLandUnits.
private void purchaseLandUnits(final Map<Territory, ProPurchaseTerritory> purchaseTerritories, final List<ProPlaceTerritory> prioritizedLandTerritories, final ProPurchaseOptionMap purchaseOptions, final Map<Territory, Double> territoryValueMap) {
final List<Unit> unplacedUnits = player.getUnits().getMatches(Matches.unitIsNotSea());
if (resourceTracker.isEmpty() && unplacedUnits.isEmpty()) {
return;
}
ProLogger.info("Purchase land units with resources: " + resourceTracker);
if (!unplacedUnits.isEmpty()) {
ProLogger.info("Purchase land units with unplaced units=" + unplacedUnits);
}
// Loop through prioritized territories and purchase land units
for (final ProPlaceTerritory placeTerritory : prioritizedLandTerritories) {
final Territory t = placeTerritory.getTerritory();
ProLogger.debug("Checking land place for " + t.getName());
// Check remaining production
int remainingUnitProduction = purchaseTerritories.get(t).getRemainingUnitProduction();
ProLogger.debug(t + ", remainingUnitProduction=" + remainingUnitProduction);
if (remainingUnitProduction <= 0) {
continue;
}
// Determine most cost efficient units that can be produced in this territory
final List<ProPurchaseOption> landFodderOptions = ProPurchaseUtils.findPurchaseOptionsForTerritory(player, purchaseOptions.getLandFodderOptions(), t, isBid);
final List<ProPurchaseOption> landAttackOptions = ProPurchaseUtils.findPurchaseOptionsForTerritory(player, purchaseOptions.getLandAttackOptions(), t, isBid);
final List<ProPurchaseOption> landDefenseOptions = ProPurchaseUtils.findPurchaseOptionsForTerritory(player, purchaseOptions.getLandDefenseOptions(), t, isBid);
// Determine enemy distance and locally owned units
int enemyDistance = ProUtils.getClosestEnemyOrNeutralLandTerritoryDistance(data, player, t, territoryValueMap);
if (enemyDistance <= 0) {
enemyDistance = 10;
}
final int fodderPercent = 80 - enemyDistance * 5;
ProLogger.debug(t + ", enemyDistance=" + enemyDistance + ", fodderPercent=" + fodderPercent);
final Set<Territory> neighbors = data.getMap().getNeighbors(t, 2, ProMatches.territoryCanMoveLandUnits(player, data, false));
neighbors.add(t);
final List<Unit> ownedLocalUnits = new ArrayList<>();
for (final Territory neighbor : neighbors) {
ownedLocalUnits.addAll(neighbor.getUnits().getMatches(Matches.unitIsOwnedBy(player)));
}
// Check for unplaced units
final List<Unit> unitsToPlace = new ArrayList<>();
for (final Iterator<Unit> it = unplacedUnits.iterator(); it.hasNext(); ) {
final Unit u = it.next();
if (remainingUnitProduction > 0 && ProPurchaseUtils.canUnitsBePlaced(Collections.singletonList(u), player, t, isBid)) {
remainingUnitProduction--;
unitsToPlace.add(u);
it.remove();
ProLogger.trace("Selected unplaced unit=" + u);
}
}
// Purchase as many units as possible
int addedFodderUnits = 0;
double attackAndDefenseDifference = 0;
boolean selectFodderUnit = true;
while (true) {
// Remove options that cost too much PUs or production
ProPurchaseUtils.removeInvalidPurchaseOptions(player, startOfTurnData, landFodderOptions, resourceTracker, remainingUnitProduction, unitsToPlace, purchaseTerritories);
ProPurchaseUtils.removeInvalidPurchaseOptions(player, startOfTurnData, landAttackOptions, resourceTracker, remainingUnitProduction, unitsToPlace, purchaseTerritories);
ProPurchaseUtils.removeInvalidPurchaseOptions(player, startOfTurnData, landDefenseOptions, resourceTracker, remainingUnitProduction, unitsToPlace, purchaseTerritories);
// Select purchase option
Optional<ProPurchaseOption> optionalSelectedOption = Optional.empty();
if (!selectFodderUnit && attackAndDefenseDifference > 0 && !landDefenseOptions.isEmpty()) {
final Map<ProPurchaseOption, Double> defenseEfficiencies = new HashMap<>();
for (final ProPurchaseOption ppo : landDefenseOptions) {
defenseEfficiencies.put(ppo, ppo.getDefenseEfficiency2(enemyDistance, data, ownedLocalUnits, unitsToPlace));
}
optionalSelectedOption = ProPurchaseUtils.randomizePurchaseOption(defenseEfficiencies, "Land Defense");
} else if (!selectFodderUnit && !landAttackOptions.isEmpty()) {
final Map<ProPurchaseOption, Double> attackEfficiencies = new HashMap<>();
for (final ProPurchaseOption ppo : landAttackOptions) {
attackEfficiencies.put(ppo, ppo.getAttackEfficiency2(enemyDistance, data, ownedLocalUnits, unitsToPlace));
}
optionalSelectedOption = ProPurchaseUtils.randomizePurchaseOption(attackEfficiencies, "Land Attack");
} else if (!landFodderOptions.isEmpty()) {
final Map<ProPurchaseOption, Double> fodderEfficiencies = new HashMap<>();
for (final ProPurchaseOption ppo : landFodderOptions) {
fodderEfficiencies.put(ppo, ppo.getFodderEfficiency(enemyDistance, data, ownedLocalUnits, unitsToPlace));
}
optionalSelectedOption = ProPurchaseUtils.randomizePurchaseOption(fodderEfficiencies, "Land Fodder");
if (optionalSelectedOption.isPresent()) {
addedFodderUnits += optionalSelectedOption.get().getQuantity();
}
}
if (!optionalSelectedOption.isPresent()) {
break;
}
final ProPurchaseOption selectedOption = optionalSelectedOption.get();
// Create new temp units
resourceTracker.purchase(selectedOption);
remainingUnitProduction -= selectedOption.getQuantity();
unitsToPlace.addAll(selectedOption.getUnitType().create(selectedOption.getQuantity(), player, true));
attackAndDefenseDifference += (selectedOption.getAttack() - selectedOption.getDefense());
selectFodderUnit = ((double) addedFodderUnits / unitsToPlace.size() * 100) <= fodderPercent;
ProLogger.trace("Selected unit=" + selectedOption.getUnitType().getName());
}
// Add units to place territory
placeTerritory.getPlaceUnits().addAll(unitsToPlace);
ProLogger.debug(t + ", placedUnits=" + unitsToPlace);
}
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class ProTechAi method findAttackers.
private static List<Unit> findAttackers(final Territory start, final int maxDistance, final HashSet<Integer> ignoreDistance, final PlayerID player, final GameData data, final Predicate<Unit> unitCondition, final Predicate<Territory> routeCondition, final List<Route> routes, final boolean sea) {
final IntegerMap<Territory> distance = new IntegerMap<>();
final Map<Territory, Territory> visited = new HashMap<>();
final List<Unit> units = new ArrayList<>();
final Queue<Territory> q = new ArrayDeque<>();
q.add(start);
Territory current;
distance.put(start, 0);
visited.put(start, null);
while (!q.isEmpty()) {
current = q.remove();
if (distance.getInt(current) == maxDistance) {
break;
}
for (final Territory neighbor : data.getMap().getNeighbors(current)) {
if (!distance.keySet().contains(neighbor)) {
if (!neighbor.getUnits().anyMatch(unitCondition)) {
if (!routeCondition.test(neighbor)) {
continue;
}
}
if (sea) {
final Route r = new Route();
r.setStart(neighbor);
r.add(current);
if (MoveValidator.validateCanal(r, null, player, data) != null) {
continue;
}
}
distance.put(neighbor, distance.getInt(current) + 1);
visited.put(neighbor, current);
q.add(neighbor);
final int dist = distance.getInt(neighbor);
if (ignoreDistance.contains(dist)) {
continue;
}
for (final Unit u : neighbor.getUnits()) {
if (unitCondition.test(u) && Matches.unitHasEnoughMovementForRoutes(routes).test(u)) {
units.add(u);
}
}
}
}
}
// pain in the ass, should just redesign stop blitz attack
for (final Territory t : visited.keySet()) {
final Route r = new Route();
Territory t2 = t;
r.setStart(t);
while (t2 != null) {
t2 = visited.get(t2);
if (t2 != null) {
r.add(t2);
}
}
routes.add(r);
}
return units;
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class ProTechAi method determineEnemyBlitzStrength.
/**
* Determine the enemy potential for blitzing a territory - all enemies are combined.
*
* @param blitzHere
* - Territory expecting to be blitzed
* @return actual strength of enemy units (armor)
*/
private static float determineEnemyBlitzStrength(final Territory blitzHere, final List<Route> blitzTerrRoutes, final GameData data, final PlayerID enemyPlayer) {
final HashSet<Integer> ignore = new HashSet<>();
ignore.add(1);
final Predicate<Unit> blitzUnit = Matches.unitIsOwnedBy(enemyPlayer).and(Matches.unitCanBlitz()).and(Matches.unitCanMove());
final Predicate<Territory> validBlitzRoute = Matches.territoryHasNoEnemyUnits(enemyPlayer, data).and(Matches.territoryIsNotImpassableToLandUnits(enemyPlayer, data));
final List<Route> routes = new ArrayList<>();
final List<Unit> blitzUnits = findAttackers(blitzHere, 2, ignore, enemyPlayer, data, blitzUnit, validBlitzRoute, routes, false);
for (final Route r : routes) {
if (r.numberOfSteps() == 2) {
blitzTerrRoutes.add(r);
}
}
return strength(blitzUnits, true, false, true);
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class ProTechAi method getStrengthOfPotentialAttackers.
/**
* Returns the strength of all attackers to a territory.
* Differentiates between sea and land attack
* Determines all transports within range of territory
* Determines all air units within range of territory (using 2 for fighters and 3 for bombers)
* Does not check for extended range fighters or bombers
*/
private static float getStrengthOfPotentialAttackers(final Territory location, final GameData data, final PlayerID player) {
final boolean transportsFirst = false;
PlayerID enemyPlayer = null;
final List<PlayerID> enemyPlayers = getEnemyPlayers(data, player);
final HashMap<PlayerID, Float> enemyPlayerAttackMap = new HashMap<>();
final Iterator<PlayerID> playerIter = enemyPlayers.iterator();
if (location == null) {
return -1000.0F;
}
boolean nonTransportsInAttack = false;
final boolean onWater = location.isWater();
if (!onWater) {
nonTransportsInAttack = true;
}
final Set<Territory> waterTerr = data.getMap().getNeighbors(location, Matches.territoryIsWater());
while (playerIter.hasNext()) {
float seaStrength = 0.0F;
float firstStrength = 0.0F;
float secondStrength = 0.0F;
float blitzStrength = 0.0F;
float strength;
enemyPlayer = playerIter.next();
final Predicate<Unit> enemyPlane = Matches.unitIsAir().and(Matches.unitIsOwnedBy(enemyPlayer)).and(Matches.unitCanMove());
final Predicate<Unit> enemyTransport = Matches.unitIsOwnedBy(enemyPlayer).and(Matches.unitIsSea()).and(Matches.unitIsTransport()).and(Matches.unitCanMove());
final Predicate<Unit> enemyShip = Matches.unitIsOwnedBy(enemyPlayer).and(Matches.unitIsSea()).and(Matches.unitCanMove());
final Predicate<Unit> enemyTransportable = Matches.unitIsOwnedBy(enemyPlayer).and(Matches.unitCanBeTransported()).and(Matches.unitIsNotAa()).and(Matches.unitCanMove());
final Predicate<Unit> transport = Matches.unitIsSea().and(Matches.unitIsTransport()).and(Matches.unitCanMove());
final List<Territory> enemyFighterTerritories = findUnitTerr(data, enemyPlane);
int maxFighterDistance = 0;
// and likely player will have at least 1 max move plane.
for (final Territory enemyFighterTerritory : enemyFighterTerritories) {
final List<Unit> enemyFighterUnits = enemyFighterTerritory.getUnits().getMatches(enemyPlane);
maxFighterDistance = Math.max(maxFighterDistance, MoveValidator.getMaxMovement(enemyFighterUnits));
}
// must be able to land...we will miss fighters who have a Carrier that can reach same sea zone...C'est la vie
maxFighterDistance--;
if (maxFighterDistance < 0) {
maxFighterDistance = 0;
}
final List<Territory> enemyTransportTerritories = findUnitTerr(data, transport);
int maxTransportDistance = 0;
for (final Territory enemyTransportTerritory : enemyTransportTerritories) {
final List<Unit> enemyTransportUnits = enemyTransportTerritory.getUnits().getMatches(transport);
maxTransportDistance = Math.max(maxTransportDistance, MoveValidator.getMaxMovement(enemyTransportUnits));
}
final List<Unit> alreadyLoaded = new ArrayList<>();
final List<Route> blitzTerrRoutes = new ArrayList<>();
final List<Territory> checked = new ArrayList<>();
final List<Unit> enemyWaterUnits = new ArrayList<>();
for (final Territory t : data.getMap().getNeighbors(location, onWater ? Matches.territoryIsWater() : Matches.territoryIsLand())) {
final List<Unit> enemies = t.getUnits().getMatches(Matches.unitIsOwnedBy(enemyPlayer));
enemyWaterUnits.addAll(enemies);
firstStrength += strength(enemies, true, onWater, transportsFirst);
checked.add(t);
}
if (Matches.territoryIsLand().test(location)) {
blitzStrength = determineEnemyBlitzStrength(location, blitzTerrRoutes, data, enemyPlayer);
} else {
// get ships attack strength
// old assumed fleets won't split up, new lets them. no biggie.
// assumes max ship movement is 3.
// note, both old and new implementations
// allow units to be calculated that are in
// territories we have already assaulted
// this can be easily changed
final HashSet<Integer> ignore = new HashSet<>();
ignore.add(1);
final List<Route> r = new ArrayList<>();
final List<Unit> ships = findAttackers(location, 3, ignore, enemyPlayer, data, enemyShip, Matches.territoryIsBlockedSea(enemyPlayer, data), r, true);
secondStrength = strength(ships, true, true, transportsFirst);
enemyWaterUnits.addAll(ships);
}
final List<Unit> attackPlanes = findPlaneAttackersThatCanLand(location, maxFighterDistance, enemyPlayer, data, checked);
final float airStrength = allAirStrength(attackPlanes);
if (Matches.territoryHasWaterNeighbor(data).test(location) && Matches.territoryIsLand().test(location)) {
for (final Territory t4 : data.getMap().getNeighbors(location, maxTransportDistance)) {
if (!t4.isWater()) {
continue;
}
for (final Territory waterCheck : waterTerr) {
if (enemyPlayer == null) {
continue;
}
final List<Unit> transports = t4.getUnits().getMatches(enemyTransport);
if (transports.isEmpty()) {
continue;
}
if (!t4.equals(waterCheck)) {
final Route seaRoute = getMaxSeaRoute(data, t4, waterCheck, enemyPlayer, maxTransportDistance);
if (seaRoute == null || seaRoute.getEnd() == null || seaRoute.getEnd() != waterCheck) {
continue;
}
}
final List<Unit> loadedUnits = new ArrayList<>();
int availInf = 0;
int availOther = 0;
for (final Unit candidateTransport : transports) {
final Collection<Unit> thisTransUnits = TransportTracker.transporting(candidateTransport);
if (thisTransUnits == null) {
availInf += 2;
availOther += 1;
continue;
}
int inf = 2;
int other = 1;
for (final Unit checkUnit : thisTransUnits) {
if (Matches.unitIsLandTransportable().test(checkUnit)) {
inf--;
}
if (Matches.unitIsNotLandTransportable().test(checkUnit)) {
inf--;
other--;
}
loadedUnits.add(checkUnit);
}
availInf += inf;
availOther += other;
}
final Set<Territory> transNeighbors = data.getMap().getNeighbors(t4, Matches.isTerritoryAllied(enemyPlayer, data));
for (final Territory transNeighbor : transNeighbors) {
final List<Unit> transUnits = transNeighbor.getUnits().getMatches(enemyTransportable);
transUnits.removeAll(alreadyLoaded);
final List<Unit> availTransUnits = sortTransportUnits(transUnits);
for (final Unit transUnit : availTransUnits) {
if (availInf > 0 && Matches.unitIsLandTransportable().test(transUnit)) {
availInf--;
loadedUnits.add(transUnit);
alreadyLoaded.add(transUnit);
}
if (availInf > 0 && availOther > 0 && Matches.unitIsNotLandTransportable().test(transUnit)) {
availInf--;
availOther--;
loadedUnits.add(transUnit);
alreadyLoaded.add(transUnit);
}
}
}
seaStrength += strength(loadedUnits, true, false, transportsFirst);
break;
}
}
}
strength = seaStrength + blitzStrength + firstStrength + secondStrength;
if (strength > 0.0F) {
strength += airStrength;
}
if (onWater) {
final Iterator<Unit> enemyWaterUnitsIter = enemyWaterUnits.iterator();
while (enemyWaterUnitsIter.hasNext() && !nonTransportsInAttack) {
if (Matches.unitIsNotTransport().test(enemyWaterUnitsIter.next())) {
nonTransportsInAttack = true;
}
}
}
if (!nonTransportsInAttack) {
strength = 0.0F;
}
enemyPlayerAttackMap.put(enemyPlayer, strength);
}
float maxStrength = 0.0F;
for (final PlayerID enemyPlayerCandidate : enemyPlayers) {
if (enemyPlayerAttackMap.get(enemyPlayerCandidate) > maxStrength) {
enemyPlayer = enemyPlayerCandidate;
maxStrength = enemyPlayerAttackMap.get(enemyPlayerCandidate);
}
}
for (final PlayerID enemyPlayerCandidate : enemyPlayers) {
if (enemyPlayer != enemyPlayerCandidate) {
// give 40% of other players...this is will affect a lot of decisions by AI
maxStrength += enemyPlayerAttackMap.get(enemyPlayerCandidate) * 0.40F;
}
}
return maxStrength;
}
use of games.strategy.engine.data.Territory in project triplea by triplea-game.
the class ProTechAi method tech.
static void tech(final ITechDelegate techDelegate, final GameData data, final PlayerID player) {
if (!Properties.getWW2V3TechModel(data)) {
return;
}
final Territory myCapitol = TerritoryAttachment.getFirstOwnedCapitalOrFirstUnownedCapital(player, data);
final float enemyStrength = getStrengthOfPotentialAttackers(myCapitol, data, player);
float myStrength = (myCapitol == null || myCapitol.getUnits() == null) ? 0.0F : strength(myCapitol.getUnits().getUnits(), false, false, false);
final List<Territory> areaStrength = getNeighboringLandTerritories(data, player, myCapitol);
for (final Territory areaTerr : areaStrength) {
myStrength += strength(areaTerr.getUnits().getUnits(), false, false, false) * 0.75F;
}
final boolean capDanger = myStrength < (enemyStrength * 1.25F + 3.0F);
final Resource pus = data.getResourceList().getResource(Constants.PUS);
final int pusRemaining = player.getResources().getQuantity(pus);
final Resource techtokens = data.getResourceList().getResource(Constants.TECH_TOKENS);
final int techTokensQuantity = player.getResources().getQuantity(techtokens);
int tokensToBuy = 0;
if (!capDanger && techTokensQuantity < 3 && pusRemaining > Math.random() * 160) {
tokensToBuy = 1;
}
if (techTokensQuantity > 0 || tokensToBuy > 0) {
final List<TechnologyFrontier> cats = TechAdvance.getPlayerTechCategories(player);
// retaining 65% chance of choosing land advances using basic ww2v3 model.
if (data.getTechnologyFrontier().isEmpty()) {
if (Math.random() > 0.35) {
techDelegate.rollTech(techTokensQuantity + tokensToBuy, cats.get(1), tokensToBuy, null);
} else {
techDelegate.rollTech(techTokensQuantity + tokensToBuy, cats.get(0), tokensToBuy, null);
}
} else {
final int rand = (int) (Math.random() * cats.size());
techDelegate.rollTech(techTokensQuantity + tokensToBuy, cats.get(rand), tokensToBuy, null);
}
}
}
Aggregations