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;
  }