gt:         if (maxValueTerritory != null) {
context: package games.strategy.triplea.ai.pro;
  
  import games.strategy.engine.data.GameData;
  import games.strategy.engine.data.PlayerId;
  import games.strategy.engine.data.Route;
  import games.strategy.engine.data.Territory;
  import games.strategy.engine.data.Unit;
  import games.strategy.triplea.Properties;
  import games.strategy.triplea.TripleAUnit;
  import games.strategy.triplea.ai.pro.data.ProBattleResult;
  import games.strategy.triplea.ai.pro.data.ProOtherMoveOptions;
  import games.strategy.triplea.ai.pro.data.ProPlaceTerritory;
  import games.strategy.triplea.ai.pro.data.ProPurchaseOption;
  import games.strategy.triplea.ai.pro.data.ProPurchaseTerritory;
  import games.strategy.triplea.ai.pro.data.ProTerritory;
  import games.strategy.triplea.ai.pro.data.ProTerritoryManager;
  import games.strategy.triplea.ai.pro.data.ProTransport;
  import games.strategy.triplea.ai.pro.logging.ProLogger;
  import games.strategy.triplea.ai.pro.util.ProBattleUtils;
  import games.strategy.triplea.ai.pro.util.ProMatches;
  import games.strategy.triplea.ai.pro.util.ProMoveUtils;
  import games.strategy.triplea.ai.pro.util.ProOddsCalculator;
  import games.strategy.triplea.ai.pro.util.ProPurchaseUtils;
  import games.strategy.triplea.ai.pro.util.ProSortMoveOptionsUtils;
  import games.strategy.triplea.ai.pro.util.ProTerritoryValueUtils;
  import games.strategy.triplea.ai.pro.util.ProTransportUtils;
  import games.strategy.triplea.ai.pro.util.ProUtils;
  import games.strategy.triplea.attachments.TerritoryAttachment;
  import games.strategy.triplea.attachments.UnitAttachment;
  import games.strategy.triplea.delegate.AbstractMoveDelegate;
  import games.strategy.triplea.delegate.Matches;
  import games.strategy.triplea.delegate.MoveValidator;
  import games.strategy.triplea.delegate.TransportTracker;
  import games.strategy.triplea.delegate.data.MoveValidationResult;
  import games.strategy.triplea.delegate.remote.IMoveDelegate;
  import games.strategy.triplea.util.TuvUtils;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeMap;
  import java.util.function.Predicate;
  import java.util.stream.Collectors;
  import org.triplea.java.collections.CollectionUtils;
  
  /** Pro non-combat move AI. */
  class ProNonCombatMoveAi {
  
    private final ProOddsCalculator calc;
    private GameData data;
    private PlayerId player;
    private Map<Unit, Territory> unitTerritoryMap;
    private ProTerritoryManager territoryManager;
  
    ProNonCombatMoveAi(final ProAi ai) {
      calc = ai.getCalc();
    }
  
    Map<Territory, ProTerritory> simulateNonCombatMove(final IMoveDelegate moveDel) {
      return doNonCombatMove(null, null, moveDel);
    }
  
    Map<Territory, ProTerritory> doNonCombatMove(
        final Map<Territory, ProTerritory> initialFactoryMoveMap,
        final Map<Territory, ProPurchaseTerritory> purchaseTerritories,
        final IMoveDelegate moveDel) {
      ProLogger.info("Starting non-combat move phase");
  
      // Current data at the start of non-combat move
      data = ProData.getData();
      player = ProData.getPlayer();
      unitTerritoryMap = ProData.unitTerritoryMap;
      territoryManager = new ProTerritoryManager(calc);
  
      // Find the max number of units that can move to each allied territory
      territoryManager.populateDefenseOptions(new ArrayList<>());
  
      // Find number of units in each move territory that can't move and all infra units
      findUnitsThatCantMove(purchaseTerritories, ProData.purchaseOptions.getLandOptions());
      final Map<Unit, Set<Territory>> infraUnitMoveMap = findInfraUnitsThatCanMove();
  
      // Try to have one land unit in each territory that is bordering an enemy territory
      final List<Territory> movedOneDefenderToTerritories =
          moveOneDefenderToLandTerritoriesBorderingEnemy();
  
      // Determine max enemy attack units and if territories can be held
      territoryManager.populateEnemyAttackOptions(
          movedOneDefenderToTerritories, territoryManager.getDefendTerritories());
      determineIfMoveTerritoriesCanBeHeld();
  
      // Prioritize territories to defend
      Map<Territory, ProTerritory> factoryMoveMap = initialFactoryMoveMap;
      final List<ProTerritory> prioritizedTerritories = prioritizeDefendOptions(factoryMoveMap);
  
      // Determine which territories to defend and how many units each one needs
      final int enemyDistance =
          ProUtils.getClosestEnemyLandTerritoryDistance(data, player, ProData.myCapital);
      moveUnitsToDefendTerritories(prioritizedTerritories, enemyDistance);
  
      // Copy data in case capital defense needs increased
      final ProTerritoryManager territoryManagerCopy =
          new ProTerritoryManager(calc, territoryManager);
  
      // Get list of territories that can't be held and find move value for each territory
      final List<Territory> territoriesThatCantBeHeld = territoryManager.getCantHoldTerritories();
      final Map<Territory, Double> territoryValueMap =
          ProTerritoryValueUtils.findTerritoryValues(
              player,
              territoriesThatCantBeHeld,
              new ArrayList<>(),
              new HashSet<>(territoryManager.getDefendTerritories()));
      final Map<Territory, Double> seaTerritoryValueMap =
          ProTerritoryValueUtils.findSeaTerritoryValues(
              player, territoriesThatCantBeHeld, territoryManager.getDefendTerritories());
  
      // Use loop to ensure capital is protected after moves
      if (ProData.myCapital != null) {
        int defenseRange = -1;
        while (true) {
  
          // Add value to territories near capital if necessary
          for (final Territory t : territoryManager.getDefendTerritories()) {
            double value = territoryValueMap.get(t);
            final int distance =
                data.getMap()
                    .getDistance(
                        ProData.myCapital,
                        t,
                        ProMatches.territoryCanMoveLandUnits(player, data, false));
            if (distance >= 0 && distance <= defenseRange) {
              value *= 10;
            }
            territoryManager.getDefendOptions().getTerritoryMap().get(t).setValue(value);
            if (t.isWater()) {
              territoryManager
                  .getDefendOptions()
                  .getTerritoryMap()
                  .get(t)
                  .setSeaValue(seaTerritoryValueMap.get(t));
            }
          }
  
          moveUnitsToBestTerritories();
  
          // Check if capital has local land superiority
          ProLogger.info(
              "Checking if capital has local land superiority with enemyDistance=" + enemyDistance);
          if (enemyDistance >= 2
              && enemyDistance <= 3
              && defenseRange == -1
              && !ProBattleUtils.territoryHasLocalLandSuperiorityAfterMoves(
                  ProData.myCapital,
                  enemyDistance,
                  player,
                  territoryManager.getDefendOptions().getTerritoryMap())) {
            defenseRange = enemyDistance - 1;
            territoryManager = territoryManagerCopy;
            ProLogger.debug(
                "Capital doesn't have local land superiority so setting defensive stance");
          } else {
            break;
          }
        }
      } else {
        moveUnitsToBestTerritories();
      }
  
      // Determine where to move infra units
      factoryMoveMap = moveInfraUnits(factoryMoveMap, infraUnitMoveMap);
  
      // Log a warning if any units not assigned to a territory (skip infrastructure for now)
      for (final Unit u : territoryManager.getDefendOptions().getUnitMoveMap().keySet()) {
        if (Matches.unitIsInfrastructure().negate().test(u)) {
          ProLogger.warn(
              player
                  + ": "
                  + unitTerritoryMap.get(u)
                  + " has unmoved unit: "
                  + u
                  + " with options: "
                  + territoryManager.getDefendOptions().getUnitMoveMap().get(u));
        }
      }
  
      // Calculate move routes and perform moves
      doMove(territoryManager.getDefendOptions().getTerritoryMap(), moveDel, data, player);
  
      // Log results
      ProLogger.info("Logging results");
      logAttackMoves(prioritizedTerritories);
  
      territoryManager = null;
      return factoryMoveMap;
    }
  
    void doMove(
        final Map<Territory, ProTerritory> moveMap,
        final IMoveDelegate moveDel,
        final GameData data,
        final PlayerId player) {
  
      this.data = data;
      this.player = player;
  
      // Calculate move routes and perform moves
      final List<Collection<Unit>> moveUnits = new ArrayList<>();
      final List<Route> moveRoutes = new ArrayList<>();
      ProMoveUtils.calculateMoveRoutes(player, moveUnits, moveRoutes, moveMap, false);
      ProMoveUtils.doMove(moveUnits, moveRoutes, moveDel);
  
      // Calculate amphib move routes and perform moves
      moveUnits.clear();
      moveRoutes.clear();
      final List<Collection<Unit>> transportsToLoad = new ArrayList<>();
      ProMoveUtils.calculateAmphibRoutes(
          player, moveUnits, moveRoutes, transportsToLoad, moveMap, false);
      ProMoveUtils.doMove(moveUnits, moveRoutes, transportsToLoad, moveDel);
    }
  
    private void findUnitsThatCantMove(
        final Map<Territory, ProPurchaseTerritory> purchaseTerritories,
        final List<ProPurchaseOption> landPurchaseOptions) {
  
      ProLogger.info("Find units that can't move");
  
      final Map<Territory, ProTerritory> moveMap =
          territoryManager.getDefendOptions().getTerritoryMap();
      final Map<Unit, Set<Territory>> unitMoveMap =
          territoryManager.getDefendOptions().getUnitMoveMap();
      final List<ProTransport> transportMapList =
          territoryManager.getDefendOptions().getTransportList();
  
      // Add all units that can't move (allied units, 0 move units, etc)
      for (final Territory t : moveMap.keySet()) {
        moveMap
            .get(t)
            .getCantMoveUnits()
            .addAll(
                t.getUnitCollection()
                    .getMatches(ProMatches.unitCantBeMovedAndIsAlliedDefender(player, data, t)));
      }
  
      // Add all units that only have 1 move option and can't be transported
      for (final Iterator<Unit> it = unitMoveMap.keySet().iterator(); it.hasNext(); ) {
        final Unit u = it.next();
        if (unitMoveMap.get(u).size() == 1) {
          final Territory onlyTerritory = unitMoveMap.get(u).iterator().next();
          if (onlyTerritory.equals(unitTerritoryMap.get(u))) {
            boolean canBeTransported = false;
            for (final ProTransport pad : transportMapList) {
              for (final Territory t : pad.getTransportMap().keySet()) {
                if (pad.getTransportMap().get(t).contains(onlyTerritory)) {
                  canBeTransported = true;
                }
              }
              for (final Territory t : pad.getSeaTransportMap().keySet()) {
                if (pad.getSeaTransportMap().get(t).contains(onlyTerritory)) {
                  canBeTransported = true;
                }
              }
            }
            if (!canBeTransported) {
              moveMap.get(onlyTerritory).getCantMoveUnits().add(u);
              it.remove();
            }
          }
        }
      }
  
      // Check if purchase units are known yet
      if (purchaseTerritories != null) {
  
        // Add all units that will be purchased
        for (final ProPurchaseTerritory ppt : purchaseTerritories.values()) {
          for (final ProPlaceTerritory placeTerritory : ppt.getCanPlaceTerritories()) {
            final Territory t = placeTerritory.getTerritory();
            if (moveMap.get(t) != null) {
              moveMap.get(t).getCantMoveUnits().addAll(placeTerritory.getPlaceUnits());
            }
          }
        }
      } else {
  
        // Add max defenders that can be purchased to each territory
        for (final Territory t : moveMap.keySet()) {
          if (ProMatches.territoryHasNonMobileFactoryAndIsNotConqueredOwnedLand(player, data)
              .test(t)) {
            moveMap
                .get(t)
                .getCantMoveUnits()
                .addAll(ProPurchaseUtils.findMaxPurchaseDefenders(player, t, landPurchaseOptions));
          }
        }
      }
  
      // Log can't move units per territory
      for (final Territory t : moveMap.keySet()) {
        if (!moveMap.get(t).getCantMoveUnits().isEmpty()) {
          ProLogger.trace(t + " has units that can't move: " + moveMap.get(t).getCantMoveUnits());
        }
      }
    }
  
    private Map<Unit, Set<Territory>> findInfraUnitsThatCanMove() {
  
      ProLogger.info("Find non-combat infra units that can move");
  
      final Map<Unit, Set<Territory>> unitMoveMap =
          territoryManager.getDefendOptions().getUnitMoveMap();
  
      // Add all units that are infra
      final Map<Unit, Set<Territory>> infraUnitMoveMap = new HashMap<>();
      for (final Iterator<Unit> it = unitMoveMap.keySet().iterator(); it.hasNext(); ) {
        final Unit u = it.next();
        if (ProMatches.unitCanBeMovedAndIsOwnedNonCombatInfra(player).test(u)) {
          infraUnitMoveMap.put(u, unitMoveMap.get(u));
          ProLogger.trace(u + " is infra unit with move options: " + unitMoveMap.get(u));
          it.remove();
        }
      }
      return infraUnitMoveMap;
    }
  
    private List<Territory> moveOneDefenderToLandTerritoriesBorderingEnemy() {
  
      ProLogger.info("Determine which territories to defend with one land unit");
  
      final Map<Territory, ProTerritory> moveMap =
          territoryManager.getDefendOptions().getTerritoryMap();
      final Map<Unit, Set<Territory>> unitMoveMap =
          territoryManager.getDefendOptions().getUnitMoveMap();
  
      // Find land territories with no can't move units and adjacent to enemy land units
      final List<Territory> territoriesToDefendWithOneUnit = new ArrayList<>();
      for (final Territory t : moveMap.keySet()) {
        final boolean hasAlliedLandUnits =
            moveMap.get(t).getCantMoveUnits().stream()
                .anyMatch(ProMatches.unitIsAlliedLandAndNotInfra(player, data));
        if (!t.isWater()
            && !hasAlliedLandUnits
            && ProMatches.territoryHasNeighborOwnedByAndHasLandUnit(
                    data, ProUtils.getPotentialEnemyPlayers(player))
                .test(t)) {
          territoriesToDefendWithOneUnit.add(t);
        }
      }
      final List<Territory> result = new ArrayList<>(territoriesToDefendWithOneUnit);
  
      // Sort units by number of defend options and cost
      final Map<Unit, Set<Territory>> sortedUnitMoveOptions =
          ProSortMoveOptionsUtils.sortUnitMoveOptions(unitMoveMap);
  
      // Set unit with the fewest move options in each territory
      for (final Unit unit : sortedUnitMoveOptions.keySet()) {
        if (Matches.unitIsLand().test(unit)) {
          for (final Territory t : sortedUnitMoveOptions.get(unit)) {
            final int unitValue = ProData.unitValueMap.getInt(unit.getType());
            int production = 0;
            final TerritoryAttachment ta = TerritoryAttachment.get(t);
            if (ta != null) {
              production = ta.getProduction();
            }
  
            // Only defend territories that either already have units (avoid abandoning territories)
            // or where unit value is less than production + 3 (avoid sacrificing expensive units to
            // block)
            if (territoriesToDefendWithOneUnit.contains(t)
                && (unitValue <= (production + 3)
                    || Matches.territoryHasUnitsOwnedBy(player).test(t))) {
              moveMap.get(t).addUnit(unit);
              unitMoveMap.remove(unit);
              territoriesToDefendWithOneUnit.remove(t);
              ProLogger.debug(t + ", added one land unit: " + unit);
              break;
            }
          }
          if (territoriesToDefendWithOneUnit.isEmpty()) {
            break;
          }
        }
      }
  
      // Only return territories that received a defender
      result.removeAll(territoriesToDefendWithOneUnit);
  
      return result;
    }
  
    private void determineIfMoveTerritoriesCanBeHeld() {
  
      ProLogger.info("Find max enemy attackers and if territories can be held");
  
      final Map<Territory, ProTerritory> moveMap =
          territoryManager.getDefendOptions().getTerritoryMap();
      final ProOtherMoveOptions enemyAttackOptions = territoryManager.getEnemyAttackOptions();
  
      // Determine which territories can possibly be held
      for (final Territory t : moveMap.keySet()) {
        final ProTerritory patd = moveMap.get(t);
  
        // Check if no enemy attackers
        if (enemyAttackOptions.getMax(t) == null) {
          ProLogger.debug("Territory=" + t.getName() + ", CanHold=true since has no enemy attackers");
          continue;
        }
  
        // Check if min defenders can hold it (not considering AA)
        final Set<Unit> enemyAttackingUnits =
            new HashSet<>(enemyAttackOptions.getMax(t).getMaxUnits());
        enemyAttackingUnits.addAll(enemyAttackOptions.getMax(t).getMaxAmphibUnits());
        patd.setMaxEnemyUnits(new ArrayList<>(enemyAttackingUnits));
        patd.setMaxEnemyBombardUnits(enemyAttackOptions.getMax(t).getMaxBombardUnits());
        final List<Unit> minDefendingUnitsAndNotAa =
            CollectionUtils.getMatches(
                patd.getCantMoveUnits(), Matches.unitIsAaForAnything().negate());
        final ProBattleResult minResult =
            calc.calculateBattleResults(
                t,
                new ArrayList<>(enemyAttackingUnits),
                minDefendingUnitsAndNotAa,
                enemyAttackOptions.getMax(t).getMaxBombardUnits());
        patd.setMinBattleResult(minResult);
        if (minResult.getTuvSwing() <= 0 && !minDefendingUnitsAndNotAa.isEmpty()) {
          ProLogger.debug(
              "Territory="
                  + t.getName()
                  + ", CanHold=true"
                  + ", MinDefenders="
                  + minDefendingUnitsAndNotAa.size()
                  + ", EnemyAttackers="
                  + enemyAttackingUnits.size()
                  + ", win%="
                  + minResult.getWinPercentage()
                  + ", EnemyTUVSwing="
                  + minResult.getTuvSwing()
                  + ", hasLandUnitRemaining="
                  + minResult.isHasLandUnitRemaining());
          continue;
        }
  
        // Check if max defenders can hold it (not considering AA)
        final Set<Unit> defendingUnits = new HashSet<>(patd.getMaxUnits());
        defendingUnits.addAll(patd.getMaxAmphibUnits());
        defendingUnits.addAll(patd.getCantMoveUnits());
        final List<Unit> defendingUnitsAndNotAa =
            CollectionUtils.getMatches(defendingUnits, Matches.unitIsAaForAnything().negate());
        final ProBattleResult result =
            calc.calculateBattleResults(
                t,
                new ArrayList<>(enemyAttackingUnits),
                defendingUnitsAndNotAa,
                enemyAttackOptions.getMax(t).getMaxBombardUnits());
        int isFactory = 0;
        if (ProMatches.territoryHasInfraFactoryAndIsLand().test(t)) {
          isFactory = 1;
        }
        int isMyCapital = 0;
        if (t.equals(ProData.myCapital)) {
          isMyCapital = 1;
        }
        final List<Unit> extraUnits = new ArrayList<>(defendingUnitsAndNotAa);
        extraUnits.removeAll(minDefendingUnitsAndNotAa);
        final double extraUnitValue = TuvUtils.getTuv(extraUnits, ProData.unitValueMap);
        final double holdValue = extraUnitValue / 8 * (1 + 0.5 * isFactory) * (1 + 2.0 * isMyCapital);
        if (minDefendingUnitsAndNotAa.size() != defendingUnitsAndNotAa.size()
            && (result.getTuvSwing() - holdValue) < minResult.getTuvSwing()) {
          ProLogger.debug(
              "Territory="
                  + t.getName()
                  + ", CanHold=true"
                  + ", MaxDefenders="
                  + defendingUnitsAndNotAa.size()
                  + ", EnemyAttackers="
                  + enemyAttackingUnits.size()
                  + ", minTUVSwing="
                  + minResult.getTuvSwing()
                  + ", win%="
                  + result.getWinPercentage()
                  + ", EnemyTUVSwing="
                  + result.getTuvSwing()
                  + ", hasLandUnitRemaining="
                  + result.isHasLandUnitRemaining()
                  + ", holdValue="
                  + holdValue);
          continue;
        }
  
        // Can't hold territory
        patd.setCanHold(false);
        ProLogger.debug(
            "Can't hold Territory="
                + t.getName()
                + ", MaxDefenders="
                + defendingUnitsAndNotAa.size()
                + ", EnemyAttackers="
                + enemyAttackingUnits.size()
                + ", minTUVSwing="
                + minResult.getTuvSwing()
                + ", win%="
                + result.getWinPercentage()
                + ", EnemyTUVSwing="
                + result.getTuvSwing()
                + ", hasLandUnitRemaining="
                + result.isHasLandUnitRemaining()
                + ", holdValue="
                + holdValue);
      }
    }
  
    private List<ProTerritory> prioritizeDefendOptions(
        final Map<Territory, ProTerritory> factoryMoveMap) {
  
      ProLogger.info("Prioritizing territories to try to defend");
  
      final Map<Territory, ProTerritory> moveMap =
          territoryManager.getDefendOptions().getTerritoryMap();
      final ProOtherMoveOptions enemyAttackOptions = territoryManager.getEnemyAttackOptions();
  
      // Calculate value of defending territory
      for (final Territory t : moveMap.keySet()) {
  
        // Determine if it is my capital or adjacent to my capital
        int isMyCapital = 0;
        if (t.equals(ProData.myCapital)) {
          isMyCapital = 1;
        }
  
        // Determine if it has a factory
        int isFactory = 0;
        if (ProMatches.territoryHasInfraFactoryAndIsLand().test(t)
            || (factoryMoveMap != null && factoryMoveMap.containsKey(t))) {
          isFactory = 1;
        }
  
        // Determine production value and if it is an enemy capital
        int production = 0;
        int isEnemyOrAlliedCapital = 0;
        final TerritoryAttachment ta = TerritoryAttachment.get(t);
        if (ta != null) {
          production = ta.getProduction();
          if (ta.isCapital() && !t.equals(ProData.myCapital)) {
            isEnemyOrAlliedCapital = 1;
          }
        }
  
        // Determine neighbor value
        double neighborValue = 0;
        if (!t.isWater()) {
          final Set<Territory> landNeighbors =
              data.getMap().getNeighbors(t, Matches.territoryIsLand());
          for (final Territory neighbor : landNeighbors) {
            double neighborProduction = TerritoryAttachment.getProduction(neighbor);
            if (Matches.isTerritoryAllied(player, data).test(neighbor)) {
              neighborProduction = 0.1 * neighborProduction;
            }
            neighborValue += neighborProduction;
          }
        }
  
        // Determine defending unit value
        final int cantMoveUnitValue =
            TuvUtils.getTuv(moveMap.get(t).getCantMoveUnits(), ProData.unitValueMap);
        double unitOwnerMultiplier = 1;
        if (moveMap.get(t).getCantMoveUnits().stream().noneMatch(Matches.unitIsOwnedBy(player))) {
          if (t.isWater()
              && moveMap.get(t).getCantMoveUnits().stream()
                  .noneMatch(Matches.unitIsTransportButNotCombatTransport())) {
            unitOwnerMultiplier = 0;
          } else {
            unitOwnerMultiplier = 0.5;
          }
        }
  
        // Calculate defense value for prioritization
        final double territoryValue =
            unitOwnerMultiplier
                * (2.0 * production
                    + 10.0 * isFactory
                    + 0.5 * cantMoveUnitValue
                    + 0.5 * neighborValue)
                * (1 + 10.0 * isMyCapital)
                * (1 + 4.0 * isEnemyOrAlliedCapital);
        moveMap.get(t).setValue(territoryValue);
      }
  
      // Sort attack territories by value
      final List<ProTerritory> prioritizedTerritories = new ArrayList<>(moveMap.values());
      prioritizedTerritories.sort(Comparator.comparingDouble(ProTerritory::getValue).reversed());
  
      // Remove territories that I'm not going to try to defend
      for (final Iterator<ProTerritory> it = prioritizedTerritories.iterator(); it.hasNext(); ) {
        final ProTerritory patd = it.next();
        final Territory t = patd.getTerritory();
        final boolean hasFactory = ProMatches.territoryHasInfraFactoryAndIsLand().test(t);
        final ProBattleResult minResult = patd.getMinBattleResult();
        final int cantMoveUnitValue =
            TuvUtils.getTuv(moveMap.get(t).getCantMoveUnits(), ProData.unitValueMap);
        final List<Unit> maxEnemyUnits = patd.getMaxEnemyUnits();
        final boolean isLandAndCanOnlyBeAttackedByAir =
            !t.isWater()
                && !maxEnemyUnits.isEmpty()
                && maxEnemyUnits.stream().allMatch(Matches.unitIsAir());
        final boolean isNotFactoryAndShouldHold =
            !hasFactory && (minResult.getTuvSwing() <= 0 || !minResult.isHasLandUnitRemaining());
        final boolean canAlreadyBeHeld =
            minResult.getTuvSwing() <= 0
                && minResult.getWinPercentage() < (100 - ProData.winPercentage);
        final boolean isNotFactoryAndHasNoEnemyNeighbors =
            !t.isWater()
                && !hasFactory
                && !ProMatches.territoryHasNeighborOwnedByAndHasLandUnit(
                        data, ProUtils.getPotentialEnemyPlayers(player))
                    .test(t);
        final boolean isNotFactoryAndOnlyAmphib =
            !t.isWater()
                && !hasFactory
                && moveMap.get(t).getMaxUnits().stream().noneMatch(Matches.unitIsLand())
                && cantMoveUnitValue < 5;
        if (!patd.isCanHold()
            || patd.getValue() <= 0
            || isLandAndCanOnlyBeAttackedByAir
            || isNotFactoryAndShouldHold
            || canAlreadyBeHeld
            || isNotFactoryAndHasNoEnemyNeighbors
            || isNotFactoryAndOnlyAmphib) {
          final double tuvSwing = minResult.getTuvSwing();
          final boolean hasRemainingLandUnit = minResult.isHasLandUnitRemaining();
          ProLogger.debug(
              "Removing territory="
                  + t.getName()
                  + ", value="
                  + patd.getValue()
                  + ", CanHold="
                  + patd.isCanHold()
                  + ", isLandAndCanOnlyBeAttackedByAir="
                  + isLandAndCanOnlyBeAttackedByAir
                  + ", isNotFactoryAndShouldHold="
                  + isNotFactoryAndShouldHold
                  + ", canAlreadyBeHeld="
                  + canAlreadyBeHeld
                  + ", isNotFactoryAndHasNoEnemyNeighbors="
                  + isNotFactoryAndHasNoEnemyNeighbors
                  + ", isNotFactoryAndOnlyAmphib="
                  + isNotFactoryAndOnlyAmphib
                  + ", tuvSwing="
                  + tuvSwing
                  + ", hasRemainingLandUnit="
                  + hasRemainingLandUnit
                  + ", maxEnemyUnits="
                  + patd.getMaxEnemyUnits().size());
          it.remove();
        }
      }
  
      // Add best sea production territory for sea factories
      List<Territory> seaFactories =
          CollectionUtils.getMatches(
              data.getMap().getTerritories(),
              ProMatches.territoryHasFactoryAndIsNotConqueredOwnedLand(player, data));
      seaFactories =
          CollectionUtils.getMatches(
              seaFactories,
              ProMatches.territoryHasInfraFactoryAndIsOwnedLandAdjacentToSea(player, data));
      final Set<Territory> territoriesToCheck = new HashSet<>(seaFactories);
      for (final Territory t : seaFactories) {
        territoriesToCheck.addAll(
            data.getMap().getNeighbors(t, ProMatches.territoryCanMoveSeaUnits(player, data, true)));
      }
      final Map<Territory, Double> territoryValueMap =
          ProTerritoryValueUtils.findTerritoryValues(
              player,
              territoryManager.getCantHoldTerritories(),
              new ArrayList<>(),
              territoriesToCheck);
      for (final Territory t : seaFactories) {
        if (territoryValueMap.get(t) >= 1) {
          continue;
        }
        final Set<Territory> neighbors =
            data.getMap().getNeighbors(t, ProMatches.territoryCanMoveSeaUnits(player, data, true));
        double maxValue = 0;
        Territory maxTerritory = null;
        for (final Territory neighbor : neighbors) {
          if (moveMap.get(neighbor) != null
              && moveMap.get(neighbor).isCanHold()
              && territoryValueMap.get(neighbor) > maxValue) {
            maxTerritory = neighbor;
            maxValue = territoryValueMap.get(neighbor);
          }
        }
        if (maxTerritory != null && enemyAttackOptions.getMax(maxTerritory) != null) {
          boolean alreadyAdded = false;
          for (final ProTerritory patd : prioritizedTerritories) {
            if (patd.getTerritory().equals(maxTerritory)) {
              alreadyAdded = true;
              break;
            }
          }
          if (!alreadyAdded) {
            prioritizedTerritories.add(moveMap.get(maxTerritory));
          }
        }
      }
  
      // Log prioritized territories
      for (final ProTerritory attackTerritoryData : prioritizedTerritories) {
        ProLogger.debug(
            "Value="
                + attackTerritoryData.getValue()
                + ", "
                + attackTerritoryData.getTerritory().getName());
      }
      return prioritizedTerritories;
    }
  
    private void moveUnitsToDefendTerritories(
        final List<ProTerritory> prioritizedTerritories, final int enemyDistance) {
  
      ProLogger.info("Determine units to defend territories with");
      if (prioritizedTerritories.isEmpty()) {
        return;
      }
  
      final Map<Territory, ProTerritory> moveMap =
          territoryManager.getDefendOptions().getTerritoryMap();
      final Map<Unit, Set<Territory>> unitMoveMap =
          territoryManager.getDefendOptions().getUnitMoveMap();
      final Map<Unit, Set<Territory>> transportMoveMap =
          territoryManager.getDefendOptions().getTransportMoveMap();
      final List<ProTransport> transportMapList =
          territoryManager.getDefendOptions().getTransportList();
  
      // Assign units to territories by prioritization
      int numToDefend = 1;
      while (true) {
  
        // Reset lists
        for (final ProTerritory t : moveMap.values()) {
          t.getTempUnits().clear();
          t.getTempAmphibAttackMap().clear();
          t.getTransportTerritoryMap().clear();
          t.setBattleResult(null);
        }
  
        // Determine number of territories to defend
        if (numToDefend <= 0) {
          break;
        }
        final List<ProTerritory> territoriesToTryToDefend =
            prioritizedTerritories.subList(0, numToDefend);
  
        // Loop through all units and determine defend options
        final Map<Unit, Set<Territory>> unitDefendOptions = new HashMap<>();
        for (final Unit unit : unitMoveMap.keySet()) {
  
          // Find number of move options
          final Set<Territory> canDefendTerritories = new LinkedHashSet<>();
          for (final ProTerritory attackTerritoryData : territoriesToTryToDefend) {
            if (unitMoveMap.get(unit).contains(attackTerritoryData.getTerritory())) {
              canDefendTerritories.add(attackTerritoryData.getTerritory());
            }
          }
          unitDefendOptions.put(unit, canDefendTerritories);
        }
  
        // Sort units by number of defend options and cost
        final Map<Unit, Set<Territory>> sortedUnitMoveOptions =
            ProSortMoveOptionsUtils.sortUnitMoveOptions(unitDefendOptions);
        final List<Unit> addedUnits = new ArrayList<>();
  
        // Set enough units in territories to have at least a chance of winning
        for (final Unit unit : sortedUnitMoveOptions.keySet()) {
          final boolean isAirUnit = UnitAttachment.get(unit.getType()).getIsAir();
          if (isAirUnit || Matches.unitIsCarrier().test(unit) || addedUnits.contains(unit)) {
            continue; // skip air and carrier units
          }
          final TreeMap<Double, Territory> estimatesMap = new TreeMap<>();
          for (final Territory t : sortedUnitMoveOptions.get(unit)) {
            List<Unit> defendingUnits =
                CollectionUtils.getMatches(
                    moveMap.get(t).getAllDefenders(),
                    ProMatches.unitIsAlliedNotOwnedAir(player, data).negate());
            if (t.isWater()) {
              defendingUnits = moveMap.get(t).getAllDefenders();
            }
            final double estimate =
                ProBattleUtils.estimateStrengthDifference(
                    t, moveMap.get(t).getMaxEnemyUnits(), defendingUnits);
            estimatesMap.put(estimate, t);
          }
          if (!estimatesMap.isEmpty() && estimatesMap.lastKey() > 60) {
            final Territory minWinTerritory = estimatesMap.lastEntry().getValue();
            final List<Unit> unitsToAdd = ProTransportUtils.getUnitsToAdd(unit, moveMap);
            moveMap.get(minWinTerritory).addTempUnits(unitsToAdd);
            addedUnits.addAll(unitsToAdd);
          }
        }
        sortedUnitMoveOptions.keySet().removeAll(addedUnits);
  
        // Set non-air units in territories
        for (final Unit unit : sortedUnitMoveOptions.keySet()) {
          if (Matches.unitCanLandOnCarrier().test(unit) || addedUnits.contains(unit)) {
            continue;
          }
          Territory maxWinTerritory = null;
          double maxWinPercentage = -1;
          for (final Territory t : sortedUnitMoveOptions.get(unit)) {
            List<Unit> defendingUnits =
                CollectionUtils.getMatches(
                    moveMap.get(t).getAllDefenders(),
                    ProMatches.unitIsAlliedNotOwnedAir(player, data).negate());
            if (t.isWater()) {
              defendingUnits = moveMap.get(t).getAllDefenders();
            }
            if (moveMap.get(t).getBattleResult() == null) {
              moveMap
                  .get(t)
                  .setBattleResult(
                      calc.estimateDefendBattleResults(
                          t,
                          moveMap.get(t).getMaxEnemyUnits(),
                          defendingUnits,
                          moveMap.get(t).getMaxEnemyBombardUnits()));
            }
            final ProBattleResult result = moveMap.get(t).getBattleResult();
            final boolean hasFactory = ProMatches.territoryHasInfraFactoryAndIsLand().test(t);
            if (result.getWinPercentage() > maxWinPercentage
                && ((t.equals(ProData.myCapital)
                        && result.getWinPercentage() > (100 - ProData.winPercentage))
                    || (hasFactory && result.getWinPercentage() > (100 - ProData.minWinPercentage))
                    || result.getTuvSwing() >= 0)) {
              maxWinTerritory = t;
              maxWinPercentage = result.getWinPercentage();
            }
          }
          if (maxWinTerritory != null) {
            moveMap.get(maxWinTerritory).setBattleResult(null);
            final List<Unit> unitsToAdd = ProTransportUtils.getUnitsToAdd(unit, moveMap);
            moveMap.get(maxWinTerritory).addTempUnits(unitsToAdd);
            addedUnits.addAll(unitsToAdd);
  
            // If carrier has dependent allied fighters then move them too
            if (Matches.unitIsCarrier().test(unit)) {
              final Territory unitTerritory = unitTerritoryMap.get(unit);
              final Map<Unit, Collection<Unit>> carrierMustMoveWith =
                  MoveValidator.carrierMustMoveWith(
                      unitTerritory.getUnits(), unitTerritory, data, player);
              if (carrierMustMoveWith.containsKey(unit)) {
                moveMap.get(maxWinTerritory).getTempUnits().addAll(carrierMustMoveWith.get(unit));
              }
            }
          }
        }
        sortedUnitMoveOptions.keySet().removeAll(addedUnits);
  
        // Set air units in territories
        for (final Unit unit : sortedUnitMoveOptions.keySet()) {
          Territory maxWinTerritory = null;
          double maxWinPercentage = -1;
          for (final Territory t : sortedUnitMoveOptions.get(unit)) {
            if (t.isWater() && Matches.unitIsAir().test(unit)) {
              if (!ProTransportUtils.validateCarrierCapacity(
                  player, t, moveMap.get(t).getAllDefendersForCarrierCalcs(data, player), unit)) {
                continue; // skip moving air to water if not enough carrier capacity
              }
            }
            if (!t.isWater()
                && !t.getOwner().equals(player)
                && Matches.unitIsAir().test(unit)
                && !ProMatches.territoryHasInfraFactoryAndIsLand().test(t)) {
              continue; // skip moving air units to allied land without a factory
            }
            List<Unit> defendingUnits =
                CollectionUtils.getMatches(
                    moveMap.get(t).getAllDefenders(),
                    ProMatches.unitIsAlliedNotOwnedAir(player, data).negate());
            if (t.isWater()) {
              defendingUnits = moveMap.get(t).getAllDefenders();
            }
            if (moveMap.get(t).getBattleResult() == null) {
              moveMap
                  .get(t)
                  .setBattleResult(
                      calc.estimateDefendBattleResults(
                          t,
                          moveMap.get(t).getMaxEnemyUnits(),
                          defendingUnits,
                          moveMap.get(t).getMaxEnemyBombardUnits()));
            }
            final ProBattleResult result = moveMap.get(t).getBattleResult();
            final boolean hasFactory = ProMatches.territoryHasInfraFactoryAndIsLand().test(t);
            if (result.getWinPercentage() > maxWinPercentage
                && ((t.equals(ProData.myCapital)
                        && result.getWinPercentage() > (100 - ProData.winPercentage))
                    || (hasFactory && result.getWinPercentage() > (100 - ProData.minWinPercentage))
                    || result.getTuvSwing() >= 0)) {
              maxWinTerritory = t;
              maxWinPercentage = result.getWinPercentage();
            }
          }
          if (maxWinTerritory != null) {
            moveMap.get(maxWinTerritory).addTempUnit(unit);
            moveMap.get(maxWinTerritory).setBattleResult(null);
            addedUnits.add(unit);
          }
        }
        sortedUnitMoveOptions.keySet().removeAll(addedUnits);
  
        // Loop through all my transports and see which territories they can defend from current list
        final List<Unit> alreadyMovedTransports = new ArrayList<>();
        if (!Properties.getTransportCasualtiesRestricted(data)) {
          final Map<Unit, Set<Territory>> transportDefendOptions = new HashMap<>();
          for (final Unit unit : transportMoveMap.keySet()) {
  
            // Find number of defend options
            final Set<Territory> canDefendTerritories = new HashSet<>();
            for (final ProTerritory attackTerritoryData : territoriesToTryToDefend) {
              if (transportMoveMap.get(unit).contains(attackTerritoryData.getTerritory())) {
                canDefendTerritories.add(attackTerritoryData.getTerritory());
              }
            }
            if (!canDefendTerritories.isEmpty()) {
              transportDefendOptions.put(unit, canDefendTerritories);
            }
          }
  
          // Loop through transports with move options and determine if any naval defense needs it
          for (final Unit transport : transportDefendOptions.keySet()) {
  
            // Find current naval defense that needs transport if it isn't transporting units
            for (final Territory t : transportDefendOptions.get(transport)) {
              if (!TransportTracker.isTransporting(transport)) {
                final List<Unit> defendingUnits = moveMap.get(t).getAllDefenders();
                if (moveMap.get(t).getBattleResult() == null) {
                  moveMap
                      .get(t)
                      .setBattleResult(
                          calc.estimateDefendBattleResults(
                              t,
                              moveMap.get(t).getMaxEnemyUnits(),
                              defendingUnits,
                              moveMap.get(t).getMaxEnemyBombardUnits()));
                }
                final ProBattleResult result = moveMap.get(t).getBattleResult();
                if (result.getTuvSwing() > 0) {
                  moveMap.get(t).addTempUnit(transport);
                  moveMap.get(t).setBattleResult(null);
                  alreadyMovedTransports.add(transport);
                  ProLogger.trace("Adding defend transport to: " + t.getName());
                  break;
                }
              }
            }
          }
        }
  
        // Loop through all my transports and see which can make amphib move
        final Map<Unit, Set<Territory>> amphibMoveOptions = new HashMap<>();
        for (final ProTransport proTransportData : transportMapList) {
  
          // If already used to defend then ignore
          if (alreadyMovedTransports.contains(proTransportData.getTransport())) {
            continue;
          }
  
          // Find number of amphib move options
          final Set<Territory> canAmphibMoveTerritories = new HashSet<>();
          for (final ProTerritory attackTerritoryData : territoriesToTryToDefend) {
            if (proTransportData.getTransportMap().containsKey(attackTerritoryData.getTerritory())) {
              canAmphibMoveTerritories.add(attackTerritoryData.getTerritory());
            }
          }
          if (!canAmphibMoveTerritories.isEmpty()) {
            amphibMoveOptions.put(proTransportData.getTransport(), canAmphibMoveTerritories);
          }
        }
  
        // Loop through transports with amphib move options and determine if any land defense needs it
        for (final Unit transport : amphibMoveOptions.keySet()) {
  
          // Find current land defense results for territories that unit can amphib move
          for (final Territory t : amphibMoveOptions.get(transport)) {
            final List<Unit> defendingUnits = moveMap.get(t).getAllDefenders();
            if (moveMap.get(t).getBattleResult() == null) {
              moveMap
                  .get(t)
                  .setBattleResult(
                      calc.estimateDefendBattleResults(
                          t,
                          moveMap.get(t).getMaxEnemyUnits(),
                          defendingUnits,
                          moveMap.get(t).getMaxEnemyBombardUnits()));
            }
            final ProBattleResult result = moveMap.get(t).getBattleResult();
            final boolean hasFactory = ProMatches.territoryHasInfraFactoryAndIsLand().test(t);
            if ((t.equals(ProData.myCapital)
                    && result.getWinPercentage() > (100 - ProData.winPercentage))
                || (hasFactory && result.getWinPercentage() > (100 - ProData.minWinPercentage))
                || result.getTuvSwing() > 0) {
  
              // Get all units that have already moved
              final List<Unit> alreadyMovedUnits = new ArrayList<>();
              for (final ProTerritory t2 : moveMap.values()) {
                alreadyMovedUnits.addAll(t2.getUnits());
                alreadyMovedUnits.addAll(t2.getTempUnits());
              }
  
              // Find units that haven't moved and can be transported
              boolean addedAmphibUnits = false;
              for (final ProTransport proTransportData : transportMapList) {
                if (proTransportData.getTransport().equals(transport)) {
  
                  // Find units to transport
                  final Set<Territory> territoriesCanLoadFrom =
                      proTransportData.getTransportMap().get(t);
                  final List<Unit> amphibUnitsToAdd =
                      ProTransportUtils.getUnitsToTransportFromTerritories(
                          player, transport, territoriesCanLoadFrom, alreadyMovedUnits);
                  if (amphibUnitsToAdd.isEmpty()) {
                    continue;
                  }
  
                  // Find safest territory to unload from
                  double minStrengthDifference = Double.POSITIVE_INFINITY;
                  Territory minTerritory = null;
                  final Set<Territory> territoriesToMoveTransport =
                      data.getMap()
                          .getNeighbors(t, ProMatches.territoryCanMoveSeaUnits(player, data, false));
                  final Set<Territory> loadFromTerritories = new HashSet<>();
                  for (final Unit u : amphibUnitsToAdd) {
                    loadFromTerritories.add(unitTerritoryMap.get(u));
                  }
                  for (final Territory territoryToMoveTransport : territoriesToMoveTransport) {
                    if (proTransportData.getSeaTransportMap().containsKey(territoryToMoveTransport)
                        && proTransportData
                            .getSeaTransportMap()
                            .get(territoryToMoveTransport)
                            .containsAll(loadFromTerritories)
                        && moveMap.get(territoryToMoveTransport) != null
                        && (moveMap.get(territoryToMoveTransport).isCanHold() || hasFactory)) {
                      final List<Unit> attackers =
                          moveMap.get(territoryToMoveTransport).getMaxEnemyUnits();
                      final List<Unit> defenders =
                          moveMap.get(territoryToMoveTransport).getAllDefenders();
                      defenders.add(transport);
                      final double strengthDifference =
                          ProBattleUtils.estimateStrengthDifference(
                              territoryToMoveTransport, attackers, defenders);
                      if (strengthDifference < minStrengthDifference) {
                        minTerritory = territoryToMoveTransport;
                        minStrengthDifference = strengthDifference;
                      }
                    }
                  }
                  if (minTerritory != null) {
  
                    // Add amphib defense
                    moveMap.get(t).getTransportTerritoryMap().put(transport, minTerritory);
                    moveMap.get(t).addTempUnits(amphibUnitsToAdd);
                    moveMap.get(t).putTempAmphibAttackMap(transport, amphibUnitsToAdd);
                    moveMap.get(t).setBattleResult(null);
                    for (final Unit unit : amphibUnitsToAdd) {
                      sortedUnitMoveOptions.remove(unit);
                    }
                    ProLogger.trace(
                        "Adding amphibious defense to: "
                            + t
                            + ", units="
                            + amphibUnitsToAdd
                            + ", unloadTerritory="
                            + minTerritory);
                    addedAmphibUnits = true;
                    break;
                  }
                }
              }
              if (addedAmphibUnits) {
                break;
              }
            }
          }
        }
  
        // Determine if all defenses are successful
        boolean areSuccessful = true;
        boolean containsCapital = false;
        final Set<Territory> territoriesToCheck = new HashSet<>();
        for (final ProTerritory patd : territoriesToTryToDefend) {
          final Territory t = patd.getTerritory();
          territoriesToCheck.add(t);
          final List<Unit> nonAirDefenders =
              CollectionUtils.getMatches(moveMap.get(t).getTempUnits(), Matches.unitIsNotAir());
          for (final Unit u : nonAirDefenders) {
            territoriesToCheck.add(unitTerritoryMap.get(u));
          }
        }
        final Map<Territory, Double> territoryValueMap =
            ProTerritoryValueUtils.findTerritoryValues(
                player,
                territoryManager.getCantHoldTerritories(),
                new ArrayList<>(),
                territoriesToCheck);
        ProLogger.debug("Current number of territories: " + numToDefend);
        for (final ProTerritory patd : territoriesToTryToDefend) {
          final Territory t = patd.getTerritory();
  
          // Find defense result and hold value based on used defenders TUV
          final List<Unit> defendingUnits = moveMap.get(t).getAllDefenders();
          moveMap
              .get(t)
              .setBattleResult(
                  calc.calculateBattleResults(
                      t,
                      moveMap.get(t).getMaxEnemyUnits(),
                      defendingUnits,
                      moveMap.get(t).getMaxEnemyBombardUnits()));
          final ProBattleResult result = patd.getBattleResult();
          int isFactory = 0;
          if (ProMatches.territoryHasInfraFactoryAndIsLand().test(t)) {
            isFactory = 1;
          }
          int isMyCapital = 0;
          if (t.equals(ProData.myCapital)) {
            isMyCapital = 1;
            containsCapital = true;
          }
          final double extraUnitValue =
              TuvUtils.getTuv(moveMap.get(t).getTempUnits(), ProData.unitValueMap);
          final List<Unit> unsafeTransports = new ArrayList<>();
          for (final Unit transport : moveMap.get(t).getTransportTerritoryMap().keySet()) {
            final Territory transportTerritory =
                moveMap.get(t).getTransportTerritoryMap().get(transport);
            if (!moveMap.get(transportTerritory).isCanHold()) {
              unsafeTransports.add(transport);
            }
          }
          final int unsafeTransportValue = TuvUtils.getTuv(unsafeTransports, ProData.unitValueMap);
          final double holdValue =
              extraUnitValue / 8 * (1 + 0.5 * isFactory) * (1 + 2.0 * isMyCapital)
                  - unsafeTransportValue;
  
          // Find strategic value
          boolean hasHigherStrategicValue = true;
          if (!t.isWater()
              && !t.equals(ProData.myCapital)
              && !ProMatches.territoryHasInfraFactoryAndIsLand().test(t)) {
            double totalValue = 0.0;
            final List<Unit> nonAirDefenders =
                CollectionUtils.getMatches(moveMap.get(t).getTempUnits(), Matches.unitIsNotAir());
            for (final Unit u : nonAirDefenders) {
              totalValue += territoryValueMap.get(unitTerritoryMap.get(u));
            }
            final double averageValue = totalValue / nonAirDefenders.size();
            if (territoryValueMap.get(t) < averageValue) {
              hasHigherStrategicValue = false;
              ProLogger.trace(
                  t
                      + " has lower value then move from with value="
                      + territoryValueMap.get(t)
                      + ", averageMoveFromValue="
                      + averageValue);
            }
          }
  
          // Check if its worth defending
          if ((result.getTuvSwing() - holdValue) > patd.getMinBattleResult().getTuvSwing()
              || (!hasHigherStrategicValue
                  && (result.getTuvSwing() + extraUnitValue / 2)
                      >= patd.getMinBattleResult().getTuvSwing())) {
            areSuccessful = false;
          }
          ProLogger.debug(
              patd.getResultString()
                  + ", holdValue="
                  + holdValue
                  + ", minTUVSwing="
                  + patd.getMinBattleResult().getTuvSwing()
                  + ", hasHighStrategicValue="
                  + hasHigherStrategicValue
                  + ", defenders="
                  + defendingUnits
                  + ", attackers="
                  + moveMap.get(t).getMaxEnemyUnits());
        }
  
        final Territory currentTerritory = prioritizedTerritories.get(numToDefend - 1).getTerritory();
        if (ProData.myCapital != null) {
  
          // Check capital defense
          if (containsCapital
              && !currentTerritory.equals(ProData.myCapital)
              && moveMap.get(ProData.myCapital).getBattleResult().getWinPercentage()
                  > (100 - ProData.winPercentage)) {
            if (!Collections.disjoint(
                moveMap.get(currentTerritory).getAllDefenders(),
                moveMap.get(ProData.myCapital).getMaxDefenders())) {
              areSuccessful = false;
              ProLogger.debug(
                  "Capital isn't safe after defense moves with winPercentage="
                      + moveMap.get(ProData.myCapital).getBattleResult().getWinPercentage());
            }
          }
  
          // Check capital local superiority
          if (!currentTerritory.isWater() && enemyDistance >= 2 && enemyDistance <= 3) {
            final int distance =
                data.getMap()
                    .getDistance(
                        ProData.myCapital,
                        currentTerritory,
                        ProMatches.territoryCanMoveLandUnits(player, data, true));
            if (distance > 0
                && (enemyDistance == distance || enemyDistance == (distance - 1))
                && !ProBattleUtils.territoryHasLocalLandSuperiorityAfterMoves(
                    ProData.myCapital, enemyDistance, player, moveMap)) {
              areSuccessful = false;
              ProLogger.debug(
                  "Capital doesn't have local land superiority after defense "
                      + "moves with enemyDistance="
                      + enemyDistance);
            }
          }
        }
  
        // Determine whether to try more territories, remove a territory, or end
        if (areSuccessful) {
          numToDefend++;
          for (final ProTerritory patd : territoriesToTryToDefend) {
            patd.setCanAttack(true);
          }
  
          // Can defend all territories in list so end
          if (numToDefend > prioritizedTerritories.size()) {
            break;
          }
        } else {
  
          // Remove territory last territory in prioritized list since we can't hold them all
          ProLogger.debug("Removing territory: " + currentTerritory);
          prioritizedTerritories.get(numToDefend - 1).setCanHold(false);
          prioritizedTerritories.remove(numToDefend - 1);
          if (numToDefend > prioritizedTerritories.size()) {
            numToDefend--;
          }
        }
      }
  
      // Add temp units to move lists
      for (final ProTerritory t : moveMap.values()) {
  
        // Handle allied units such as fighters on carriers
        final List<Unit> alliedUnits =
            CollectionUtils.getMatches(t.getTempUnits(), Matches.unitIsOwnedBy(player).negate());
        for (final Unit alliedUnit : alliedUnits) {
          t.addCantMoveUnit(alliedUnit);
          t.getTempUnits().remove(alliedUnit);
        }
        t.addUnits(t.getTempUnits());
        t.putAllAmphibAttackMap(t.getTempAmphibAttackMap());
        for (final Unit u : t.getTempUnits()) {
          if (Matches.unitIsTransport().test(u)) {
            transportMoveMap.remove(u);
            transportMapList.removeIf(proTransport -> proTransport.getTransport().equals(u));
          } else {
            unitMoveMap.remove(u);
          }
        }
        for (final Unit u : t.getTempAmphibAttackMap().keySet()) {
          transportMoveMap.remove(u);
          transportMapList.removeIf(proTransport -> proTransport.getTransport().equals(u));
        }
        t.getTempUnits().clear();
        t.getTempAmphibAttackMap().clear();
      }
      ProLogger.debug("Final number of territories: " + (numToDefend - 1));
    }
  
    private void moveUnitsToBestTerritories() {
  
      final Map<Territory, ProTerritory> moveMap =
          territoryManager.getDefendOptions().getTerritoryMap();
      final Map<Unit, Set<Territory>> unitMoveMap =
          territoryManager.getDefendOptions().getUnitMoveMap();
      final Map<Unit, Set<Territory>> transportMoveMap =
          territoryManager.getDefendOptions().getTransportMoveMap();
      final List<ProTransport> transportMapList =
          territoryManager.getDefendOptions().getTransportList();
  
      while (true) {
        ProLogger.info("Move units to best value territories");
        final Set<Territory> territoriesToDefend = new HashSet<>();
        final Map<Unit, Set<Territory>> currentUnitMoveMap = new HashMap<>(unitMoveMap);
        final Map<Unit, Set<Territory>> currentTransportMoveMap = new HashMap<>(transportMoveMap);
        final List<ProTransport> currentTransportMapList = new ArrayList<>(transportMapList);
  
        // Reset lists
        for (final ProTerritory t : moveMap.values()) {
          t.getTempUnits().clear();
          for (final Unit transport : t.getTempAmphibAttackMap().keySet()) {
            t.getTransportTerritoryMap().remove(transport);
          }
          t.getTempAmphibAttackMap().clear();
          t.setBattleResult(null);
        }
  
        ProLogger.debug("Move amphib units");
  
        // Transport amphib units to best territory
        for (final Iterator<ProTransport> it = currentTransportMapList.iterator(); it.hasNext(); ) {
          final ProTransport amphibData = it.next();
          final Unit transport = amphibData.getTransport();
  
          // Get all units that have already moved
          final List<Unit> alreadyMovedUnits = new ArrayList<>();
          for (final ProTerritory t : moveMap.values()) {
            alreadyMovedUnits.addAll(t.getUnits());
            alreadyMovedUnits.addAll(t.getTempUnits());
          }
  
          // Transport amphib units to best land territory
          Territory maxValueTerritory = null;
          List<Unit> maxAmphibUnitsToAdd = null;
          double maxValue = Double.MIN_VALUE;
          double maxSeaValue = 0;
          Territory maxUnloadFromTerritory = null;
          for (final Territory t : amphibData.getTransportMap().keySet()) {
            if (moveMap.get(t).getValue() >= maxValue) {
  
              // Find units to load
              final Set<Territory> territoriesCanLoadFrom = amphibData.getTransportMap().get(t);
              final List<Unit> amphibUnitsToAdd =
                  ProTransportUtils.getUnitsToTransportThatCantMoveToHigherValue(
                      player,
                      transport,
                      territoriesCanLoadFrom,
                      alreadyMovedUnits,
                      moveMap,
                      currentUnitMoveMap,
                      moveMap.get(t).getValue());
              if (amphibUnitsToAdd.isEmpty()) {
                continue;
              }
  
              // Find best territory to move transport
              final Set<Territory> loadFromTerritories = new HashSet<>();
              for (final Unit u : amphibUnitsToAdd) {
                loadFromTerritories.add(unitTerritoryMap.get(u));
              }
              final Set<Territory> territoriesToMoveTransport =
                  data.getMap()
                      .getNeighbors(t, ProMatches.territoryCanMoveSeaUnits(player, data, false));
              for (final Territory territoryToMoveTransport : territoriesToMoveTransport) {
                if (amphibData.getSeaTransportMap().containsKey(territoryToMoveTransport)
                    && amphibData
                        .getSeaTransportMap()
                        .get(territoryToMoveTransport)
                        .containsAll(loadFromTerritories)
                    && moveMap.get(territoryToMoveTransport) != null
                    && moveMap.get(territoryToMoveTransport).isCanHold()
                    && (moveMap.get(t).getValue() > maxValue
                        || moveMap.get(territoryToMoveTransport).getValue() > maxSeaValue)) {
                  maxValueTerritory = t;
                  maxAmphibUnitsToAdd = amphibUnitsToAdd;
                  maxValue = moveMap.get(t).getValue();
                  maxSeaValue = moveMap.get(territoryToMoveTransport).getValue();
                  maxUnloadFromTerritory = territoryToMoveTransport;
                }
              }
            }
          }
  