Search in sources :

Example 1 with ProTransport

use of games.strategy.triplea.ai.pro.data.ProTransport in project triplea by triplea-game.

the class ProNonCombatMoveAi method findUnitsThatCantMove.

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.getUnits().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.territoryHasNonMobileInfraFactoryAndIsNotConqueredOwnedLand(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());
        }
    }
}
Also used : ProPlaceTerritory(games.strategy.triplea.ai.pro.data.ProPlaceTerritory) ProPurchaseTerritory(games.strategy.triplea.ai.pro.data.ProPurchaseTerritory) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) Territory(games.strategy.engine.data.Territory) Set(java.util.Set) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) ProTransport(games.strategy.triplea.ai.pro.data.ProTransport) ProPlaceTerritory(games.strategy.triplea.ai.pro.data.ProPlaceTerritory) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) ProPurchaseTerritory(games.strategy.triplea.ai.pro.data.ProPurchaseTerritory) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit)

Example 2 with ProTransport

use of games.strategy.triplea.ai.pro.data.ProTransport in project triplea by triplea-game.

the class ProCombatMoveAi method tryToAttackTerritories.

private Map<Unit, Set<Territory>> tryToAttackTerritories(final List<ProTerritory> prioritizedTerritories, final List<Unit> alreadyMovedUnits) {
    final Map<Territory, ProTerritory> attackMap = territoryManager.getAttackOptions().getTerritoryMap();
    final ProOtherMoveOptions enemyAttackOptions = territoryManager.getEnemyAttackOptions();
    final Map<Unit, Set<Territory>> unitAttackMap = territoryManager.getAttackOptions().getUnitMoveMap();
    final Map<Unit, Set<Territory>> transportAttackMap = territoryManager.getAttackOptions().getTransportMoveMap();
    final Map<Unit, Set<Territory>> bombardMap = territoryManager.getAttackOptions().getBombardMap();
    final List<ProTransport> transportMapList = territoryManager.getAttackOptions().getTransportList();
    // Reset lists
    for (final ProTerritory t : attackMap.values()) {
        t.getUnits().clear();
        t.getBombardTerritoryMap().clear();
        t.getAmphibAttackMap().clear();
        t.getTransportTerritoryMap().clear();
        t.setBattleResult(null);
    }
    // Loop through all units and determine attack options
    final Map<Unit, Set<Territory>> unitAttackOptions = new HashMap<>();
    for (final Unit unit : unitAttackMap.keySet()) {
        // Find number of attack options
        final Set<Territory> canAttackTerritories = new HashSet<>();
        for (final ProTerritory attackTerritoryData : prioritizedTerritories) {
            if (unitAttackMap.get(unit).contains(attackTerritoryData.getTerritory())) {
                canAttackTerritories.add(attackTerritoryData.getTerritory());
            }
        }
        // Add units with attack options to map
        if (canAttackTerritories.size() >= 1) {
            unitAttackOptions.put(unit, canAttackTerritories);
        }
    }
    // Sort units by number of attack options and cost
    Map<Unit, Set<Territory>> sortedUnitAttackOptions = ProSortMoveOptionsUtils.sortUnitMoveOptions(unitAttackOptions);
    // Try to set at least one destroyer in each sea territory with subs
    for (final Iterator<Unit> it = sortedUnitAttackOptions.keySet().iterator(); it.hasNext(); ) {
        final Unit unit = it.next();
        final boolean isDestroyerUnit = UnitAttachment.get(unit.getType()).getIsDestroyer();
        if (!isDestroyerUnit) {
            // skip non-destroyer units
            continue;
        }
        for (final Territory t : sortedUnitAttackOptions.get(unit)) {
            // Add destroyer if territory has subs and a destroyer has been already added
            final List<Unit> defendingUnits = attackMap.get(t).getMaxEnemyDefenders(player, data);
            if (defendingUnits.stream().anyMatch(Matches.unitIsSub()) && attackMap.get(t).getUnits().stream().noneMatch(Matches.unitIsDestroyer())) {
                attackMap.get(t).addUnit(unit);
                it.remove();
                break;
            }
        }
    }
    // Set enough land and sea units in territories to have at least a chance of winning
    for (final Iterator<Unit> it = sortedUnitAttackOptions.keySet().iterator(); it.hasNext(); ) {
        final Unit unit = it.next();
        final boolean isAirUnit = UnitAttachment.get(unit.getType()).getIsAir();
        if (isAirUnit) {
            // skip air units
            continue;
        }
        final TreeMap<Double, Territory> estimatesMap = new TreeMap<>();
        for (final Territory t : sortedUnitAttackOptions.get(unit)) {
            if (t.isWater() && !attackMap.get(t).isCanHold()) {
                // ignore sea territories that can't be held
                continue;
            }
            final List<Unit> defendingUnits = attackMap.get(t).getMaxEnemyDefenders(player, data);
            double estimate = ProBattleUtils.estimateStrengthDifference(t, attackMap.get(t).getUnits(), defendingUnits);
            final boolean hasAa = defendingUnits.stream().anyMatch(Matches.unitIsAaForAnything());
            if (hasAa) {
                estimate -= 10;
            }
            estimatesMap.put(estimate, t);
        }
        if (!estimatesMap.isEmpty() && estimatesMap.firstKey() < 40) {
            final Territory minWinTerritory = estimatesMap.entrySet().iterator().next().getValue();
            attackMap.get(minWinTerritory).addUnit(unit);
            it.remove();
        }
    }
    // Re-sort attack options
    sortedUnitAttackOptions = ProSortMoveOptionsUtils.sortUnitNeededOptionsThenAttack(player, sortedUnitAttackOptions, attackMap, ProData.unitTerritoryMap, calc);
    // Set non-air units in territories that can be held
    for (final Iterator<Unit> it = sortedUnitAttackOptions.keySet().iterator(); it.hasNext(); ) {
        final Unit unit = it.next();
        final boolean isAirUnit = UnitAttachment.get(unit.getType()).getIsAir();
        if (isAirUnit) {
            // skip air units
            continue;
        }
        Territory minWinTerritory = null;
        double minWinPercentage = ProData.winPercentage;
        for (final Territory t : sortedUnitAttackOptions.get(unit)) {
            final ProTerritory patd = attackMap.get(t);
            if (!attackMap.get(t).isCurrentlyWins() && attackMap.get(t).isCanHold()) {
                if (attackMap.get(t).getBattleResult() == null) {
                    attackMap.get(t).setBattleResult(calc.estimateAttackBattleResults(t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet()));
                }
                final ProBattleResult result = attackMap.get(t).getBattleResult();
                if (result.getWinPercentage() < minWinPercentage || (!result.isHasLandUnitRemaining() && minWinTerritory == null)) {
                    minWinPercentage = result.getWinPercentage();
                    minWinTerritory = t;
                }
            }
        }
        if (minWinTerritory != null) {
            attackMap.get(minWinTerritory).addUnit(unit);
            attackMap.get(minWinTerritory).setBattleResult(null);
            it.remove();
        }
    }
    // Re-sort attack options
    sortedUnitAttackOptions = ProSortMoveOptionsUtils.sortUnitNeededOptionsThenAttack(player, sortedUnitAttackOptions, attackMap, ProData.unitTerritoryMap, calc);
    // Set air units in territories that can't be held (don't move planes to empty territories)
    for (final Iterator<Unit> it = sortedUnitAttackOptions.keySet().iterator(); it.hasNext(); ) {
        final Unit unit = it.next();
        final boolean isAirUnit = UnitAttachment.get(unit.getType()).getIsAir();
        if (!isAirUnit) {
            // skip non-air units
            continue;
        }
        Territory minWinTerritory = null;
        double minWinPercentage = ProData.winPercentage;
        for (final Territory t : sortedUnitAttackOptions.get(unit)) {
            final ProTerritory patd = attackMap.get(t);
            if (!patd.isCurrentlyWins() && !patd.isCanHold()) {
                // Check if air unit should avoid this territory due to no guaranteed safe landing location
                final boolean isEnemyCapital = ProUtils.getLiveEnemyCapitals(data, player).contains(t);
                final boolean isAdjacentToAlliedCapital = Matches.territoryHasNeighborMatching(data, Matches.territoryIsInList(ProUtils.getLiveAlliedCapitals(data, player))).test(t);
                final int range = TripleAUnit.get(unit).getMovementLeft();
                final int distance = data.getMap().getDistance_IgnoreEndForCondition(ProData.unitTerritoryMap.get(unit), t, ProMatches.territoryCanMoveAirUnitsAndNoAa(player, data, true));
                final boolean usesMoreThanHalfOfRange = distance > range / 2;
                if (isAirUnit && !isEnemyCapital && !isAdjacentToAlliedCapital && usesMoreThanHalfOfRange) {
                    continue;
                }
                // Check battle results
                if (patd.getBattleResult() == null) {
                    patd.setBattleResult(calc.estimateAttackBattleResults(t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet()));
                }
                final ProBattleResult result = patd.getBattleResult();
                if (result.getWinPercentage() < minWinPercentage || (!result.isHasLandUnitRemaining() && minWinTerritory == null)) {
                    final List<Unit> defendingUnits = patd.getMaxEnemyDefenders(player, data);
                    final boolean hasNoDefenders = defendingUnits.stream().noneMatch(ProMatches.unitIsEnemyAndNotInfa(player, data));
                    final boolean isOverwhelmingWin = ProBattleUtils.checkForOverwhelmingWin(t, patd.getUnits(), defendingUnits);
                    final boolean hasAa = defendingUnits.stream().anyMatch(Matches.unitIsAaForAnything());
                    if (!hasNoDefenders && !isOverwhelmingWin && (!hasAa || result.getWinPercentage() < minWinPercentage)) {
                        minWinPercentage = result.getWinPercentage();
                        minWinTerritory = t;
                        if (patd.isStrafing()) {
                            break;
                        }
                    }
                }
            }
        }
        if (minWinTerritory != null) {
            attackMap.get(minWinTerritory).addUnit(unit);
            attackMap.get(minWinTerritory).setBattleResult(null);
            it.remove();
        }
    }
    // Re-sort attack options
    sortedUnitAttackOptions = ProSortMoveOptionsUtils.sortUnitNeededOptionsThenAttack(player, sortedUnitAttackOptions, attackMap, ProData.unitTerritoryMap, calc);
    // Set remaining units in any territory that needs it (don't move planes to empty territories)
    for (final Iterator<Unit> it = sortedUnitAttackOptions.keySet().iterator(); it.hasNext(); ) {
        final Unit unit = it.next();
        final boolean isAirUnit = UnitAttachment.get(unit.getType()).getIsAir();
        Territory minWinTerritory = null;
        double minWinPercentage = ProData.winPercentage;
        for (final Territory t : sortedUnitAttackOptions.get(unit)) {
            final ProTerritory patd = attackMap.get(t);
            if (!patd.isCurrentlyWins()) {
                // Check if air unit should avoid this territory due to no guaranteed safe landing location
                final boolean isAdjacentToAlliedFactory = Matches.territoryHasNeighborMatching(data, ProMatches.territoryHasInfraFactoryAndIsAlliedLand(player, data)).test(t);
                final int range = TripleAUnit.get(unit).getMovementLeft();
                final int distance = data.getMap().getDistance_IgnoreEndForCondition(ProData.unitTerritoryMap.get(unit), t, ProMatches.territoryCanMoveAirUnitsAndNoAa(player, data, true));
                final boolean usesMoreThanHalfOfRange = distance > range / 2;
                final boolean territoryValueIsLessThanUnitValue = patd.getValue() < ProData.unitValueMap.getInt(unit.getType());
                if (isAirUnit && !isAdjacentToAlliedFactory && usesMoreThanHalfOfRange && (territoryValueIsLessThanUnitValue || (!t.isWater() && !patd.isCanHold()))) {
                    continue;
                }
                if (patd.getBattleResult() == null) {
                    patd.setBattleResult(calc.estimateAttackBattleResults(t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet()));
                }
                final ProBattleResult result = patd.getBattleResult();
                if (result.getWinPercentage() < minWinPercentage || (!result.isHasLandUnitRemaining() && minWinTerritory == null)) {
                    final List<Unit> defendingUnits = patd.getMaxEnemyDefenders(player, data);
                    final boolean hasNoDefenders = defendingUnits.stream().noneMatch(ProMatches.unitIsEnemyAndNotInfa(player, data));
                    final boolean isOverwhelmingWin = ProBattleUtils.checkForOverwhelmingWin(t, patd.getUnits(), defendingUnits);
                    final boolean hasAa = defendingUnits.stream().anyMatch(Matches.unitIsAaForAnything());
                    if (!isAirUnit || (!hasNoDefenders && !isOverwhelmingWin && (!hasAa || result.getWinPercentage() < minWinPercentage))) {
                        minWinPercentage = result.getWinPercentage();
                        minWinTerritory = t;
                    }
                }
            }
        }
        if (minWinTerritory != null) {
            attackMap.get(minWinTerritory).addUnit(unit);
            attackMap.get(minWinTerritory).setBattleResult(null);
            it.remove();
        }
    }
    // Re-sort attack options
    sortedUnitAttackOptions = ProSortMoveOptionsUtils.sortUnitNeededOptions(player, sortedUnitAttackOptions, attackMap, calc);
    // If transports can take casualties try placing in naval battles first
    final List<Unit> alreadyAttackedWithTransports = new ArrayList<>();
    if (!Properties.getTransportCasualtiesRestricted(data)) {
        // Loop through all my transports and see which territories they can attack from current list
        final Map<Unit, Set<Territory>> transportAttackOptions = new HashMap<>();
        for (final Unit unit : transportAttackMap.keySet()) {
            // Find number of attack options
            final Set<Territory> canAttackTerritories = new HashSet<>();
            for (final ProTerritory attackTerritoryData : prioritizedTerritories) {
                if (transportAttackMap.get(unit).contains(attackTerritoryData.getTerritory())) {
                    canAttackTerritories.add(attackTerritoryData.getTerritory());
                }
            }
            if (!canAttackTerritories.isEmpty()) {
                transportAttackOptions.put(unit, canAttackTerritories);
            }
        }
        // Loop through transports with attack options and determine if any naval battle needs it
        for (final Unit transport : transportAttackOptions.keySet()) {
            // Find current naval battle that needs transport if it isn't transporting units
            for (final Territory t : transportAttackOptions.get(transport)) {
                final ProTerritory patd = attackMap.get(t);
                final List<Unit> defendingUnits = patd.getMaxEnemyDefenders(player, data);
                if (!patd.isCurrentlyWins() && !TransportTracker.isTransporting(transport) && !defendingUnits.isEmpty()) {
                    if (patd.getBattleResult() == null) {
                        patd.setBattleResult(calc.estimateAttackBattleResults(t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet()));
                    }
                    final ProBattleResult result = patd.getBattleResult();
                    if (result.getWinPercentage() < ProData.winPercentage || !result.isHasLandUnitRemaining()) {
                        patd.addUnit(transport);
                        patd.setBattleResult(null);
                        alreadyAttackedWithTransports.add(transport);
                        ProLogger.trace("Adding attack transport to: " + t.getName());
                        break;
                    }
                }
            }
        }
    }
    // Loop through all my transports and see which can make amphib attack
    final Map<Unit, Set<Territory>> amphibAttackOptions = new HashMap<>();
    for (final ProTransport proTransportData : transportMapList) {
        // If already used to attack then ignore
        if (alreadyAttackedWithTransports.contains(proTransportData.getTransport())) {
            continue;
        }
        // Find number of attack options
        final Set<Territory> canAmphibAttackTerritories = new HashSet<>();
        for (final ProTerritory attackTerritoryData : prioritizedTerritories) {
            if (proTransportData.getTransportMap().containsKey(attackTerritoryData.getTerritory())) {
                canAmphibAttackTerritories.add(attackTerritoryData.getTerritory());
            }
        }
        if (!canAmphibAttackTerritories.isEmpty()) {
            amphibAttackOptions.put(proTransportData.getTransport(), canAmphibAttackTerritories);
        }
    }
    // Loop through transports with amphib attack options and determine if any land battle needs it
    for (final Unit transport : amphibAttackOptions.keySet()) {
        // Find current land battle results for territories that unit can amphib attack
        Territory minWinTerritory = null;
        double minWinPercentage = ProData.winPercentage;
        List<Unit> minAmphibUnitsToAdd = null;
        Territory minUnloadFromTerritory = null;
        for (final Territory t : amphibAttackOptions.get(transport)) {
            final ProTerritory patd = attackMap.get(t);
            if (!patd.isCurrentlyWins()) {
                if (patd.getBattleResult() == null) {
                    patd.setBattleResult(calc.estimateAttackBattleResults(t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet()));
                }
                final ProBattleResult result = patd.getBattleResult();
                if (result.getWinPercentage() < minWinPercentage || (!result.isHasLandUnitRemaining() && minWinTerritory == null)) {
                    // Get all units that have already attacked
                    final List<Unit> alreadyAttackedWithUnits = new ArrayList<>(alreadyMovedUnits);
                    alreadyAttackedWithUnits.addAll(attackMap.values().stream().map(ProTerritory::getUnits).flatMap(Collection::stream).collect(Collectors.toList()));
                    // Find units that haven't attacked and can be transported
                    for (final ProTransport proTransportData : transportMapList) {
                        if (proTransportData.getTransport().equals(transport)) {
                            // Find units to load
                            final Set<Territory> territoriesCanLoadFrom = proTransportData.getTransportMap().get(t);
                            final List<Unit> amphibUnitsToAdd = ProTransportUtils.getUnitsToTransportFromTerritories(player, transport, territoriesCanLoadFrom, alreadyAttackedWithUnits);
                            if (amphibUnitsToAdd.isEmpty()) {
                                continue;
                            }
                            // Find best territory to move transport
                            double minStrengthDifference = Double.POSITIVE_INFINITY;
                            minUnloadFromTerritory = 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(ProData.unitTerritoryMap.get(u));
                            }
                            for (final Territory territoryToMoveTransport : territoriesToMoveTransport) {
                                if (proTransportData.getSeaTransportMap().containsKey(territoryToMoveTransport) && proTransportData.getSeaTransportMap().get(territoryToMoveTransport).containsAll(loadFromTerritories)) {
                                    List<Unit> attackers = new ArrayList<>();
                                    if (enemyAttackOptions.getMax(territoryToMoveTransport) != null) {
                                        attackers = enemyAttackOptions.getMax(territoryToMoveTransport).getMaxUnits();
                                    }
                                    final List<Unit> defenders = territoryToMoveTransport.getUnits().getMatches(Matches.isUnitAllied(player, data));
                                    defenders.add(transport);
                                    final double strengthDifference = ProBattleUtils.estimateStrengthDifference(territoryToMoveTransport, attackers, defenders);
                                    if (strengthDifference < minStrengthDifference) {
                                        minStrengthDifference = strengthDifference;
                                        minUnloadFromTerritory = territoryToMoveTransport;
                                    }
                                }
                            }
                            minWinTerritory = t;
                            minWinPercentage = result.getWinPercentage();
                            minAmphibUnitsToAdd = amphibUnitsToAdd;
                            break;
                        }
                    }
                }
            }
        }
        if (minWinTerritory != null) {
            if (minUnloadFromTerritory != null) {
                attackMap.get(minWinTerritory).getTransportTerritoryMap().put(transport, minUnloadFromTerritory);
            }
            attackMap.get(minWinTerritory).addUnits(minAmphibUnitsToAdd);
            attackMap.get(minWinTerritory).putAmphibAttackMap(transport, minAmphibUnitsToAdd);
            attackMap.get(minWinTerritory).setBattleResult(null);
            for (final Unit unit : minAmphibUnitsToAdd) {
                sortedUnitAttackOptions.remove(unit);
            }
            ProLogger.trace("Adding amphibious attack to " + minWinTerritory + ", units=" + minAmphibUnitsToAdd.size() + ", unloadFrom=" + minUnloadFromTerritory);
        }
    }
    // Get all units that have already moved
    final Set<Unit> alreadyAttackedWithUnits = new HashSet<>();
    for (final ProTerritory t : attackMap.values()) {
        alreadyAttackedWithUnits.addAll(t.getUnits());
        alreadyAttackedWithUnits.addAll(t.getAmphibAttackMap().keySet());
    }
    // Loop through all my bombard units and see which can bombard
    final Map<Unit, Set<Territory>> bombardOptions = new HashMap<>();
    for (final Unit u : bombardMap.keySet()) {
        // If already used to attack then ignore
        if (alreadyAttackedWithUnits.contains(u)) {
            continue;
        }
        // Find number of bombard options
        final Set<Territory> canBombardTerritories = new HashSet<>();
        for (final ProTerritory patd : prioritizedTerritories) {
            final List<Unit> defendingUnits = patd.getMaxEnemyDefenders(player, data);
            final boolean hasDefenders = defendingUnits.stream().anyMatch(Matches.unitIsInfrastructure().negate());
            if (bombardMap.get(u).contains(patd.getTerritory()) && !patd.getTransportTerritoryMap().isEmpty() && hasDefenders && !TransportTracker.isTransporting(u)) {
                canBombardTerritories.add(patd.getTerritory());
            }
        }
        if (!canBombardTerritories.isEmpty()) {
            bombardOptions.put(u, canBombardTerritories);
        }
    }
    // Loop through bombard units to see if any amphib battles need
    for (final Unit u : bombardOptions.keySet()) {
        // Find current land battle results for territories that unit can bombard
        Territory minWinTerritory = null;
        double minWinPercentage = Double.MAX_VALUE;
        Territory minBombardFromTerritory = null;
        for (final Territory t : bombardOptions.get(u)) {
            final ProTerritory patd = attackMap.get(t);
            if (patd.getBattleResult() == null) {
                patd.setBattleResult(calc.estimateAttackBattleResults(t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet()));
            }
            final ProBattleResult result = patd.getBattleResult();
            if (result.getWinPercentage() < minWinPercentage || (!result.isHasLandUnitRemaining() && minWinTerritory == null)) {
                // Find territory to bombard from
                Territory bombardFromTerritory = null;
                for (final Territory unloadFromTerritory : patd.getTransportTerritoryMap().values()) {
                    if (patd.getBombardOptionsMap().get(u).contains(unloadFromTerritory)) {
                        bombardFromTerritory = unloadFromTerritory;
                    }
                }
                if (bombardFromTerritory != null) {
                    minWinTerritory = t;
                    minWinPercentage = result.getWinPercentage();
                    minBombardFromTerritory = bombardFromTerritory;
                }
            }
        }
        if (minWinTerritory != null) {
            attackMap.get(minWinTerritory).getBombardTerritoryMap().put(u, minBombardFromTerritory);
            attackMap.get(minWinTerritory).setBattleResult(null);
            sortedUnitAttackOptions.remove(u);
            ProLogger.trace("Adding bombard to " + minWinTerritory + ", units=" + u + ", bombardFrom=" + minBombardFromTerritory);
        }
    }
    return sortedUnitAttackOptions;
}
Also used : Territory(games.strategy.engine.data.Territory) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) HashSet(java.util.HashSet) Set(java.util.Set) HashMap(java.util.HashMap) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) ArrayList(java.util.ArrayList) ProBattleResult(games.strategy.triplea.ai.pro.data.ProBattleResult) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) TreeMap(java.util.TreeMap) ProTransport(games.strategy.triplea.ai.pro.data.ProTransport) Collection(java.util.Collection) ProOtherMoveOptions(games.strategy.triplea.ai.pro.data.ProOtherMoveOptions) HashSet(java.util.HashSet)

