Пример #1
0
  private static void getNeutralOutOfWarWithAllies(
      final PoliticalActionAttachment paa, final PlayerID player, final IDelegateBridge aBridge) {
    final GameData data = aBridge.getData();
    if (!games.strategy.triplea.Properties.getAlliancesCanChainTogether(data)) {
      return;
    }

    final Collection<PlayerID> players = data.getPlayerList().getPlayers();
    final Collection<PlayerID> p1AlliedWith =
        Match.getMatches(players, Matches.isAlliedAndAlliancesCanChainTogether(player, data));
    final CompositeChange change = new CompositeChange();
    for (final String relationshipChangeString : paa.getRelationshipChange()) {
      final String[] relationshipChange = relationshipChangeString.split(":");
      final PlayerID p1 = data.getPlayerList().getPlayerID(relationshipChange[0]);
      final PlayerID p2 = data.getPlayerList().getPlayerID(relationshipChange[1]);
      if (!(p1.equals(player) || p2.equals(player))) {
        continue;
      }
      final PlayerID pOther = (p1.equals(player) ? p2 : p1);
      final RelationshipType currentType =
          data.getRelationshipTracker().getRelationshipType(p1, p2);
      final RelationshipType newType =
          data.getRelationshipTypeList().getRelationshipType(relationshipChange[2]);
      if (Matches.RelationshipTypeIsAtWar.match(currentType)
          && Matches.RelationshipTypeIsAtWar.invert().match(newType)) {
        final Collection<PlayerID> pOtherAlliedWith =
            Match.getMatches(players, Matches.isAlliedAndAlliancesCanChainTogether(pOther, data));
        if (!pOtherAlliedWith.contains(pOther)) {
          pOtherAlliedWith.add(pOther);
        }
        if (!p1AlliedWith.contains(player)) {
          p1AlliedWith.add(player);
        }
        for (final PlayerID p3 : p1AlliedWith) {
          for (final PlayerID p4 : pOtherAlliedWith) {
            final RelationshipType currentOther =
                data.getRelationshipTracker().getRelationshipType(p3, p4);
            if (!currentOther.equals(newType)
                && Matches.RelationshipTypeIsAtWar.match(currentOther)) {
              change.add(ChangeFactory.relationshipChange(p3, p4, currentOther, newType));
              aBridge
                  .getHistoryWriter()
                  .addChildToEvent(
                      p3.getName()
                          + " and "
                          + p4.getName()
                          + " sign a "
                          + newType.getName()
                          + " treaty");
              MoveDelegate.getBattleTracker(data)
                  .addRelationshipChangesThisTurn(p3, p4, currentOther, newType);
            }
          }
        }
      }
    }
    if (!change.isEmpty()) {
      aBridge.addChange(change);
    }
  }
Пример #2
0
  /** Return a Comparator that will order the specified transports in preferred unload order. */
  public static Comparator<Unit> getUnloadableTransportsComparator(
      final List<Unit> transports, final Route route, final PlayerID player, final boolean noTies) {
    final Comparator<Unit> decreasingCapacityComparator =
        getDecreasingCapacityComparator(transports);
    final Match<Unit> incapableTransportMatch = Matches.transportCannotUnload(route.getEnd());
    return (t1, t2) -> {

      // Check if transport is incapable due to game state
      final boolean isIncapable1 = incapableTransportMatch.match(t1);
      final boolean isIncapable2 = incapableTransportMatch.match(t2);
      if (!isIncapable1 && isIncapable2) {
        return -1;
      }
      if (isIncapable1 && !isIncapable2) {
        return 1;
      }

      // Prioritize allied transports
      final boolean isAlliedTrn1 = !t1.getOwner().equals(player);
      final boolean isAlliedTrn2 = !t2.getOwner().equals(player);
      if (isAlliedTrn1 && !isAlliedTrn2) {
        return -1;
      }
      if (!isAlliedTrn1 && isAlliedTrn2) {
        return 1;
      }

      // Sort by decreasing transport capacity
      final int compareCapacity = decreasingCapacityComparator.compare(t1, t2);
      if (compareCapacity != 0) {
        return compareCapacity;
      }

      // Sort by increasing movement
      final int left1 = TripleAUnit.get(t1).getMovementLeft();
      final int left2 = TripleAUnit.get(t2).getMovementLeft();
      if (left1 != left2) {
        return left1 - left2;
      }

      // If noTies is set, sort by hashcode so that result is deterministic
      if (noTies) {
        return Integer.compare(t1.hashCode(), t2.hashCode());
      } else {
        return 0;
      }
    };
  }
