package games.strategy.triplea.ai.proAI.util; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import games.strategy.engine.data.GameData; import games.strategy.engine.data.PlayerID; import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; import games.strategy.triplea.ai.proAI.ProData; import games.strategy.triplea.ai.proAI.data.ProTerritory; import games.strategy.triplea.attachments.UnitAttachment; import games.strategy.triplea.delegate.DiceRoll; import games.strategy.triplea.delegate.Matches; import games.strategy.triplea.delegate.TerritoryEffectHelper; import games.strategy.triplea.delegate.UnitBattleComparator; /** * Pro AI attack options utilities. */ public class ProSortMoveOptionsUtils { public static Map<Unit, Set<Territory>> sortUnitMoveOptions(final PlayerID player, final Map<Unit, Set<Territory>> unitAttackOptions) { final List<Map.Entry<Unit, Set<Territory>>> list = new LinkedList<>(unitAttackOptions.entrySet()); Collections.sort(list, (o1, o2) -> { // Sort by number of move options then cost of unit then unit type if (o1.getValue().size() != o2.getValue().size()) { return (o1.getValue().size() - o2.getValue().size()); } else if (ProData.unitValueMap.getInt(o1.getKey().getType()) != ProData.unitValueMap .getInt(o2.getKey().getType())) { return (ProData.unitValueMap.getInt(o1.getKey().getType()) - ProData.unitValueMap.getInt(o2.getKey().getType())); } return o1.getKey().getType().getName().compareTo(o2.getKey().getType().getName()); }); final Map<Unit, Set<Territory>> sortedUnitAttackOptions = new LinkedHashMap<>(); for (final Map.Entry<Unit, Set<Territory>> entry : list) { sortedUnitAttackOptions.put(entry.getKey(), entry.getValue()); } return sortedUnitAttackOptions; } public static Map<Unit, Set<Territory>> sortUnitNeededOptions(final PlayerID player, final Map<Unit, Set<Territory>> unitAttackOptions, final Map<Territory, ProTerritory> attackMap, final ProOddsCalculator calc) { final GameData data = ProData.getData(); final List<Map.Entry<Unit, Set<Territory>>> list = new LinkedList<>(unitAttackOptions.entrySet()); Collections.sort(list, (o1, o2) -> { // Find number of territories that still need units int numOptions1 = 0; for (final Territory t : o1.getValue()) { final ProTerritory patd = attackMap.get(t); if (patd.getBattleResult() == null) { patd.setBattleResult(calc.estimateAttackBattleResults(player, t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet())); } if (!patd.isCurrentlyWins()) { numOptions1++; } } int numOptions2 = 0; for (final Territory t : o2.getValue()) { final ProTerritory patd = attackMap.get(t); if (patd.getBattleResult() == null) { patd.setBattleResult(calc.estimateAttackBattleResults(player, t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet())); } if (!patd.isCurrentlyWins()) { numOptions2++; } } // Sort by number of move options then cost of unit then unit type if (numOptions1 != numOptions2) { return (numOptions1 - numOptions2); } if (ProData.unitValueMap.getInt(o1.getKey().getType()) != ProData.unitValueMap.getInt(o2.getKey().getType())) { return (ProData.unitValueMap.getInt(o1.getKey().getType()) - ProData.unitValueMap.getInt(o2.getKey().getType())); } return o1.getKey().getType().getName().compareTo(o2.getKey().getType().getName()); }); final Map<Unit, Set<Territory>> sortedUnitAttackOptions = new LinkedHashMap<>(); for (final Map.Entry<Unit, Set<Territory>> entry : list) { sortedUnitAttackOptions.put(entry.getKey(), entry.getValue()); } return sortedUnitAttackOptions; } public static Map<Unit, Set<Territory>> sortUnitNeededOptionsThenAttack(final PlayerID player, final Map<Unit, Set<Territory>> unitAttackOptions, final Map<Territory, ProTerritory> attackMap, final Map<Unit, Territory> unitTerritoryMap, final ProOddsCalculator calc) { final GameData data = ProData.getData(); final List<Map.Entry<Unit, Set<Territory>>> list = new LinkedList<>(unitAttackOptions.entrySet()); Collections.sort(list, (o1, o2) -> { // Sort by number of territories that still need units int numOptions1 = 0; for (final Territory t : o1.getValue()) { final ProTerritory patd = attackMap.get(t); if (patd.getBattleResult() == null) { patd.setBattleResult(calc.estimateAttackBattleResults(player, t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet())); } if (!patd.isCurrentlyWins()) { numOptions1++; } } int numOptions2 = 0; for (final Territory t : o2.getValue()) { final ProTerritory patd = attackMap.get(t); if (patd.getBattleResult() == null) { patd.setBattleResult(calc.estimateAttackBattleResults(player, t, patd.getUnits(), patd.getMaxEnemyDefenders(player, data), patd.getBombardTerritoryMap().keySet())); } if (!patd.isCurrentlyWins()) { numOptions2++; } } if (numOptions1 != numOptions2) { return (numOptions1 - numOptions2); } if (numOptions1 == 0) { return 0; } // Sort by attack efficiency int minPower1 = Integer.MAX_VALUE; for (final Territory t : o1.getValue()) { if (!attackMap.get(t).isCurrentlyWins()) { final List<Unit> defendingUnits = t.getUnits().getMatches(Matches.enemyUnit(player, data)); final List<Unit> sortedUnitsList = new ArrayList<>(attackMap.get(t).getUnits()); Collections.sort(sortedUnitsList, new UnitBattleComparator(false, ProData.unitValueMap, TerritoryEffectHelper.getEffects(t), data, false, false)); Collections.reverse(sortedUnitsList); final int powerWithout = DiceRoll.getTotalPower(DiceRoll.getUnitPowerAndRollsForNormalBattles(sortedUnitsList, defendingUnits, false, false, data, t, TerritoryEffectHelper.getEffects(t), false, null), data); sortedUnitsList.add(o1.getKey()); Collections.sort(sortedUnitsList, new UnitBattleComparator(false, ProData.unitValueMap, TerritoryEffectHelper.getEffects(t), data, false, false)); Collections.reverse(sortedUnitsList); final int powerWith = DiceRoll.getTotalPower(DiceRoll.getUnitPowerAndRollsForNormalBattles(sortedUnitsList, defendingUnits, false, false, data, t, TerritoryEffectHelper.getEffects(t), false, null), data); final int power = powerWith - powerWithout; if (power < minPower1) { minPower1 = power; } } } final UnitAttachment ua1 = UnitAttachment.get(o1.getKey().getType()); if (ua1.getIsAir()) { minPower1 *= 10; } final double attackEfficiency1 = (double) minPower1 / ProData.unitValueMap.getInt(o1.getKey().getType()); int minPower2 = Integer.MAX_VALUE; for (final Territory t : o2.getValue()) { if (!attackMap.get(t).isCurrentlyWins()) { final List<Unit> defendingUnits = t.getUnits().getMatches(Matches.enemyUnit(player, data)); final List<Unit> sortedUnitsList = new ArrayList<>(attackMap.get(t).getUnits()); Collections.sort(sortedUnitsList, new UnitBattleComparator(false, ProData.unitValueMap, TerritoryEffectHelper.getEffects(t), data, false, false)); Collections.reverse(sortedUnitsList); final int powerWithout = DiceRoll.getTotalPower(DiceRoll.getUnitPowerAndRollsForNormalBattles(sortedUnitsList, defendingUnits, false, false, data, t, TerritoryEffectHelper.getEffects(t), false, null), data); sortedUnitsList.add(o2.getKey()); Collections.sort(sortedUnitsList, new UnitBattleComparator(false, ProData.unitValueMap, TerritoryEffectHelper.getEffects(t), data, false, false)); Collections.reverse(sortedUnitsList); final int powerWith = DiceRoll.getTotalPower(DiceRoll.getUnitPowerAndRollsForNormalBattles(sortedUnitsList, defendingUnits, false, false, data, t, TerritoryEffectHelper.getEffects(t), false, null), data); final int power = powerWith - powerWithout; if (power < minPower2) { minPower2 = power; } } } final UnitAttachment ua2 = UnitAttachment.get(o2.getKey().getType()); if (ua2.getIsAir()) { minPower2 *= 10; } final double attackEfficiency2 = (double) minPower2 / ProData.unitValueMap.getInt(o2.getKey().getType()); if (attackEfficiency1 != attackEfficiency2) { if (attackEfficiency1 < attackEfficiency2) { return 1; } else { return -1; } } // Check if unit types are equal and is air then sort by average distance if (o1.getKey().getType().equals(o2.getKey().getType())) { final boolean isAirUnit = UnitAttachment.get(o1.getKey().getType()).getIsAir(); if (isAirUnit) { int distance1 = 0; for (final Territory t : o1.getValue()) { if (!attackMap.get(t).isCurrentlyWins()) { distance1 += data.getMap().getDistance_IgnoreEndForCondition(unitTerritoryMap.get(o1.getKey()), t, ProMatches.territoryCanMoveAirUnitsAndNoAA(player, data, true)); } } int distance2 = 0; for (final Territory t : o2.getValue()) { if (!attackMap.get(t).isCurrentlyWins()) { distance2 += data.getMap().getDistance_IgnoreEndForCondition(unitTerritoryMap.get(o2.getKey()), t, ProMatches.territoryCanMoveAirUnitsAndNoAA(player, data, true)); } } if (distance1 != distance2) { return distance1 - distance2; } } } return o1.getKey().getType().getName().compareTo(o2.getKey().getType().getName()); }); final Map<Unit, Set<Territory>> sortedUnitAttackOptions = new LinkedHashMap<>(); for (final Map.Entry<Unit, Set<Territory>> entry : list) { sortedUnitAttackOptions.put(entry.getKey(), entry.getValue()); } return sortedUnitAttackOptions; } }