Example 3 with ProTransport

use of games.strategy.triplea.ai.pro.data.ProTransport in project triplea by triplea-game.

the class ProNonCombatMoveAi method moveUnitsToDefendTerritories.

private void moveUnitsToDefendTerritories(final List<ProTerritory> prioritizedTerritories, final int enemyDistance, final Map<Territory, Double> territoryValueMap) {
    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);
        // Set enough units in territories to have at least a chance of winning
        for (final Iterator<Unit> it = sortedUnitMoveOptions.keySet().iterator(); it.hasNext(); ) {
            final Unit unit = it.next();
            final boolean isAirUnit = UnitAttachment.get(unit.getType()).getIsAir();
            if (isAirUnit || Matches.unitIsCarrier().test(unit)) {
                // skip air and carrier units
                continue;
            }
            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();
                moveMap.get(minWinTerritory).addTempUnit(unit);
                it.remove();
            }
        }
        // Set non-air units in territories
        for (final Iterator<Unit> it = sortedUnitMoveOptions.keySet().iterator(); it.hasNext(); ) {
            final Unit unit = it.next();
            if (Matches.unitCanLandOnCarrier().test(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).addTempUnit(unit);
                moveMap.get(maxWinTerritory).setBattleResult(null);
                it.remove();
                // 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().getUnits(), unitTerritory, data, player);
                    if (carrierMustMoveWith.containsKey(unit)) {
                        moveMap.get(maxWinTerritory).getTempUnits().addAll(carrierMustMoveWith.get(unit));
                    }
                }
            }
        }
        // Set air units in territories
        for (final Iterator<Unit> it = sortedUnitMoveOptions.keySet().iterator(); it.hasNext(); ) {
            final Unit unit = it.next();
            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)) {
                        // skip moving air to water if not enough carrier capacity
                        continue;
                    }
                }
                if (!t.isWater() && !t.getOwner().equals(player) && Matches.unitIsAir().test(unit) && !ProMatches.territoryHasInfraFactoryAndIsLand().test(t)) {
                    // skip moving air units to allied land without a factory
                    continue;
                }
                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);
                it.remove();
            }
        }
        // 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;
        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 * 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));
}
Also used : LinkedHashSet(java.util.LinkedHashSet) Set(java.util.Set) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) HashMap(java.util.HashMap) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) ArrayList(java.util.ArrayList) ProBattleResult(games.strategy.triplea.ai.pro.data.ProBattleResult) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) ProPlaceTerritory(games.strategy.triplea.ai.pro.data.ProPlaceTerritory) ProPurchaseTerritory(games.strategy.triplea.ai.pro.data.ProPurchaseTerritory) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) Territory(games.strategy.engine.data.Territory) TreeMap(java.util.TreeMap) ProTransport(games.strategy.triplea.ai.pro.data.ProTransport) Collection(java.util.Collection)