Пример #3
0
  /** Return a Comparator that will order the specified transports in preferred load order. */
  public static Comparator<Unit> getLoadableTransportsComparator(
      final List<Unit> transports, final Route route, final PlayerID player) {
    final Comparator<Unit> decreasingCapacityComparator =
        getDecreasingCapacityComparator(transports);
    final Match<Unit> incapableTransportMatch = Matches.transportCannotUnload(route.getEnd());
    return (u1, u2) -> {
      final TripleAUnit t1 = TripleAUnit.get(u1);
      final TripleAUnit t2 = TripleAUnit.get(u2);

      // Check if transport is incapable due to game state
      final boolean isIncapable1 = incapableTransportMatch.match(t1);
      final boolean isIncapable2 = incapableTransportMatch.match(t2);
      if (!isIncapable1 && isIncapable2) {
        return -1;
      }
      if (isIncapable1 && !isIncapable2) {
        return 1;
      }

      // Use allied transports as a last resort
      final boolean isAlliedTrn1 = !t1.getOwner().equals(player);
      final boolean isAlliedTrn2 = !t2.getOwner().equals(player);
      if (!isAlliedTrn1 && isAlliedTrn2) {
        return -1;
      }
      if (isAlliedTrn1 && !isAlliedTrn2) {
        return 1;
      }

      // Sort by decreasing transport capacity
      final int compareCapacity = decreasingCapacityComparator.compare(t1, t2);
      if (compareCapacity != 0) {
        return compareCapacity;
      }

      // Sort by decreasing movement
      final int left1 = t1.getMovementLeft();
      final int left2 = t1.getMovementLeft();
      if (left1 != left2) {
        return left2 - left1;
      }

      return Integer.compare(t1.hashCode(), t2.hashCode());
    };
  }
 /**
  * @param player
  * @return gets the valid actions for this player.
  */
 public static Collection<PoliticalActionAttachment> getValidActions(
     final PlayerID player,
     final HashMap<ICondition, Boolean> testedConditions,
     final GameData data) {
   if (!games.strategy.triplea.Properties.getUsePolitics(data) || !player.amNotDeadYet(data))
     return new ArrayList<PoliticalActionAttachment>();
   return Match.getMatches(
       getPoliticalActionAttachments(player),
       new CompositeMatchAnd<PoliticalActionAttachment>(
           Matches.AbstractUserActionAttachmentCanBeAttempted(testedConditions),
           Matches.politicalActionAffectsAtLeastOneAlivePlayer(player, data)));
 }
Пример #5
0
 @Override
 public void end() {
   super.end();
   resetAttempts();
   if (games.strategy.triplea.Properties.getTriggers(getData())) {
     // First set up a match for what we want to have fire as a default in this delegate. List out
     // as a composite match
     // OR.
     // use 'null, null' because this is the Default firing location for any trigger that does NOT
     // have 'when' set.
     final Match<TriggerAttachment> politicsDelegateTriggerMatch =
         new CompositeMatchAnd<>(
             TriggerAttachment.availableUses,
             TriggerAttachment.whenOrDefaultMatch(null, null),
             new CompositeMatchOr<TriggerAttachment>(TriggerAttachment.relationshipChangeMatch()));
     // get all possible triggers based on this match.
     final HashSet<TriggerAttachment> toFirePossible =
         TriggerAttachment.collectForAllTriggersMatching(
             new HashSet<>(Collections.singleton(m_player)),
             politicsDelegateTriggerMatch,
             m_bridge);
     if (!toFirePossible.isEmpty()) {
       // get all conditions possibly needed by these triggers, and then test them.
       final HashMap<ICondition, Boolean> testedConditions =
           TriggerAttachment.collectTestsForAllTriggers(toFirePossible, m_bridge);
       // get all triggers that are satisfied based on the tested conditions.
       final Set<TriggerAttachment> toFireTestedAndSatisfied =
           new HashSet<>(
               Match.getMatches(
                   toFirePossible, TriggerAttachment.isSatisfiedMatch(testedConditions)));
       // now list out individual types to fire, once for each of the matches above.
       TriggerAttachment.triggerRelationshipChange(
           toFireTestedAndSatisfied, m_bridge, null, null, true, true, true, true);
     }
   }
   chainAlliancesTogether(m_bridge);
   givesBackOriginalTerritories(m_bridge);
   // m_needToInitialize = true;
 }
