gt:     for (final PlayerId enemyPlayerCandidate : enemyPlayers) {
context: package games.strategy.triplea.ai.pro;
  
  import games.strategy.engine.data.GameData;
  import games.strategy.engine.data.PlayerId;
  import games.strategy.engine.data.Resource;
  import games.strategy.engine.data.Route;
  import games.strategy.engine.data.TechnologyFrontier;
  import games.strategy.engine.data.Territory;
  import games.strategy.engine.data.Unit;
  import games.strategy.triplea.Constants;
  import games.strategy.triplea.Properties;
  import games.strategy.triplea.attachments.TerritoryAttachment;
  import games.strategy.triplea.attachments.UnitAttachment;
  import games.strategy.triplea.delegate.Matches;
  import games.strategy.triplea.delegate.MoveValidator;
  import games.strategy.triplea.delegate.TechAdvance;
  import games.strategy.triplea.delegate.TransportTracker;
  import games.strategy.triplea.delegate.remote.ITechDelegate;
  import java.util.ArrayDeque;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.Queue;
  import java.util.Set;
  import java.util.function.Predicate;
  import javax.annotation.Nullable;
  import org.triplea.java.PredicateBuilder;
  import org.triplea.java.collections.CollectionUtils;
  import org.triplea.java.collections.IntegerMap;
  
  /** Pro tech AI. */
  final class ProTechAi {
    private ProTechAi() {}
  
    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.getUnitCollection() == null)
              ? 0.0F
              : strength(myCapitol.getUnits(), false, false, false);
      final List<Territory> areaStrength = getNeighboringLandTerritories(data, player, myCapitol);
      for (final Territory areaTerr : areaStrength) {
        myStrength += strength(areaTerr.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);
        }
      }
    }
  
    /**
     * 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;
  
      @Nullable PlayerId enemyPlayer = null;
      final List<PlayerId> enemyPlayers = getEnemyPlayers(data, player);
      final Map<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;
        // should change this to read production frontier and tech
        // reality is 99% of time units considered will have full move.
        // and likely player will have at least 1 max move plane.
        for (final Territory enemyFighterTerritory : enemyFighterTerritories) {
          final List<Unit> enemyFighterUnits =
              enemyFighterTerritory.getUnitCollection().getMatches(enemyPlane);
          maxFighterDistance =
              Math.max(
                  maxFighterDistance, MoveValidator.getMaxMovement(enemyFighterUnits).intValue());
        }
        // 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.getUnitCollection().getMatches(transport);
          maxTransportDistance =
              Math.max(
                  maxTransportDistance, MoveValidator.getMaxMovement(enemyTransportUnits).intValue());
        }
        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.getUnitCollection().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 Set<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.getUnitCollection().getMatches(enemyTransport);
              if (transports.isEmpty()) {
                continue;
              }
              if (!t4.equals(waterCheck)) {
                final Route seaRoute =
                    getMaxSeaRoute(
                        data, t4, waterCheck, transports, enemyPlayer, maxTransportDistance);
                if (seaRoute == null
                    || seaRoute.getEnd() == null
                    || !seaRoute.getEnd().equals(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);
                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.getUnitCollection().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;
  