Example 4 with ProTransport

use of games.strategy.triplea.ai.pro.data.ProTransport in project triplea by triplea-game.

the class ProNonCombatMoveAi method moveUnitsToBestTerritories.

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;
                        }
                    }
                }
            }
            if (maxValueTerritory != null) {
                ProLogger.trace(transport + " moved to " + maxUnloadFromTerritory + " and unloading to best land at " + maxValueTerritory + " with " + maxAmphibUnitsToAdd + ", value=" + maxValue);
                moveMap.get(maxValueTerritory).addTempUnits(maxAmphibUnitsToAdd);
                moveMap.get(maxValueTerritory).putTempAmphibAttackMap(transport, maxAmphibUnitsToAdd);
                moveMap.get(maxValueTerritory).getTransportTerritoryMap().put(transport, maxUnloadFromTerritory);
                currentTransportMoveMap.remove(transport);
                for (final Unit unit : maxAmphibUnitsToAdd) {
                    currentUnitMoveMap.remove(unit);
                }
                territoriesToDefend.add(maxUnloadFromTerritory);
                it.remove();
                continue;
            }
            // Transport amphib units to best sea territory
            for (final Territory t : amphibData.getSeaTransportMap().keySet()) {
                if (moveMap.get(t) != null && moveMap.get(t).getValue() > maxValue) {
                    // Find units to load
                    final Set<Territory> territoriesCanLoadFrom = amphibData.getSeaTransportMap().get(t);
                    // Don't transport adjacent units
                    territoriesCanLoadFrom.removeAll(data.getMap().getNeighbors(t));
                    final List<Unit> amphibUnitsToAdd = ProTransportUtils.getUnitsToTransportThatCantMoveToHigherValue(player, transport, territoriesCanLoadFrom, alreadyMovedUnits, moveMap, currentUnitMoveMap, 0.1);
                    if (!amphibUnitsToAdd.isEmpty()) {
                        maxValueTerritory = t;
                        maxAmphibUnitsToAdd = amphibUnitsToAdd;
                        maxValue = moveMap.get(t).getValue();
                    }
                }
            }
            if (maxValueTerritory != null) {
                final Set<Territory> possibleUnloadTerritories = data.getMap().getNeighbors(maxValueTerritory, ProMatches.territoryCanMoveLandUnitsAndIsAllied(player, data));
                Territory unloadToTerritory = null;
                int maxNumSeaNeighbors = 0;
                for (final Territory t : possibleUnloadTerritories) {
                    final int numSeaNeighbors = data.getMap().getNeighbors(t, Matches.territoryIsWater()).size();
                    final boolean isAdjacentToEnemy = ProMatches.territoryIsOrAdjacentToEnemyNotNeutralLand(player, data).test(t);
                    if (moveMap.get(t) != null && (moveMap.get(t).isCanHold() || !isAdjacentToEnemy) && numSeaNeighbors > maxNumSeaNeighbors) {
                        unloadToTerritory = t;
                        maxNumSeaNeighbors = numSeaNeighbors;
                    }
                }
                if (unloadToTerritory != null) {
                    moveMap.get(unloadToTerritory).addTempUnits(maxAmphibUnitsToAdd);
                    moveMap.get(unloadToTerritory).putTempAmphibAttackMap(transport, maxAmphibUnitsToAdd);
                    moveMap.get(unloadToTerritory).getTransportTerritoryMap().put(transport, maxValueTerritory);
                    ProLogger.trace(transport + " moved to best sea at " + maxValueTerritory + " and unloading to " + unloadToTerritory + " with " + maxAmphibUnitsToAdd + ", value=" + maxValue);
                } else {
                    moveMap.get(maxValueTerritory).addTempUnits(maxAmphibUnitsToAdd);
                    moveMap.get(maxValueTerritory).putTempAmphibAttackMap(transport, maxAmphibUnitsToAdd);
                    moveMap.get(maxValueTerritory).getTransportTerritoryMap().put(transport, maxValueTerritory);
                    ProLogger.trace(transport + " moved to best sea at " + maxValueTerritory + " with " + maxAmphibUnitsToAdd + ", value=" + maxValue);
                }
                currentTransportMoveMap.remove(transport);
                for (final Unit unit : maxAmphibUnitsToAdd) {
                    currentUnitMoveMap.remove(unit);
                }
                territoriesToDefend.add(maxValueTerritory);
                it.remove();
            }
        }
        ProLogger.debug("Move empty transports to best loading territory");
        // TODO: consider which territory is 'safest'
        for (final Iterator<Unit> it = currentTransportMoveMap.keySet().iterator(); it.hasNext(); ) {
            final Unit transport = it.next();
            final Territory currentTerritory = unitTerritoryMap.get(transport);
            final int moves = TripleAUnit.get(transport).getMovementLeft();
            if (TransportTracker.isTransporting(transport) || moves <= 0) {
                continue;
            }
            final List<ProTerritory> priorizitedLoadTerritories = new ArrayList<>();
            for (final Territory t : moveMap.keySet()) {
                // Check if land with adjacent sea that can be reached and that I'm not already adjacent to
                final boolean territoryHasTransportableUnits = Matches.territoryHasUnitsThatMatch(ProMatches.unitIsOwnedTransportableUnitAndCanBeLoaded(player, transport, false)).test(t);
                final int distance = data.getMap().getDistance_IgnoreEndForCondition(currentTerritory, t, ProMatches.territoryCanMoveSeaUnits(player, data, true));
                final boolean hasSeaNeighbor = Matches.territoryHasNeighborMatching(data, Matches.territoryIsWater()).test(t);
                final boolean hasFactory = ProMatches.territoryHasInfraFactoryAndIsOwnedLand(player).test(t);
                if (!t.isWater() && hasSeaNeighbor && distance > 0 && !(distance == 1 && territoryHasTransportableUnits && !hasFactory)) {
                    // TODO: add calculation of transports vs units
                    final double territoryValue = moveMap.get(t).getValue();
                    final int numUnitsToLoad = CollectionUtils.getMatches(moveMap.get(t).getAllDefenders(), ProMatches.unitIsOwnedTransportableUnit(player)).size();
                    final boolean hasUnconqueredFactory = ProMatches.territoryHasInfraFactoryAndIsOwnedLand(player).test(t) && !AbstractMoveDelegate.getBattleTracker(data).wasConquered(t);
                    int factoryProduction = 0;
                    if (hasUnconqueredFactory) {
                        factoryProduction = TerritoryAttachment.getProduction(t);
                    }
                    int numTurnsAway = (distance - 1) / moves;
                    if (distance <= moves) {
                        numTurnsAway = 0;
                    }
                    final double value = territoryValue + 0.5 * numTurnsAway - 0.1 * numUnitsToLoad - 0.1 * factoryProduction;
                    moveMap.get(t).setLoadValue(value);
                    priorizitedLoadTerritories.add(moveMap.get(t));
                }
            }
            // Sort prioritized territories
            priorizitedLoadTerritories.sort(Comparator.comparingDouble(ProTerritory::getLoadValue));
            // Move towards best loading territory if route is safe
            for (final ProTerritory patd : priorizitedLoadTerritories) {
                boolean movedTransport = false;
                final Set<Territory> cantHoldTerritories = new HashSet<>();
                while (true) {
                    final Predicate<Territory> match = ProMatches.territoryCanMoveSeaUnitsThrough(player, data, false).and(Matches.territoryIsInList(cantHoldTerritories).negate());
                    final Route route = data.getMap().getRoute_IgnoreEnd(currentTerritory, patd.getTerritory(), match);
                    if (route == null || MoveValidator.validateCanal(route, Collections.singletonList(transport), player, data) != null) {
                        break;
                    }
                    final List<Territory> territories = route.getAllTerritories();
                    territories.remove(territories.size() - 1);
                    final Territory moveToTerritory = territories.get(Math.min(territories.size() - 1, moves));
                    final ProTerritory patd2 = moveMap.get(moveToTerritory);
                    if (patd2 != null && patd2.isCanHold()) {
                        ProLogger.trace(transport + " moved towards best loading territory " + patd.getTerritory() + " and moved to " + moveToTerritory);
                        patd2.addTempUnit(transport);
                        territoriesToDefend.add(moveToTerritory);
                        it.remove();
                        movedTransport = true;
                        break;
                    }
                    if (!cantHoldTerritories.add(moveToTerritory)) {
                        break;
                    }
                }
                if (movedTransport) {
                    break;
                }
            }
        }
        ProLogger.debug("Move remaining transports to safest territory");
        // Move remaining transports to safest territory
        for (final Iterator<Unit> it = currentTransportMoveMap.keySet().iterator(); it.hasNext(); ) {
            final Unit transport = it.next();
            // Get all units that have already moved
            final List<Unit> alreadyMovedUnits = new ArrayList<>();
            for (final ProTerritory t : moveMap.values()) {
                alreadyMovedUnits.addAll(t.getUnits());
            }
            // Find safest territory
            double minStrengthDifference = Double.POSITIVE_INFINITY;
            Territory minTerritory = null;
            for (final Territory t : currentTransportMoveMap.get(transport)) {
                final List<Unit> attackers = moveMap.get(t).getMaxEnemyUnits();
                final List<Unit> defenders = moveMap.get(t).getMaxDefenders();
                defenders.removeAll(alreadyMovedUnits);
                defenders.addAll(moveMap.get(t).getUnits());
                defenders.removeAll(ProTransportUtils.getAirThatCantLandOnCarrier(player, t, defenders));
                final double strengthDifference = ProBattleUtils.estimateStrengthDifference(t, attackers, defenders);
                // TODO: add logic to move towards closest factory
                ProLogger.trace(transport + " at " + t + ", strengthDifference=" + strengthDifference + ", attackers=" + attackers + ", defenders=" + defenders);
                if (strengthDifference < minStrengthDifference) {
                    minStrengthDifference = strengthDifference;
                    minTerritory = t;
                }
            }
            if (minTerritory != null) {
                // TODO: consider which is 'safest'
                if (TransportTracker.isTransporting(transport)) {
                    final List<Unit> amphibUnits = (List<Unit>) TransportTracker.transporting(transport);
                    final Set<Territory> possibleUnloadTerritories = data.getMap().getNeighbors(minTerritory, ProMatches.territoryCanMoveLandUnitsAndIsAllied(player, data));
                    if (!possibleUnloadTerritories.isEmpty()) {
                        // Find best unload territory
                        Territory unloadToTerritory = possibleUnloadTerritories.iterator().next();
                        for (final Territory t : possibleUnloadTerritories) {
                            if (moveMap.get(t) != null && moveMap.get(t).isCanHold()) {
                                unloadToTerritory = t;
                            }
                        }
                        ProLogger.trace(transport + " moved to safest territory at " + minTerritory + " and unloading to " + unloadToTerritory + " with " + amphibUnits + ", strengthDifference=" + minStrengthDifference);
                        moveMap.get(unloadToTerritory).addTempUnits(amphibUnits);
                        moveMap.get(unloadToTerritory).putTempAmphibAttackMap(transport, amphibUnits);
                        moveMap.get(unloadToTerritory).getTransportTerritoryMap().put(transport, minTerritory);
                        for (final Unit unit : amphibUnits) {
                            currentUnitMoveMap.remove(unit);
                        }
                        it.remove();
                    } else {
                        // Move transport with units since no unload options
                        ProLogger.trace(transport + " moved to safest territory at " + minTerritory + " with " + amphibUnits + ", strengthDifference=" + minStrengthDifference);
                        moveMap.get(minTerritory).addTempUnits(amphibUnits);
                        moveMap.get(minTerritory).putTempAmphibAttackMap(transport, amphibUnits);
                        moveMap.get(minTerritory).getTransportTerritoryMap().put(transport, minTerritory);
                        for (final Unit unit : amphibUnits) {
                            currentUnitMoveMap.remove(unit);
                        }
                        it.remove();
                    }
                } else {
                    // If not transporting units
                    ProLogger.trace(transport + " moved to safest territory at " + minTerritory + ", strengthDifference=" + minStrengthDifference);
                    moveMap.get(minTerritory).addTempUnit(transport);
                    it.remove();
                }
            }
        }
        // Get all transport final territories
        ProMoveUtils.calculateAmphibRoutes(player, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), moveMap, false);
        for (final ProTerritory t : moveMap.values()) {
            for (final Map.Entry<Unit, Territory> entry : t.getTransportTerritoryMap().entrySet()) {
                final ProTerritory territory = moveMap.get(entry.getValue());
                if (territory != null) {
                    territory.addTempUnit(entry.getKey());
                }
            }
        }
        ProLogger.debug("Move sea units");
        // Move sea units to defend transports
        for (final Iterator<Unit> it = currentUnitMoveMap.keySet().iterator(); it.hasNext(); ) {
            final Unit u = it.next();
            if (Matches.unitIsSea().test(u)) {
                for (final Territory t : currentUnitMoveMap.get(u)) {
                    if (moveMap.get(t).isCanHold() && !moveMap.get(t).getAllDefenders().isEmpty() && moveMap.get(t).getAllDefenders().stream().anyMatch(ProMatches.unitIsOwnedTransport(player))) {
                        final List<Unit> defendingUnits = CollectionUtils.getMatches(moveMap.get(t).getAllDefenders(), Matches.unitIsNotLand());
                        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();
                        ProLogger.trace(t.getName() + " TUVSwing=" + result.getTuvSwing() + ", Win%=" + result.getWinPercentage() + ", enemyAttackers=" + moveMap.get(t).getMaxEnemyUnits().size() + ", defenders=" + defendingUnits.size());
                        if (result.getWinPercentage() > (100 - ProData.winPercentage) || result.getTuvSwing() > 0) {
                            ProLogger.trace(u + " added sea to defend transport at " + t);
                            moveMap.get(t).addTempUnit(u);
                            moveMap.get(t).setBattleResult(null);
                            territoriesToDefend.add(t);
                            it.remove();
                            // If carrier has dependent allied fighters then move them too
                            if (Matches.unitIsCarrier().test(u)) {
                                final Territory unitTerritory = unitTerritoryMap.get(u);
                                final Map<Unit, Collection<Unit>> carrierMustMoveWith = MoveValidator.carrierMustMoveWith(unitTerritory.getUnits().getUnits(), unitTerritory, data, player);
                                if (carrierMustMoveWith.containsKey(u)) {
                                    moveMap.get(t).getTempUnits().addAll(carrierMustMoveWith.get(u));
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
        // Move air units to defend transports
        for (final Iterator<Unit> it = currentUnitMoveMap.keySet().iterator(); it.hasNext(); ) {
            final Unit u = it.next();
            if (Matches.unitCanLandOnCarrier().test(u)) {
                for (final Territory t : currentUnitMoveMap.get(u)) {
                    if (t.isWater() && moveMap.get(t).isCanHold() && !moveMap.get(t).getAllDefenders().isEmpty() && moveMap.get(t).getAllDefenders().stream().anyMatch(ProMatches.unitIsOwnedTransport(player))) {
                        if (!ProTransportUtils.validateCarrierCapacity(player, t, moveMap.get(t).getAllDefendersForCarrierCalcs(data, player), u)) {
                            continue;
                        }
                        final List<Unit> defendingUnits = CollectionUtils.getMatches(moveMap.get(t).getAllDefenders(), Matches.unitIsNotLand());
                        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();
                        ProLogger.trace(t.getName() + " TUVSwing=" + result.getTuvSwing() + ", Win%=" + result.getWinPercentage() + ", enemyAttackers=" + moveMap.get(t).getMaxEnemyUnits().size() + ", defenders=" + defendingUnits.size());
                        if (result.getWinPercentage() > (100 - ProData.winPercentage) || result.getTuvSwing() > 0) {
                            ProLogger.trace(u + " added air to defend transport at " + t);
                            moveMap.get(t).addTempUnit(u);
                            moveMap.get(t).setBattleResult(null);
                            territoriesToDefend.add(t);
                            it.remove();
                            break;
                        }
                    }
                }
            }
        }
        // Move sea units to best location or safest location
        for (final Iterator<Unit> it = currentUnitMoveMap.keySet().iterator(); it.hasNext(); ) {
            final Unit u = it.next();
            if (Matches.unitIsSea().test(u)) {
                Territory maxValueTerritory = null;
                double maxValue = 0;
                for (final Territory t : currentUnitMoveMap.get(u)) {
                    if (moveMap.get(t).isCanHold()) {
                        final int transports = CollectionUtils.countMatches(moveMap.get(t).getAllDefenders(), ProMatches.unitIsOwnedTransport(player));
                        final double value = (1 + transports) * moveMap.get(t).getSeaValue() + (1 + transports * 100) * moveMap.get(t).getValue() / 10000;
                        ProLogger.trace(t + ", value=" + value + ", seaValue=" + moveMap.get(t).getSeaValue() + ", tValue=" + moveMap.get(t).getValue() + ", transports=" + transports);
                        if (value > maxValue) {
                            maxValue = value;
                            maxValueTerritory = t;
                        }
                    }
                }
                if (maxValueTerritory != null) {
                    ProLogger.trace(u + " added to best territory " + maxValueTerritory + ", value=" + maxValue);
                    moveMap.get(maxValueTerritory).addTempUnit(u);
                    moveMap.get(maxValueTerritory).setBattleResult(null);
                    territoriesToDefend.add(maxValueTerritory);
                    it.remove();
                    // If carrier has dependent allied fighters then move them too
                    if (Matches.unitIsCarrier().test(u)) {
                        final Territory unitTerritory = unitTerritoryMap.get(u);
                        final Map<Unit, Collection<Unit>> carrierMustMoveWith = MoveValidator.carrierMustMoveWith(unitTerritory.getUnits().getUnits(), unitTerritory, data, player);
                        if (carrierMustMoveWith.containsKey(u)) {
                            moveMap.get(maxValueTerritory).getTempUnits().addAll(carrierMustMoveWith.get(u));
                        }
                    }
                } else {
                    // Get all units that have already moved
                    final List<Unit> alreadyMovedUnits = new ArrayList<>();
                    for (final ProTerritory t : moveMap.values()) {
                        alreadyMovedUnits.addAll(t.getUnits());
                    }
                    // Find safest territory
                    double minStrengthDifference = Double.POSITIVE_INFINITY;
                    Territory minTerritory = null;
                    for (final Territory t : currentUnitMoveMap.get(u)) {
                        final List<Unit> attackers = moveMap.get(t).getMaxEnemyUnits();
                        final List<Unit> defenders = moveMap.get(t).getMaxDefenders();
                        defenders.removeAll(alreadyMovedUnits);
                        defenders.addAll(moveMap.get(t).getUnits());
                        final double strengthDifference = ProBattleUtils.estimateStrengthDifference(t, attackers, defenders);
                        if (strengthDifference < minStrengthDifference) {
                            minStrengthDifference = strengthDifference;
                            minTerritory = t;
                        }
                    }
                    if (minTerritory != null) {
                        ProLogger.trace(u + " moved to safest territory at " + minTerritory + ", strengthDifference=" + minStrengthDifference);
                        moveMap.get(minTerritory).addTempUnit(u);
                        moveMap.get(minTerritory).setBattleResult(null);
                        it.remove();
                        // If carrier has dependent allied fighters then move them too
                        if (Matches.unitIsCarrier().test(u)) {
                            final Territory unitTerritory = unitTerritoryMap.get(u);
                            final Map<Unit, Collection<Unit>> carrierMustMoveWith = MoveValidator.carrierMustMoveWith(unitTerritory.getUnits().getUnits(), unitTerritory, data, player);
                            if (carrierMustMoveWith.containsKey(u)) {
                                moveMap.get(minTerritory).getTempUnits().addAll(carrierMustMoveWith.get(u));
                            }
                        }
                    } else {
                        final Territory currentTerritory = unitTerritoryMap.get(u);
                        ProLogger.trace(u + " added to current territory since no better options at " + currentTerritory);
                        moveMap.get(currentTerritory).addTempUnit(u);
                        moveMap.get(currentTerritory).setBattleResult(null);
                        it.remove();
                    }
                }
            }
        }
        // Determine if all defenses are successful
        ProLogger.debug("Checking if all sea moves are safe for " + territoriesToDefend);
        boolean areSuccessful = true;
        for (final Territory t : territoriesToDefend) {
            // Find result with temp units
            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 = moveMap.get(t).getBattleResult();
            int isWater = 0;
            if (t.isWater()) {
                isWater = 1;
            }
            final double extraUnitValue = TuvUtils.getTuv(moveMap.get(t).getTempUnits(), ProData.unitValueMap);
            final double holdValue = result.getTuvSwing() - (extraUnitValue / 8 * (1 + isWater));
            // Find min result without temp units
            final List<Unit> minDefendingUnits = new ArrayList<>(defendingUnits);
            minDefendingUnits.removeAll(moveMap.get(t).getTempUnits());
            final ProBattleResult minResult = calc.calculateBattleResults(t, moveMap.get(t).getMaxEnemyUnits(), minDefendingUnits, moveMap.get(t).getMaxEnemyBombardUnits());
            // Check if territory is worth defending with temp units
            if (holdValue > minResult.getTuvSwing()) {
                areSuccessful = false;
                moveMap.get(t).setCanHold(false);
                moveMap.get(t).setValue(0);
                moveMap.get(t).setSeaValue(0);
                ProLogger.trace(t + " unable to defend so removing with holdValue=" + holdValue + ", minTUVSwing=" + minResult.getTuvSwing() + ", defenders=" + defendingUnits + ", enemyAttackers=" + moveMap.get(t).getMaxEnemyUnits());
            }
            ProLogger.trace(moveMap.get(t).getResultString() + ", holdValue=" + holdValue + ", minTUVSwing=" + minResult.getTuvSwing());
        }
        // Determine whether to try more territories, remove a territory, or end
        if (areSuccessful) {
            break;
        }
    }
    // 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.info("Move land units");
    // TODO: consider if territory ends up being safe
    for (final Iterator<Unit> it = unitMoveMap.keySet().iterator(); it.hasNext(); ) {
        final Unit u = it.next();
        if (Matches.unitIsLand().test(u)) {
            Territory maxValueTerritory = null;
            double maxValue = 0;
            int maxNeedAmphibUnitValue = Integer.MIN_VALUE;
            for (final Territory t : unitMoveMap.get(u)) {
                if (moveMap.get(t).isCanHold() && moveMap.get(t).getValue() >= maxValue) {
                    // Find transport capacity of neighboring (distance 1) transports
                    final List<Unit> transports1 = new ArrayList<>();
                    final Set<Territory> seaNeighbors = data.getMap().getNeighbors(t, ProMatches.territoryCanMoveSeaUnits(player, data, true));
                    for (final Territory neighborTerritory : seaNeighbors) {
                        if (moveMap.containsKey(neighborTerritory)) {
                            transports1.addAll(CollectionUtils.getMatches(moveMap.get(neighborTerritory).getAllDefenders(), ProMatches.unitIsOwnedTransport(player)));
                        }
                    }
                    int transportCapacity1 = 0;
                    for (final Unit transport : transports1) {
                        transportCapacity1 += UnitAttachment.get(transport.getType()).getTransportCapacity();
                    }
                    // Find transport capacity of nearby (distance 2) transports
                    final List<Unit> transports2 = new ArrayList<>();
                    final Set<Territory> nearbySeaTerritories = data.getMap().getNeighbors(t, 2, ProMatches.territoryCanMoveSeaUnits(player, data, true));
                    nearbySeaTerritories.removeAll(seaNeighbors);
                    for (final Territory neighborTerritory : nearbySeaTerritories) {
                        if (moveMap.containsKey(neighborTerritory)) {
                            transports2.addAll(CollectionUtils.getMatches(moveMap.get(neighborTerritory).getAllDefenders(), ProMatches.unitIsOwnedTransport(player)));
                        }
                    }
                    int transportCapacity2 = 0;
                    for (final Unit transport : transports2) {
                        transportCapacity2 += UnitAttachment.get(transport.getType()).getTransportCapacity();
                    }
                    final List<Unit> unitsToTransport = CollectionUtils.getMatches(moveMap.get(t).getAllDefenders(), ProMatches.unitIsOwnedTransportableUnit(player));
                    // Find transport cost of potential amphib units
                    int transportCost = 0;
                    for (final Unit unit : unitsToTransport) {
                        transportCost += UnitAttachment.get(unit.getType()).getTransportCost();
                    }
                    // Find territory that needs amphib units that most
                    int hasFactory = 0;
                    if (ProMatches.territoryHasInfraFactoryAndIsOwnedLandAdjacentToSea(player, data).test(t)) {
                        hasFactory = 1;
                    }
                    final int neededNeighborTransportValue = Math.max(0, transportCapacity1 - transportCost);
                    final int neededNearbyTransportValue = Math.max(0, transportCapacity1 + transportCapacity2 - transportCost);
                    final int needAmphibUnitValue = 1000 * neededNeighborTransportValue + 100 * neededNearbyTransportValue + (1 + 10 * hasFactory) * data.getMap().getNeighbors(t, ProMatches.territoryCanMoveSeaUnits(player, data, true)).size();
                    if (moveMap.get(t).getValue() > maxValue || needAmphibUnitValue > maxNeedAmphibUnitValue) {
                        maxValue = moveMap.get(t).getValue();
                        maxNeedAmphibUnitValue = needAmphibUnitValue;
                        maxValueTerritory = t;
                    }
                }
            }
            if (maxValueTerritory != null) {
                ProLogger.trace(u + " moved to " + maxValueTerritory + " with value=" + maxValue + ", numNeededTransportUnits=" + maxNeedAmphibUnitValue);
                moveMap.get(maxValueTerritory).addUnit(u);
                it.remove();
            }
        }
    }
    // Move land units towards nearest factory that is adjacent to the sea
    final Set<Territory> myFactoriesAdjacentToSea = new HashSet<>(CollectionUtils.getMatches(data.getMap().getTerritories(), ProMatches.territoryHasInfraFactoryAndIsOwnedLandAdjacentToSea(player, data)));
    for (final Iterator<Unit> it = unitMoveMap.keySet().iterator(); it.hasNext(); ) {
        final Unit u = it.next();
        if (Matches.unitIsLand().test(u)) {
            int minDistance = Integer.MAX_VALUE;
            Territory minTerritory = null;
            for (final Territory t : unitMoveMap.get(u)) {
                if (moveMap.get(t).isCanHold()) {
                    for (final Territory factory : myFactoriesAdjacentToSea) {
                        int distance = data.getMap().getDistance(t, factory, ProMatches.territoryCanMoveLandUnits(player, data, true));
                        if (distance < 0) {
                            distance = 10 * data.getMap().getDistance(t, factory);
                        }
                        if (distance >= 0 && distance < minDistance) {
                            minDistance = distance;
                            minTerritory = t;
                        }
                    }
                }
            }
            if (minTerritory != null) {
                ProLogger.trace(u.getType().getName() + " moved towards closest factory adjacent to sea at " + minTerritory.getName());
                moveMap.get(minTerritory).addUnit(u);
                it.remove();
            }
        }
    }
    ProLogger.info("Move land units to safest territory");
    // Move any remaining land units to safest territory (this is rarely used)
    for (final Iterator<Unit> it = unitMoveMap.keySet().iterator(); it.hasNext(); ) {
        final Unit u = it.next();
        if (Matches.unitIsLand().test(u)) {
            // Get all units that have already moved
            final List<Unit> alreadyMovedUnits = moveMap.values().stream().map(ProTerritory::getUnits).flatMap(Collection::stream).collect(Collectors.toList());
            // Find safest territory
            double minStrengthDifference = Double.POSITIVE_INFINITY;
            Territory minTerritory = null;
            for (final Territory t : unitMoveMap.get(u)) {
                final List<Unit> attackers = moveMap.get(t).getMaxEnemyUnits();
                final List<Unit> defenders = moveMap.get(t).getMaxDefenders();
                defenders.removeAll(alreadyMovedUnits);
                defenders.addAll(moveMap.get(t).getUnits());
                final double strengthDifference = ProBattleUtils.estimateStrengthDifference(t, attackers, defenders);
                if (strengthDifference < minStrengthDifference) {
                    minStrengthDifference = strengthDifference;
                    minTerritory = t;
                }
            }
            if (minTerritory != null) {
                ProLogger.debug(u.getType().getName() + " moved to safest territory at " + minTerritory.getName() + " with strengthDifference=" + minStrengthDifference);
                moveMap.get(minTerritory).addUnit(u);
                it.remove();
            }
        }
    }
    ProLogger.info("Move air units");
    // Get list of territories that can't be held
    final List<Territory> territoriesThatCantBeHeld = moveMap.entrySet().stream().filter(e -> !e.getValue().isCanHold()).map(Map.Entry::getKey).collect(Collectors.toList());
    // Move air units to safe territory with most attack options
    for (final Iterator<Unit> it = unitMoveMap.keySet().iterator(); it.hasNext(); ) {
        final Unit u = it.next();
        if (Matches.unitIsNotAir().test(u)) {
            continue;
        }
        double maxAirValue = 0;
        Territory maxTerritory = null;
        for (final Territory t : unitMoveMap.get(u)) {
            if (!moveMap.get(t).isCanHold()) {
                continue;
            }
            if (t.isWater() && !ProTransportUtils.validateCarrierCapacity(player, t, moveMap.get(t).getAllDefendersForCarrierCalcs(data, player), u)) {
                ProLogger.trace(t + " already at MAX carrier capacity");
                continue;
            }
            // Check to see if the territory is safe
            final List<Unit> defendingUnits = moveMap.get(t).getAllDefenders();
            defendingUnits.add(u);
            if (moveMap.get(t).getBattleResult() == null) {
                moveMap.get(t).setBattleResult(calc.calculateBattleResults(t, moveMap.get(t).getMaxEnemyUnits(), defendingUnits, moveMap.get(t).getMaxEnemyBombardUnits()));
            }
            final ProBattleResult result = moveMap.get(t).getBattleResult();
            ProLogger.trace(t + ", TUVSwing=" + result.getTuvSwing() + ", win%=" + result.getWinPercentage() + ", defendingUnits=" + defendingUnits + ", enemyAttackers=" + moveMap.get(t).getMaxEnemyUnits());
            if (result.getWinPercentage() >= ProData.minWinPercentage || result.getTuvSwing() > 0) {
                moveMap.get(t).setCanHold(false);
                continue;
            }
            // Determine if territory can be held with owned units
            final List<Unit> myDefenders = CollectionUtils.getMatches(defendingUnits, Matches.unitIsOwnedBy(player));
            final ProBattleResult result2 = calc.calculateBattleResults(t, moveMap.get(t).getMaxEnemyUnits(), myDefenders, moveMap.get(t).getMaxEnemyBombardUnits());
            int cantHoldWithoutAllies = 0;
            if (result2.getWinPercentage() >= ProData.minWinPercentage || result2.getTuvSwing() > 0) {
                cantHoldWithoutAllies = 1;
            }
            // Find number of potential attack options next turn
            final int range = TripleAUnit.get(u).getMaxMovementAllowed();
            final Set<Territory> possibleAttackTerritories = data.getMap().getNeighbors(t, range / 2, ProMatches.territoryCanMoveAirUnits(player, data, true));
            final int numEnemyAttackTerritories = CollectionUtils.countMatches(possibleAttackTerritories, ProMatches.territoryIsEnemyNotNeutralLand(player, data));
            final int numLandAttackTerritories = CollectionUtils.countMatches(possibleAttackTerritories, ProMatches.territoryIsEnemyOrCantBeHeldAndIsAdjacentToMyLandUnits(player, data, territoriesThatCantBeHeld));
            final int numSeaAttackTerritories = CollectionUtils.countMatches(possibleAttackTerritories, Matches.territoryHasEnemySeaUnits(player, data).and(Matches.territoryHasUnitsThatMatch(Matches.unitIsNotSub())));
            final Set<Territory> possibleMoveTerritories = data.getMap().getNeighbors(t, range, ProMatches.territoryCanMoveAirUnits(player, data, true));
            final int numNearbyEnemyTerritories = CollectionUtils.countMatches(possibleMoveTerritories, ProMatches.territoryIsEnemyNotNeutralLand(player, data));
            // Check if number of attack territories and value are max
            final int isntFactory = ProMatches.territoryHasInfraFactoryAndIsLand().test(t) ? 0 : 1;
            final int hasOwnedCarrier = moveMap.get(t).getAllDefenders().stream().anyMatch(ProMatches.unitIsOwnedCarrier(player)) ? 1 : 0;
            final double airValue = (200.0 * numSeaAttackTerritories + 100 * numLandAttackTerritories + 10 * numEnemyAttackTerritories + numNearbyEnemyTerritories) / (1 + cantHoldWithoutAllies) / (1 + cantHoldWithoutAllies * isntFactory) * (1 + hasOwnedCarrier);
            if (airValue > maxAirValue) {
                maxAirValue = airValue;
                maxTerritory = t;
            }
            ProLogger.trace("Safe territory: " + t + ", airValue=" + airValue + ", numLandAttackOptions=" + numLandAttackTerritories + ", numSeaAttackTerritories=" + numSeaAttackTerritories + ", numEnemyAttackTerritories=" + numEnemyAttackTerritories);
        }
        if (maxTerritory != null) {
            ProLogger.debug(u.getType().getName() + " added to safe territory with most attack options " + maxTerritory + ", maxAirValue=" + maxAirValue);
            moveMap.get(maxTerritory).addUnit(u);
            moveMap.get(maxTerritory).setBattleResult(null);
            it.remove();
        }
    }
    // Move air units to safest territory
    for (final Iterator<Unit> it = unitMoveMap.keySet().iterator(); it.hasNext(); ) {
        final Unit u = it.next();
        if (Matches.unitIsNotAir().test(u)) {
            continue;
        }
        double minStrengthDifference = Double.POSITIVE_INFINITY;
        Territory minTerritory = null;
        for (final Territory t : unitMoveMap.get(u)) {
            if (t.isWater() && !ProTransportUtils.validateCarrierCapacity(player, t, moveMap.get(t).getAllDefendersForCarrierCalcs(data, player), u)) {
                ProLogger.trace(t + " already at MAX carrier capacity");
                continue;
            }
            final List<Unit> attackers = moveMap.get(t).getMaxEnemyUnits();
            final List<Unit> defenders = moveMap.get(t).getAllDefenders();
            defenders.add(u);
            final double strengthDifference = ProBattleUtils.estimateStrengthDifference(t, attackers, defenders);
            ProLogger.trace("Unsafe territory: " + t + " with strengthDifference=" + strengthDifference);
            if (strengthDifference < minStrengthDifference) {
                minStrengthDifference = strengthDifference;
                minTerritory = t;
            }
        }
        if (minTerritory != null) {
            ProLogger.debug(u.getType().getName() + " added to safest territory at " + minTerritory + " with strengthDifference=" + minStrengthDifference);
            moveMap.get(minTerritory).addUnit(u);
            it.remove();
        }
    }
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) HashMap(java.util.HashMap) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) ArrayList(java.util.ArrayList) ProBattleResult(games.strategy.triplea.ai.pro.data.ProBattleResult) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) List(java.util.List) ArrayList(java.util.ArrayList) Route(games.strategy.engine.data.Route) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) ProPlaceTerritory(games.strategy.triplea.ai.pro.data.ProPlaceTerritory) ProPurchaseTerritory(games.strategy.triplea.ai.pro.data.ProPurchaseTerritory) ProTerritory(games.strategy.triplea.ai.pro.data.ProTerritory) Territory(games.strategy.engine.data.Territory) ProTransport(games.strategy.triplea.ai.pro.data.ProTransport) Collection(java.util.Collection) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap)

Aggregations

Territory (games.strategy.engine.data.Territory)4 Unit (games.strategy.engine.data.Unit)4 TripleAUnit (games.strategy.triplea.TripleAUnit)4 ProTerritory (games.strategy.triplea.ai.pro.data.ProTerritory)4 ProTransport (games.strategy.triplea.ai.pro.data.ProTransport)4 HashSet (java.util.HashSet)4 Set (java.util.Set)4 ProBattleResult (games.strategy.triplea.ai.pro.data.ProBattleResult)3 ProPlaceTerritory (games.strategy.triplea.ai.pro.data.ProPlaceTerritory)3 ProPurchaseTerritory (games.strategy.triplea.ai.pro.data.ProPurchaseTerritory)3 ArrayList (java.util.ArrayList)3 Collection (java.util.Collection)3 HashMap (java.util.HashMap)3 LinkedHashSet (java.util.LinkedHashSet)3 TreeMap (java.util.TreeMap)3 Route (games.strategy.engine.data.Route)1 ProOtherMoveOptions (games.strategy.triplea.ai.pro.data.ProOtherMoveOptions)1 List (java.util.List)1 Map (java.util.Map)1