Пример #6
0
 public static void chainAlliancesTogether(final IDelegateBridge aBridge) {
   final GameData data = aBridge.getData();
   if (!games.strategy.triplea.Properties.getAlliancesCanChainTogether(data)) {
     return;
   }
   final Collection<RelationshipType> allTypes =
       data.getRelationshipTypeList().getAllRelationshipTypes();
   RelationshipType alliedType = null;
   RelationshipType warType = null;
   for (final RelationshipType type : allTypes) {
     if (type.getRelationshipTypeAttachment().getIsDefaultWarPosition()) {
       warType = type;
     } else if (type.getRelationshipTypeAttachment().getAlliancesCanChainTogether()) {
       alliedType = type;
     }
   }
   if (alliedType == null) {
     return;
   }
   // first do alliances. then, do war (since we don't want to declare war on a potential ally).
   final Collection<PlayerID> players = data.getPlayerList().getPlayers();
   for (final PlayerID p1 : players) {
     final HashSet<PlayerID> p1NewAllies = new HashSet<>();
     final Collection<PlayerID> p1AlliedWith =
         Match.getMatches(players, Matches.isAlliedAndAlliancesCanChainTogether(p1, data));
     for (final PlayerID p2 : p1AlliedWith) {
       p1NewAllies.addAll(
           Match.getMatches(players, Matches.isAlliedAndAlliancesCanChainTogether(p2, data)));
     }
     p1NewAllies.removeAll(p1AlliedWith);
     p1NewAllies.remove(p1);
     for (final PlayerID p3 : p1NewAllies) {
       if (!data.getRelationshipTracker().getRelationshipType(p1, p3).equals(alliedType)) {
         final RelationshipType current =
             data.getRelationshipTracker().getRelationshipType(p1, p3);
         aBridge.addChange(ChangeFactory.relationshipChange(p1, p3, current, alliedType));
         aBridge
             .getHistoryWriter()
             .addChildToEvent(
                 p1.getName()
                     + " and "
                     + p3.getName()
                     + " are joined together in an "
                     + alliedType.getName()
                     + " treaty");
         MoveDelegate.getBattleTracker(data)
             .addRelationshipChangesThisTurn(p1, p3, current, alliedType);
       }
     }
   }
   // now war
   if (warType == null) {
     return;
   }
   for (final PlayerID p1 : players) {
     final HashSet<PlayerID> p1NewWar = new HashSet<>();
     final Collection<PlayerID> p1WarWith = Match.getMatches(players, Matches.isAtWar(p1, data));
     final Collection<PlayerID> p1AlliedWith =
         Match.getMatches(players, Matches.isAlliedAndAlliancesCanChainTogether(p1, data));
     for (final PlayerID p2 : p1AlliedWith) {
       p1NewWar.addAll(Match.getMatches(players, Matches.isAtWar(p2, data)));
     }
     p1NewWar.removeAll(p1WarWith);
     p1NewWar.remove(p1);
     for (final PlayerID p3 : p1NewWar) {
       if (!data.getRelationshipTracker().getRelationshipType(p1, p3).equals(warType)) {
         final RelationshipType current =
             data.getRelationshipTracker().getRelationshipType(p1, p3);
         aBridge.addChange(ChangeFactory.relationshipChange(p1, p3, current, warType));
         aBridge
             .getHistoryWriter()
             .addChildToEvent(
                 p1.getName()
                     + " and "
                     + p3.getName()
                     + " declare "
                     + warType.getName()
                     + " on each other");
         MoveDelegate.getBattleTracker(data)
             .addRelationshipChangesThisTurn(p1, p3, current, warType);
       }
     }
   }
 }
Пример #7
0
 /**
  * Get a list of players that should accept this action and then ask each player if it accepts
  * this action.
  *
  * @param paa the politicalActionAttachment that should be accepted
  */
 private boolean actionIsAccepted(final PoliticalActionAttachment paa) {
   final GameData data = getData();
   final CompositeMatchOr<PoliticalActionAttachment> intoAlliedChainOrIntoOrOutOfWar =
       new CompositeMatchOr<>(
           Matches.politicalActionIsRelationshipChangeOf(
               null,
               Matches.RelationshipTypeIsAlliedAndAlliancesCanChainTogether.invert(),
               Matches.RelationshipTypeIsAlliedAndAlliancesCanChainTogether,
               data),
           Matches.politicalActionIsRelationshipChangeOf(
               null,
               Matches.RelationshipTypeIsAtWar.invert(),
               Matches.RelationshipTypeIsAtWar,
               data),
           Matches.politicalActionIsRelationshipChangeOf(
               null,
               Matches.RelationshipTypeIsAtWar,
               Matches.RelationshipTypeIsAtWar.invert(),
               data));
   if (!games.strategy.triplea.Properties.getAlliancesCanChainTogether(data)
       || !intoAlliedChainOrIntoOrOutOfWar.match(paa)) {
     for (final PlayerID player : paa.getActionAccept()) {
       if (!(getRemotePlayer(player))
           .acceptAction(
               m_player, PoliticsText.getInstance().getAcceptanceQuestion(paa.getText()), true)) {
         return false;
       }
     }
   } else {
     // if alliances chain together, then our allies must have a say in anyone becoming a new
     // ally/enemy
     final LinkedHashSet<PlayerID> playersWhoNeedToAccept = new LinkedHashSet<>();
     playersWhoNeedToAccept.addAll(paa.getActionAccept());
     playersWhoNeedToAccept.addAll(
         Match.getMatches(
             data.getPlayerList().getPlayers(),
             Matches.isAlliedAndAlliancesCanChainTogether(m_player, data)));
     for (final PlayerID player : paa.getActionAccept()) {
       playersWhoNeedToAccept.addAll(
           Match.getMatches(
               data.getPlayerList().getPlayers(),
               Matches.isAlliedAndAlliancesCanChainTogether(player, data)));
     }
     final HashSet<PlayerID> alliesWhoMustAccept = playersWhoNeedToAccept;
     alliesWhoMustAccept.removeAll(paa.getActionAccept());
     for (final PlayerID player : playersWhoNeedToAccept) {
       String actionText = PoliticsText.getInstance().getAcceptanceQuestion(paa.getText());
       if (actionText.equals("NONE")) {
         actionText =
             m_player.getName()
                 + " wants to take the following action: "
                 + MyFormatter.attachmentNameToText(paa.getName())
                 + " \r\n Do you approve?";
       } else {
         actionText =
             m_player.getName()
                 + " wants to take the following action: "
                 + MyFormatter.attachmentNameToText(paa.getName())
                 + ".  Do you approve? \r\n\r\n "
                 + m_player.getName()
                 + " will ask "
                 + MyFormatter.defaultNamedToTextList(paa.getActionAccept())
                 + ", the following question: \r\n "
                 + actionText;
       }
       if (!(getRemotePlayer(player)).acceptAction(m_player, actionText, true)) {
         return false;
       }
     }
     for (final PlayerID player : paa.getActionAccept()) {
       if (!(getRemotePlayer(player))
           .acceptAction(
               m_player, PoliticsText.getInstance().getAcceptanceQuestion(paa.getText()), true)) {
         return false;
       }
     }
   }
   return true;
 }
  public static Map<Territory, Double> findTerritoryValues(
      final PlayerID player,
      final List<Territory> territoriesThatCantBeHeld,
      final List<Territory> territoriesToAttack) {
    final GameData data = ProData.getData();
    final List<Territory> allTerritories = data.getMap().getTerritories();

    // Get all enemy factories and capitals (check if most territories have factories and if so
    // remove them)
    final Set<Territory> enemyCapitalsAndFactories = new HashSet<Territory>();
    enemyCapitalsAndFactories.addAll(
        Match.getMatches(
            allTerritories,
            ProMatches.territoryHasInfraFactoryAndIsOwnedByPlayersOrCantBeHeld(
                player,
                data,
                ProUtils.getPotentialEnemyPlayers(player),
                territoriesThatCantBeHeld)));
    final int numPotentialEnemyTerritories =
        Match.countMatches(
            allTerritories, Matches.isTerritoryOwnedBy(ProUtils.getPotentialEnemyPlayers(player)));
    if (enemyCapitalsAndFactories.size() * 2 >= numPotentialEnemyTerritories) {
      enemyCapitalsAndFactories.clear();
    }
    enemyCapitalsAndFactories.addAll(ProUtils.getLiveEnemyCapitals(data, player));
    enemyCapitalsAndFactories.removeAll(territoriesToAttack);

    // Find max land mass size
    int maxLandMassSize = 1;
    for (final Territory t : allTerritories) {
      if (!t.isWater()) {
        final int landMassSize =
            1
                + data.getMap()
                    .getNeighbors(
                        t, 6, ProMatches.territoryCanPotentiallyMoveLandUnits(player, data, true))
                    .size();
        if (landMassSize > maxLandMassSize) {
          maxLandMassSize = landMassSize;
        }
      }
    }

    // Loop through factories/capitals and find value
    final Map<Territory, Double> enemyCapitalsAndFactoriesMap = new HashMap<Territory, Double>();
    for (final Territory t : enemyCapitalsAndFactories) {

      // Get factory production if factory
      int factoryProduction = 0;
      if (ProMatches.territoryHasInfraFactoryAndIsLand(player).match(t)) {
        factoryProduction = TerritoryAttachment.getProduction(t);
      }

      // Get player production if capital
      double playerProduction = 0;
      final TerritoryAttachment ta = TerritoryAttachment.get(t);
      if (ta != null && ta.isCapital()) {
        playerProduction = ProUtils.getPlayerProduction(t.getOwner(), data);
      }

      // Check if neutral
      final int isNeutral = t.getOwner().isNull() ? 1 : 0;

      // Calculate value
      final int landMassSize =
          1
              + data.getMap()
                  .getNeighbors(
                      t, 6, ProMatches.territoryCanPotentiallyMoveLandUnits(player, data, true))
                  .size();
      final double value =
          Math.sqrt(factoryProduction + Math.sqrt(playerProduction))
              * 32
              / (1 + 3 * isNeutral)
              * landMassSize
              / maxLandMassSize;
      enemyCapitalsAndFactoriesMap.put(t, value);
    }

    // Determine value for land territories
    final Map<Territory, Double> territoryValueMap = new HashMap<Territory, Double>();
    for (final Territory t : allTerritories) {
      if (!t.isWater() && !territoriesThatCantBeHeld.contains(t)) {

        // Determine value based on enemy factory land distance
        final List<Double> values = new ArrayList<Double>();
        for (final Territory enemyCapitalOrFactory : enemyCapitalsAndFactoriesMap.keySet()) {
          final int distance =
              data.getMap()
                  .getDistance(
                      t,
                      enemyCapitalOrFactory,
                      ProMatches.territoryCanPotentiallyMoveLandUnits(player, data, true));
          if (distance > 0) {
            values.add(
                enemyCapitalsAndFactoriesMap.get(enemyCapitalOrFactory) / Math.pow(2, distance));
          }
        }
        Collections.sort(values, Collections.reverseOrder());
        double capitalOrFactoryValue = 0;
        for (int i = 0; i < values.size(); i++) {
          capitalOrFactoryValue +=
              values.get(i) / Math.pow(2, i); // Decrease each additional factory value by half
        }

        // Determine value based on nearby territory production
        double nearbyEnemyValue = 0;
        final Set<Territory> nearbyTerritories =
            data.getMap()
                .getNeighbors(
                    t, 2, ProMatches.territoryCanPotentiallyMoveLandUnits(player, data, true));
        final List<Territory> nearbyEnemyTerritories =
            Match.getMatches(
                nearbyTerritories,
                ProMatches.territoryIsEnemyOrCantBeHeld(player, data, territoriesThatCantBeHeld));
        nearbyEnemyTerritories.removeAll(territoriesToAttack);
        for (final Territory nearbyEnemyTerritory : nearbyEnemyTerritories) {
          final int distance =
              data.getMap()
                  .getDistance(
                      t,
                      nearbyEnemyTerritory,
                      ProMatches.territoryCanPotentiallyMoveLandUnits(player, data, true));
          if (distance > 0) {
            double value = TerritoryAttachment.getProduction(nearbyEnemyTerritory);
            if (nearbyEnemyTerritory.getOwner().isNull()) {
              value =
                  findTerritoryAttackValue(player, nearbyEnemyTerritory) / 3; // find neutral value
            } else if (ProMatches.territoryIsAlliedLandAndHasNoEnemyNeighbors(player, data)
                .match(nearbyEnemyTerritory)) {
              value *= 0.1; // reduce value for can't hold amphib allied territories
            }
            if (value > 0) {
              nearbyEnemyValue += (value / Math.pow(2, distance));
            }
          }
        }
        final int landMassSize =
            1
                + data.getMap()
                    .getNeighbors(
                        t, 6, ProMatches.territoryCanPotentiallyMoveLandUnits(player, data, true))
                    .size();
        double value = nearbyEnemyValue * landMassSize / maxLandMassSize + capitalOrFactoryValue;
        if (ProMatches.territoryHasInfraFactoryAndIsLand(player).match(t)) {
          value *= 1.1; // prefer territories with factories
        }
        territoryValueMap.put(t, value);
      } else if (!t.isWater()) {
        territoryValueMap.put(t, 0.0);
      }
    }

    // Determine value for water territories
    for (final Territory t : allTerritories) {
      if (!territoriesThatCantBeHeld.contains(t)
          && t.isWater()
          && !data.getMap().getNeighbors(t, Matches.TerritoryIsWater).isEmpty()) {

        // Determine value based on enemy factory distance
        final List<Double> values = new ArrayList<Double>();
        for (final Territory enemyCapitalOrFactory : enemyCapitalsAndFactoriesMap.keySet()) {
          final Route route =
              data.getMap()
                  .getRoute_IgnoreEnd(
                      t,
                      enemyCapitalOrFactory,
                      ProMatches.territoryCanMoveSeaUnits(player, data, true));
          if (route == null || MoveValidator.validateCanal(route, null, player, data) != null) {
            continue;
          }
          final int distance = route.numberOfSteps();
          if (distance > 0) {
            values.add(
                enemyCapitalsAndFactoriesMap.get(enemyCapitalOrFactory) / Math.pow(2, distance));
          }
        }
        Collections.sort(values, Collections.reverseOrder());
        double capitalOrFactoryValue = 0;
        for (int i = 0; i < values.size(); i++) {
          capitalOrFactoryValue +=
              values.get(i) / Math.pow(2, i); // Decrease each additional factory value by half
        }

        // Determine value based on nearby territory production
        double nearbyLandValue = 0;
        final Set<Territory> nearbyTerritories = data.getMap().getNeighbors(t, 3);
        final List<Territory> nearbyLandTerritories =
            Match.getMatches(
                nearbyTerritories,
                ProMatches.territoryCanPotentiallyMoveLandUnits(player, data, false));
        nearbyLandTerritories.removeAll(territoriesToAttack);
        for (final Territory nearbyLandTerritory : nearbyLandTerritories) {
          final Route route =
              data.getMap()
                  .getRoute_IgnoreEnd(
                      t,
                      nearbyLandTerritory,
                      ProMatches.territoryCanMoveSeaUnits(player, data, true));
          if (route == null || MoveValidator.validateCanal(route, null, player, data) != null) {
            continue;
          }
          final int distance = route.numberOfSteps();
          if (distance > 0 && distance <= 3) {
            if (ProMatches.territoryIsEnemyOrCantBeHeld(player, data, territoriesThatCantBeHeld)
                .match(nearbyLandTerritory)) {
              double value = TerritoryAttachment.getProduction(nearbyLandTerritory);
              if (nearbyLandTerritory.getOwner().isNull()) {
                value = findTerritoryAttackValue(player, nearbyLandTerritory);
              }
              nearbyLandValue += value;
            }
            nearbyLandValue += territoryValueMap.get(nearbyLandTerritory);
          }
        }
        final double value = capitalOrFactoryValue / 100 + nearbyLandValue / 10;
        territoryValueMap.put(t, value);
      } else if (t.isWater()) {
        territoryValueMap.put(t, 0.0);
      }
    }
    return territoryValueMap;
  }
  public static Map<Territory, Double> findSeaTerritoryValues(
      final PlayerID player, final List<Territory> territoriesThatCantBeHeld) {
    final GameData data = ProData.getData();
    final List<Territory> allTerritories = data.getMap().getTerritories();

    // Determine value for water territories
    final Map<Territory, Double> territoryValueMap = new HashMap<Territory, Double>();
    for (final Territory t : allTerritories) {
      if (!territoriesThatCantBeHeld.contains(t)
          && t.isWater()
          && !data.getMap().getNeighbors(t, Matches.TerritoryIsWater).isEmpty()) {

        // Determine sea value based on nearby convoy production
        double nearbySeaProductionValue = 0;
        final Set<Territory> nearbySeaTerritories =
            data.getMap()
                .getNeighbors(t, 4, ProMatches.territoryCanMoveSeaUnits(player, data, true));
        final List<Territory> nearbyEnemySeaTerritories =
            Match.getMatches(
                nearbySeaTerritories,
                ProMatches.territoryIsEnemyOrCantBeHeld(player, data, territoriesThatCantBeHeld));
        for (final Territory nearbyEnemySeaTerritory : nearbyEnemySeaTerritories) {
          final Route route =
              data.getMap()
                  .getRoute_IgnoreEnd(
                      t,
                      nearbyEnemySeaTerritory,
                      ProMatches.territoryCanMoveSeaUnits(player, data, true));
          if (route == null || MoveValidator.validateCanal(route, null, player, data) != null) {
            continue;
          }
          final int distance = route.numberOfSteps();
          if (distance > 0) {
            nearbySeaProductionValue +=
                TerritoryAttachment.getProduction(nearbyEnemySeaTerritory) / Math.pow(2, distance);
          }
        }

        // Determine sea value based on nearby enemy sea units
        double nearbyEnemySeaUnitValue = 0;
        final List<Territory> nearbyEnemySeaUnitTerritories =
            Match.getMatches(nearbySeaTerritories, Matches.territoryHasEnemyUnits(player, data));
        for (final Territory nearbyEnemySeaTerritory : nearbyEnemySeaUnitTerritories) {
          final Route route =
              data.getMap()
                  .getRoute_IgnoreEnd(
                      t,
                      nearbyEnemySeaTerritory,
                      ProMatches.territoryCanMoveSeaUnits(player, data, true));
          if (route == null || MoveValidator.validateCanal(route, null, player, data) != null) {
            continue;
          }
          final int distance = route.numberOfSteps();
          if (distance > 0) {
            nearbyEnemySeaUnitValue +=
                nearbyEnemySeaTerritory.getUnits().countMatches(Matches.unitIsEnemyOf(data, player))
                    / Math.pow(2, distance);
          }
        }

        // Set final values
        final double value = 100 * nearbySeaProductionValue + nearbyEnemySeaUnitValue;
        territoryValueMap.put(t, value);
      } else if (t.isWater()) {
        territoryValueMap.put(t, 0.0);
      }
    }
    return territoryValueMap;
  }
 private void fireRocket(
     final PlayerID player,
     final Territory attackedTerritory,
     final IDelegateBridge bridge,
     final Territory attackFrom) {
   final GameData data = bridge.getData();
   final PlayerID attacked = attackedTerritory.getOwner();
   final Resource PUs = data.getResourceList().getResource(Constants.PUS);
   final boolean DamageFromBombingDoneToUnits =
       isDamageFromBombingDoneToUnitsInsteadOfTerritories(data);
   // unit damage vs territory damage
   final Collection<Unit> enemyUnits =
       attackedTerritory
           .getUnits()
           .getMatches(
               new CompositeMatchAnd<Unit>(
                   Matches.enemyUnit(player, data), Matches.unitIsBeingTransported().invert()));
   final Collection<Unit> enemyTargetsTotal =
       Match.getMatches(
           enemyUnits, Matches.UnitIsAtMaxDamageOrNotCanBeDamaged(attackedTerritory).invert());
   final Collection<Unit> targets = new ArrayList<Unit>();
   final Collection<Unit> rockets;
   // attackFrom could be null if WW2V1
   if (attackFrom == null) rockets = null;
   else
     rockets =
         new ArrayList<Unit>(
             Match.getMatches(attackFrom.getUnits().getUnits(), rocketMatch(player, data)));
   final int numberOfAttacks =
       (rockets == null
           ? 1
           : Math.min(
               TechAbilityAttachment.getRocketNumberPerTerritory(player, data),
               TechAbilityAttachment.getRocketDiceNumber(rockets, data)));
   if (numberOfAttacks <= 0) return;
   final String transcript;
   if (DamageFromBombingDoneToUnits) {
     // TODO: rockets needs to be completely redone to allow for multiple rockets to fire at
     // different targets, etc etc.
     final HashSet<UnitType> legalTargetsForTheseRockets = new HashSet<UnitType>();
     if (rockets == null)
       legalTargetsForTheseRockets.addAll(data.getUnitTypeList().getAllUnitTypes());
     else {
       // a hack for now, we let the rockets fire at anyone who could be targetted by any rocket
       for (final Unit r : rockets) {
         legalTargetsForTheseRockets.addAll(
             UnitAttachment.get(r.getType()).getBombingTargets(data));
       }
     }
     final Collection<Unit> enemyTargets =
         Match.getMatches(enemyTargetsTotal, Matches.unitIsOfTypes(legalTargetsForTheseRockets));
     if (enemyTargets.isEmpty()) return; // TODO: this sucks
     Unit target = null;
     if (enemyTargets.size() == 1) target = enemyTargets.iterator().next();
     else {
       while (target == null) {
         final ITripleaPlayer iplayer = (ITripleaPlayer) bridge.getRemotePlayer(player);
         target = iplayer.whatShouldBomberBomb(attackedTerritory, enemyTargets, rockets);
       }
     }
     if (target == null)
       throw new IllegalStateException("No Targets in " + attackedTerritory.getName());
     targets.add(target);
   }
   final boolean doNotUseBombingBonus =
       !games.strategy.triplea.Properties.getUseBombingMaxDiceSidesAndBonus(data)
           || rockets == null;
   int cost = 0;
   if (!games.strategy.triplea.Properties.getLL_DAMAGE_ONLY(data)) {
     if (doNotUseBombingBonus || rockets == null) {
       // no low luck, and no bonus, so just roll based on the map's dice sides
       final int[] rolls =
           bridge.getRandom(
               data.getDiceSides(),
               numberOfAttacks,
               player,
               DiceType.BOMBING,
               "Rocket fired by " + player.getName() + " at " + attacked.getName());
       for (final int r : rolls) {
         cost += r + 1; // we are zero based
       }
       transcript =
           "Rockets "
               + (attackFrom == null ? "" : "in " + attackFrom.getName())
               + " roll: "
               + MyFormatter.asDice(rolls);
     } else {
       // we must use bombing bonus
       int highestMaxDice = 0;
       int highestBonus = 0;
       final int diceSides = data.getDiceSides();
       for (final Unit u : rockets) {
         final UnitAttachment ua = UnitAttachment.get(u.getType());
         int maxDice = ua.getBombingMaxDieSides();
         int bonus = ua.getBombingBonus();
         // both could be -1, meaning they were not set. if they were not set, then we use default
         // dice sides for the map, and zero for the bonus.
         if (maxDice < 0) maxDice = diceSides;
         if (bonus < 0) bonus = 0;
         // we only roll once for rockets, so if there are other rockets here we just roll for the
         // best rocket
         if ((bonus + ((maxDice + 1) / 2)) > (highestBonus + ((highestMaxDice + 1) / 2))) {
           highestMaxDice = maxDice;
           highestBonus = bonus;
         }
       }
       // now we roll, or don't if there is nothing to roll.
       if (highestMaxDice > 0) {
         final int[] rolls =
             bridge.getRandom(
                 highestMaxDice,
                 numberOfAttacks,
                 player,
                 DiceType.BOMBING,
                 "Rocket fired by " + player.getName() + " at " + attacked.getName());
         for (int i = 0; i < rolls.length; i++) {
           final int r = rolls[i] + highestBonus;
           rolls[i] = r;
           cost += r + 1; // we are zero based
         }
         transcript =
             "Rockets "
                 + (attackFrom == null ? "" : "in " + attackFrom.getName())
                 + " roll: "
                 + MyFormatter.asDice(rolls);
       } else {
         cost = highestBonus * numberOfAttacks;
         transcript =
             "Rockets "
                 + (attackFrom == null ? "" : "in " + attackFrom.getName())
                 + " do "
                 + highestBonus
                 + " damage for each rocket";
       }
     }
   } else {
     if (doNotUseBombingBonus || rockets == null) {
       // no bonus, so just roll based on the map's dice sides, but modify for LL
       final int maxDice = (data.getDiceSides() + 1) / 3;
       final int bonus = (data.getDiceSides() + 1) / 3;
       final int[] rolls =
           bridge.getRandom(
               maxDice,
               numberOfAttacks,
               player,
               DiceType.BOMBING,
               "Rocket fired by " + player.getName() + " at " + attacked.getName());
       for (int i = 0; i < rolls.length; i++) {
         final int r = rolls[i] + bonus;
         rolls[i] = r;
         cost += r + 1; // we are zero based
       }
       transcript =
           "Rockets "
               + (attackFrom == null ? "" : "in " + attackFrom.getName())
               + " roll: "
               + MyFormatter.asDice(rolls);
     } else {
       int highestMaxDice = 0;
       int highestBonus = 0;
       final int diceSides = data.getDiceSides();
       for (final Unit u : rockets) {
         final UnitAttachment ua = UnitAttachment.get(u.getType());
         int maxDice = ua.getBombingMaxDieSides();
         int bonus = ua.getBombingBonus();
         // both could be -1, meaning they were not set. if they were not set, then we use default
         // dice sides for the map, and zero for the bonus.
         if (maxDice < 0 || doNotUseBombingBonus) maxDice = diceSides;
         if (bonus < 0 || doNotUseBombingBonus) bonus = 0;
         // now, regardless of whether they were set or not, we have to apply "low luck" to them,
         // meaning in this case that we reduce the luck by 2/3.
         if (maxDice >= 5) {
           bonus += (maxDice + 1) / 3;
           maxDice = (maxDice + 1) / 3;
         }
         // we only roll once for rockets, so if there are other rockets here we just roll for the
         // best rocket
         if ((bonus + ((maxDice + 1) / 2)) > (highestBonus + ((highestMaxDice + 1) / 2))) {
           highestMaxDice = maxDice;
           highestBonus = bonus;
         }
       }
       // now we roll, or don't if there is nothing to roll.
       if (highestMaxDice > 0) {
         final int[] rolls =
             bridge.getRandom(
                 highestMaxDice,
                 numberOfAttacks,
                 player,
                 DiceType.BOMBING,
                 "Rocket fired by " + player.getName() + " at " + attacked.getName());
         for (int i = 0; i < rolls.length; i++) {
           final int r = rolls[i] + highestBonus;
           rolls[i] = r;
           cost += r + 1; // we are zero based
         }
         transcript =
             "Rockets "
                 + (attackFrom == null ? "" : "in " + attackFrom.getName())
                 + " roll: "
                 + MyFormatter.asDice(rolls);
       } else {
         cost = highestBonus * numberOfAttacks;
         transcript =
             "Rockets "
                 + (attackFrom == null ? "" : "in " + attackFrom.getName())
                 + " do "
                 + highestBonus
                 + " damage for each rocket";
       }
     }
   }
   int territoryProduction = TerritoryAttachment.getProduction(attackedTerritory);
   if (DamageFromBombingDoneToUnits && !targets.isEmpty()) {
     // we are doing damage to 'target', not to the territory
     final Unit target = targets.iterator().next();
     // UnitAttachment ua = UnitAttachment.get(target.getType());
     final TripleAUnit taUnit = (TripleAUnit) target;
     final int damageLimit = taUnit.getHowMuchMoreDamageCanThisUnitTake(target, attackedTerritory);
     cost = Math.max(0, Math.min(cost, damageLimit));
     final int totalDamage = taUnit.getUnitDamage() + cost;
     // Record production lost
     // DelegateFinder.moveDelegate(data).PUsLost(attackedTerritory, cost);
     // apply the hits to the targets
     final IntegerMap<Unit> damageMap = new IntegerMap<Unit>();
     damageMap.put(target, totalDamage);
     bridge.addChange(ChangeFactory.bombingUnitDamage(damageMap));
     // attackedTerritory.notifyChanged();
   }
   // in WW2V2, limit rocket attack cost to production value of factory.
   else if (isWW2V2(data) || isLimitRocketDamageToProduction(data)) {
     // If we are limiting total PUs lost then take that into account
     if (isPUCap(data) || isLimitRocketDamagePerTurn(data)) {
       final int alreadyLost = DelegateFinder.moveDelegate(data).PUsAlreadyLost(attackedTerritory);
       territoryProduction -= alreadyLost;
       territoryProduction = Math.max(0, territoryProduction);
     }
     if (cost > territoryProduction) {
       cost = territoryProduction;
     }
   }
   // Record the PUs lost
   DelegateFinder.moveDelegate(data).PUsLost(attackedTerritory, cost);
   if (DamageFromBombingDoneToUnits && !targets.isEmpty()) {
     getRemote(bridge)
         .reportMessage(
             "Rocket attack in "
                 + attackedTerritory.getName()
                 + " does "
                 + cost
                 + " damage to "
                 + targets.iterator().next(),
             "Rocket attack in "
                 + attackedTerritory.getName()
                 + " does "
                 + cost
                 + " damage to "
                 + targets.iterator().next());
     bridge
         .getHistoryWriter()
         .startEvent(
             "Rocket attack in "
                 + attackedTerritory.getName()
                 + " does "
                 + cost
                 + " damage to "
                 + targets.iterator().next());
   } else {
     cost *= Properties.getPU_Multiplier(data);
     getRemote(bridge)
         .reportMessage(
             "Rocket attack in " + attackedTerritory.getName() + " costs:" + cost,
             "Rocket attack in " + attackedTerritory.getName() + " costs:" + cost);
     // Trying to remove more PUs than the victim has is A Bad Thing[tm]
     final int availForRemoval = attacked.getResources().getQuantity(PUs);
     if (cost > availForRemoval) cost = availForRemoval;
     final String transcriptText =
         attacked.getName() + " lost " + cost + " PUs to rocket attack by " + player.getName();
     bridge.getHistoryWriter().startEvent(transcriptText);
     final Change rocketCharge = ChangeFactory.changeResourcesChange(attacked, PUs, -cost);
     bridge.addChange(rocketCharge);
   }
   bridge
       .getHistoryWriter()
       .addChildToEvent(transcript, rockets == null ? null : new ArrayList<Unit>(rockets));
   // this is null in WW2V1
   if (attackFrom != null) {
     if (rockets != null && !rockets.isEmpty()) {
       // TODO: only a certain number fired...
       final Change change =
           ChangeFactory.markNoMovementChange(Collections.singleton(rockets.iterator().next()));
       bridge.addChange(change);
     } else {
       throw new IllegalStateException("No rockets?" + attackFrom.getUnits().getUnits());
     }
   }
   // kill any units that can die if they have reached max damage (veqryn)
   if (Match.someMatch(targets, Matches.UnitCanDieFromReachingMaxDamage)) {
     final List<Unit> unitsCanDie =
         Match.getMatches(targets, Matches.UnitCanDieFromReachingMaxDamage);
     unitsCanDie.retainAll(
         Match.getMatches(
             unitsCanDie, Matches.UnitIsAtMaxDamageOrNotCanBeDamaged(attackedTerritory)));
     if (!unitsCanDie.isEmpty()) {
       // targets.removeAll(unitsCanDie);
       final Change removeDead = ChangeFactory.removeUnits(attackedTerritory, unitsCanDie);
       final String transcriptText =
           MyFormatter.unitsToText(unitsCanDie) + " lost in " + attackedTerritory.getName();
       bridge.getHistoryWriter().addChildToEvent(transcriptText, unitsCanDie);
       bridge.addChange(removeDead);
     }
   }
   // play a sound
   if (cost > 0)
     bridge
         .getSoundChannelBroadcaster()
         .playSoundForAll(SoundPath.CLIP_BOMBING_ROCKET, player.getName());
 }