~ubuntu-branches/ubuntu/precise/triplea/precise

« back to all changes in this revision

Viewing changes to src/games/strategy/triplea/Dynamix_AI/DUtils.java

  • Committer: Package Import Robot
  • Author(s): Scott Howard
  • Date: 2011-11-11 21:40:11 UTC
  • Revision ID: package-import@ubuntu.com-20111111214011-sehf2rwat36o2xqf
Tags: upstream-1.3.2.2
ImportĀ upstreamĀ versionĀ 1.3.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This program is free software; you can redistribute it and/or modify
 
3
 * it under the terms of the GNU General Public License as published by
 
4
 * the Free Software Foundation; either version 2 of the License, or
 
5
 * (at your option) any later version.
 
6
 * This program is distributed in the hope that it will be useful,
 
7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
8
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
9
 * GNU General Public License for more details.
 
10
 * You should have received a copy of the GNU General Public License
 
11
 * along with this program; if not, write to the Free Software
 
12
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
13
 */
 
14
 
 
15
package games.strategy.triplea.Dynamix_AI;
 
16
 
 
17
import games.strategy.triplea.Dynamix_AI.Group.UnitGroup;
 
18
import games.strategy.engine.data.GameData;
 
19
import games.strategy.engine.data.GameMap;
 
20
import games.strategy.engine.data.PlayerID;
 
21
import games.strategy.engine.data.ProductionFrontier;
 
22
import games.strategy.engine.data.ProductionRule;
 
23
import games.strategy.engine.data.Resource;
 
24
import games.strategy.engine.data.Route;
 
25
import games.strategy.engine.data.Territory;
 
26
import games.strategy.engine.data.Unit;
 
27
import games.strategy.engine.data.UnitType;
 
28
import games.strategy.triplea.Constants;
 
29
import games.strategy.triplea.Dynamix_AI.CommandCenter.CachedCalculationCenter;
 
30
import games.strategy.triplea.Dynamix_AI.CommandCenter.FactoryCenter;
 
31
import games.strategy.triplea.Dynamix_AI.CommandCenter.GlobalCenter;
 
32
import games.strategy.triplea.Dynamix_AI.CommandCenter.StrategyCenter;
 
33
import games.strategy.triplea.Dynamix_AI.CommandCenter.TacticalCenter;
 
34
import games.strategy.triplea.Dynamix_AI.CommandCenter.ThreatInvalidationCenter;
 
35
import games.strategy.triplea.Dynamix_AI.Group.PurchaseGroup;
 
36
import games.strategy.triplea.Dynamix_AI.Others.CM_Task;
 
37
import games.strategy.triplea.Dynamix_AI.Others.CM_TaskType;
 
38
import games.strategy.triplea.Dynamix_AI.Others.NCM_Call;
 
39
import games.strategy.triplea.Dynamix_AI.Others.NCM_CallType;
 
40
import games.strategy.triplea.Dynamix_AI.Others.NCM_Task;
 
41
import games.strategy.triplea.Dynamix_AI.Others.NCM_TaskType;
 
42
import games.strategy.triplea.Dynamix_AI.Others.PhaseType;
 
43
import games.strategy.triplea.Dynamix_AI.Others.StrategyType;
 
44
import games.strategy.triplea.Dynamix_AI.UI.UI;
 
45
import games.strategy.triplea.Properties;
 
46
import games.strategy.triplea.TripleAUnit;
 
47
import games.strategy.triplea.attatchments.RulesAttachment;
 
48
import games.strategy.triplea.attatchments.TerritoryAttachment;
 
49
import games.strategy.triplea.attatchments.UnitAttachment;
 
50
import games.strategy.triplea.delegate.Matches;
 
51
import games.strategy.triplea.delegate.MustFightBattle;
 
52
import games.strategy.triplea.formatter.MyFormatter;
 
53
import games.strategy.triplea.oddsCalculator.ta.AggregateResults;
 
54
import games.strategy.triplea.oddsCalculator.ta.BattleResults;
 
55
import games.strategy.util.CompositeMatchAnd;
 
56
import games.strategy.util.CompositeMatchOr;
 
57
import games.strategy.util.IntegerMap;
 
58
import games.strategy.util.InverseMatch;
 
59
import games.strategy.util.Match;
 
60
import java.util.Arrays;
 
61
import java.util.Collection;
 
62
import java.util.Collections;
 
63
import java.util.Comparator;
 
64
import java.util.HashMap;
 
65
import java.util.ArrayList;
 
66
import java.util.HashSet;
 
67
import java.util.Iterator;
 
68
import java.util.List;
 
69
import java.util.Map;
 
70
import java.util.Random;
 
71
import java.util.TreeMap;
 
72
import java.util.logging.Level;
 
73
import javax.swing.SwingUtilities;
 
74
 
 
75
/**
 
76
 *
 
77
 * @author Stephen
 
78
 */
 
79
public class DUtils
 
80
{
 
81
    public static float GetAttackScoreOfUnits(Collection<Unit> units)
 
82
    {
 
83
        float result = 0;
 
84
 
 
85
        for(Unit unit : units)
 
86
        {
 
87
            UnitAttachment ua = UnitAttachment.get(unit.getType());
 
88
            PlayerID owner = unit.getOwner();
 
89
            float unitAttack = 1;
 
90
            unitAttack += ua.getAttack(owner);
 
91
            if(ua.isTwoHit())
 
92
                unitAttack = unitAttack * 2.0F;
 
93
            if(ua.getAttackRolls(owner) > 1)
 
94
                unitAttack = unitAttack * (float)ua.getAttackRolls(owner);
 
95
 
 
96
            result += unitAttack;
 
97
        }
 
98
 
 
99
        return result;
 
100
    }
 
101
    public static float GetDefenseScoreOfUnits(Collection<Unit> units)
 
102
    {
 
103
        float result = 0;
 
104
 
 
105
        for(Unit unit : units)
 
106
        {
 
107
            UnitAttachment ua = UnitAttachment.get(unit.getType());
 
108
            if(ua.isAA())
 
109
                continue;
 
110
            PlayerID owner = unit.getOwner();
 
111
            float unitDefense = 1;
 
112
            unitDefense += ua.getDefense(owner);
 
113
            if(ua.isTwoHit())
 
114
                unitDefense = unitDefense * 2.0F;
 
115
 
 
116
            result += unitDefense;
 
117
        }
 
118
 
 
119
        return result;
 
120
    }
 
121
    public static float GetValueOfUnits(Collection<Unit> units)
 
122
    {
 
123
        float result = 0;
 
124
 
 
125
        for(Unit unit : units)
 
126
        {
 
127
            UnitAttachment ua = UnitAttachment.get(unit.getUnitType());
 
128
            result += 1;
 
129
            result += GetAttackStrengthOfUnit(unit);
 
130
            result += GetDefenseStrengthOfUnit(unit);
 
131
            if(ua.isAir())
 
132
                result += 3; //Air units can be retreated to safe places, and stuff
 
133
        }
 
134
 
 
135
        return result;
 
136
    }
 
137
    public static List<Unit> GetUnitsOnMap(GameData data)
 
138
    {        
 
139
        return GetUnitsMatchingXInTerritoriesMatchingY(data, Match.ALWAYS_MATCH, Match.ALWAYS_MATCH);
 
140
    }
 
141
    public static List<Unit> GetUnitsMatchingXOnMap(GameData data, Match<Unit> unitMatch)
 
142
    {
 
143
        return GetUnitsMatchingXInTerritoriesMatchingY(data, unitMatch, Match.ALWAYS_MATCH);
 
144
    }
 
145
    public static List<Unit> GetUnitsMatchingXInTerritoriesMatchingY(GameData data, Match<Unit> unitMatch, Match<Territory> terMatch)
 
146
    {        
 
147
        return GetUnitsMatchingXInTerritories(GetTerritoriesMatching(data, terMatch), unitMatch);
 
148
    }
 
149
    public static List<Unit> GetUnitsInTerritories(List<Territory> territories)
 
150
    {
 
151
        return GetUnitsMatchingXInTerritories(territories, Match.ALWAYS_MATCH);
 
152
    }
 
153
    public static List<Unit> GetUnitsMatchingXInTerritories(List<Territory> territories, Match<Unit> unitMatch)
 
154
    {
 
155
        List<Unit> result = new ArrayList<Unit>();
 
156
        for(Territory ter : territories)
 
157
            result.addAll(ter.getUnits().getMatches(unitMatch));
 
158
        return result;
 
159
    }
 
160
    public static List<Unit> GetUnitsInUGs(List<UnitGroup> ugs)
 
161
    {
 
162
        List<Unit> result = new ArrayList<Unit>();
 
163
        for(UnitGroup ug : ugs)
 
164
            result.addAll(ug.GetUnits());
 
165
        return result;
 
166
    }
 
167
    public static int GetTUVOfUnit(Unit unit, ProductionFrontier frontier, Resource resource)
 
168
    {
 
169
        int result = 0;
 
170
        for (ProductionRule rule : frontier.getRules())
 
171
        {
 
172
            if (((UnitType) rule.getResults().keySet().toArray()[0]).getName().equals(unit.getUnitType().getName()))
 
173
                result += (rule.getCosts().getInt(resource) / rule.getResults().keySet().size()); //We divide the cost by how many units we get from the purchase
 
174
        }
 
175
        return result;
 
176
    }
 
177
    public static int GetTUVOfUnit(Unit unit, Resource resource)
 
178
    {
 
179
        if(unit.getOwner().isNull())
 
180
            return GetTUVOfUnit(unit, GlobalCenter.GetMergedAndAveragedProductionFrontier(), resource);
 
181
        else
 
182
            return GetTUVOfUnit(unit, unit.getOwner().getProductionFrontier(), resource);
 
183
    }
 
184
    public static int GetTUVOfUnits(Collection<Unit> units, ProductionFrontier frontier, Resource resource)
 
185
    {
 
186
        int result = 0;
 
187
        for(Unit unit : units)
 
188
            result += GetTUVOfUnit(unit, frontier, resource);
 
189
        return result;
 
190
    }
 
191
    public static int GetTUVOfUnits(Collection<Unit> units, Resource resource)
 
192
    {
 
193
        int result = 0;
 
194
        for(Unit unit : units)
 
195
            result += GetTUVOfUnit(unit, resource);
 
196
        return result;
 
197
    }
 
198
    public static float GetDefenseStrengthOfUnit(Unit unit)
 
199
    {
 
200
        return GetDefenseScoreOfUnits(Collections.singleton(unit));
 
201
    }
 
202
    public static float GetAttackStrengthOfUnit(Unit unit)
 
203
    {
 
204
        return GetAttackScoreOfUnits(Collections.singleton(unit));
 
205
    }
 
206
    /**
 
207
     * Runs simulated battles numerous times and returns an AggregateResults object that lists the percent of times the attacker won, lost, etc.
 
208
     * @param ter - The map territory used to determine the attacking units, the defending units, and the battle site
 
209
     * @param player - The attacking player
 
210
     * @param data - The game data containing the map, units, players, etc.
 
211
     * @param runCount - How many times to simulate the battle. The more it's simulated, the more accurate the results will be
 
212
     * @param toTake - Whether the attacker needs to have a unit left over after the attack to take the territory for a battle simulation to be counted as a win
 
213
     * @return Returns an AggregateResults object that lists the percent of times the attacker won, lost, etc.
 
214
     */
 
215
    public static AggregateResults GetBattleResults(Territory ter, PlayerID player, GameData data, int runCount, boolean toTake)
 
216
    {
 
217
        List<Unit> attacking = new ArrayList<Unit>();
 
218
        List<Unit> defending = new ArrayList<Unit>();
 
219
 
 
220
        for(Unit unit : ter.getUnits().getUnits())
 
221
        {
 
222
            if(unit.getOwner().equals(player))
 
223
                attacking.add(unit);
 
224
            else if(!data.getAllianceTracker().isAllied(player, unit.getOwner()))
 
225
                defending.add(unit);
 
226
        }
 
227
 
 
228
        return GetBattleResults(attacking, defending, ter, data, runCount, toTake);
 
229
    }
 
230
    /**
 
231
     * Used when you have two or more lists of units, etc and you want all the units in one collection.
 
232
     * Usage:
 
233
     * List<Unit> unitCollection1 = GetNNEnemyUnitsThatCanReach(target);
 
234
     * List<Unit> unitCollection2 = GetNNEnemyUnitsThatCanReach(target2);
 
235
     * List<Unit> allUnits = CombineCollections(unitCollection1, unitCollection2);
 
236
     */
 
237
    public static List CombineCollections(Collection ... collections)
 
238
    {
 
239
        List result = new ArrayList();
 
240
        for(Collection collection : collections)
 
241
        {
 
242
            result.addAll(collection);
 
243
        }
 
244
        return result;
 
245
    }
 
246
    /**
 
247
     * Used when you have 1 or more collections with lists of units, etc and you want all the units in all the lists in one collection.
 
248
     * Usage:
 
249
     * Collection<List<Unit>> unitLists = units_Mapped.values();
 
250
     * List<Unit> allUnits = CombineListsInCollections(unitLists);
 
251
     */
 
252
    public static List CombineListsInCollections(Collection ... collections)
 
253
    {
 
254
        List result = new ArrayList();
 
255
        for(Collection collection : collections)
 
256
        {
 
257
            for (Object list : collection.toArray())
 
258
            {
 
259
                result.addAll((Collection)list);
 
260
            }
 
261
        }
 
262
        return result;
 
263
    }
 
264
    public static float ToFloat(int percentage)
 
265
    {
 
266
        return percentage / 100.0F;
 
267
    }
 
268
    public static List ToList(Collection collection)
 
269
    {
 
270
        return new ArrayList(collection);
 
271
    }
 
272
    public static List ToList(Object[] array)
 
273
    {
 
274
        return Arrays.asList(array);
 
275
    }
 
276
    public static Object[] ToArray(Object ... toSmashIntoArray)
 
277
    {
 
278
        return toSmashIntoArray;
 
279
    }
 
280
    /**
 
281
     * First, determines if <code>map</code> contains <code>key</code>.
 
282
     * If it does, it retrieves the value(which is expected to be a List) of <code>key</code> in <code>map</code>, adds <code>obj</code> to the list,
 
283
     * and puts the updated list back into <code>map</code> using <code>key</code> as the key.
 
284
     * If it doesn't, it creates a new list, adds <code>obj</code> to the list, and puts the new list into <code>map</code> using <code>key</code> as the key.
 
285
     */
 
286
    public static void AddObjToListValueForKeyInMap(HashMap map, Object key, Object obj)
 
287
    {
 
288
        AddObjectsToListValueForKeyInMap(map, key, Collections.singletonList(obj));
 
289
    }
 
290
    
 
291
    /**
 
292
     * Same as AddObjToListValueForKeyInMap except that it does adds a list of objects instead of one object.
 
293
     */
 
294
    public static void AddObjectsToListValueForKeyInMap(HashMap map, Object key, List objs)
 
295
    {
 
296
        if (map.containsKey(key))
 
297
        {
 
298
            List<Object> newList = (List<Object>) map.get(key);
 
299
            newList.addAll(objs);
 
300
            map.put(key, newList);
 
301
        }
 
302
        else
 
303
        {
 
304
            List<Object> newList = new ArrayList<Object>();
 
305
            newList.addAll(objs);
 
306
            map.put(key, newList);
 
307
        }
 
308
    }
 
309
 
 
310
    /**
 
311
     * Same as AddObjectsToListValueForKeyInMap except that this method assumes the hashmap has HashSet values instead of List values
 
312
     */
 
313
    public static void AddObjectsToHashSetValueForKeyInMap(HashMap map, Object key, List objs)
 
314
    {
 
315
        if (map.containsKey(key))
 
316
        {
 
317
            HashSet<Object> newList = (HashSet<Object>) map.get(key);
 
318
            newList.addAll(objs);
 
319
            map.put(key, newList);
 
320
        }
 
321
        else
 
322
        {
 
323
            HashSet<Object> newList = new HashSet<Object>();
 
324
            newList.addAll(objs);
 
325
            map.put(key, newList);
 
326
        }
 
327
    }
 
328
    
 
329
    /**
 
330
     * (GetHitPointsScoreOfUnits)
 
331
     */
 
332
    public static int GetHPScoreOfUnits(List<Unit> units)
 
333
    {
 
334
        int result = 0;
 
335
        for (Unit unit : units)
 
336
        {
 
337
            result++;
 
338
            if (UnitAttachment.get(unit.getType()).isTwoHit())
 
339
            {
 
340
                result++;
 
341
            }
 
342
        }
 
343
        return result;
 
344
    }
 
345
    public static int GetAttackStrengthOfUnits(List<Unit> units)
 
346
    {
 
347
        int result = 0;
 
348
        for (Unit unit : units)
 
349
        {
 
350
            UnitAttachment ua = UnitAttachment.get(unit.getType());
 
351
            result += (ua.getAttack(unit.getOwner()) * (ua.isTwoHit() ? 2 : 1));
 
352
        }
 
353
        return result;
 
354
    }
 
355
    public static int GetDefenseStrengthOfUnits(List<Unit> units)
 
356
    {
 
357
        int result = 0;
 
358
        for (Unit unit : units)
 
359
        {
 
360
            UnitAttachment ua = UnitAttachment.get(unit.getType());
 
361
            result += (ua.getDefense(unit.getOwner()) * (ua.isTwoHit() ? 2 : 1));
 
362
        }
 
363
        return result;
 
364
    }
 
365
    public static boolean CanPlayerPlaceAnywhere(GameData data, PlayerID player)
 
366
    {
 
367
        if(Properties.getPlaceInAnyTerritory(data))
 
368
        {
 
369
                RulesAttachment ra = (RulesAttachment) player.getAttachment(Constants.RULES_ATTATCHMENT_NAME);
 
370
                if(ra != null && ra.getPlacementAnyTerritory())
 
371
                        return true;
 
372
        }
 
373
 
 
374
        return false;
 
375
    }
 
376
    public static List<PlayerID> GetAliveEnemyPlayers(GameData data, PlayerID player)
 
377
    {
 
378
        List<PlayerID> result = new ArrayList<PlayerID>();
 
379
        for (PlayerID enemy : data.getPlayerList().getPlayers())
 
380
        {
 
381
            if (!data.getAllianceTracker().isAllied(player, enemy) && TerritoryAttachment.getAllCurrentlyOwnedCapitals(enemy, data).size() > 0)
 
382
                result.add(enemy);
 
383
        }
 
384
        return result;
 
385
    }
 
386
    public static List<PlayerID> GetEnemyPlayers(GameData data, PlayerID us)
 
387
    {
 
388
        List<PlayerID> result = new ArrayList<PlayerID>();
 
389
        for (PlayerID player : data.getPlayerList().getPlayers())
 
390
        {
 
391
            if (!data.getAllianceTracker().isAllied(us, player))
 
392
                result.add(player);
 
393
        }
 
394
        return result;
 
395
    }
 
396
    public static List<PlayerID> GetAlliedPlayersIncludingUs(GameData data, PlayerID us)
 
397
    {
 
398
        List<PlayerID> result = new ArrayList<PlayerID>();
 
399
        for (PlayerID player : data.getPlayerList().getPlayers())
 
400
        {
 
401
            if (data.getAllianceTracker().isAllied(us, player))
 
402
                result.add(player);
 
403
        }
 
404
        return result;
 
405
    }
 
406
    /**
 
407
     * Used to find the x closest routes between two territories that are of similar length.
 
408
     * This can be used to find the friendly/enemy units in the general area between two countries to determine how many units to send in one direction.
 
409
     */
 
410
    public static List<Route> GetXClosestSimiliarLengthLandRoutesBetweenTers(GameData data, int maxNumberOfRoutes, Territory ter1, Territory ter2)
 
411
    {
 
412
        List<Route> result = new ArrayList<Route>();
 
413
        List<Territory> allRouteTers = new ArrayList<Territory>();
 
414
 
 
415
        int uniqueRoutesFound = 0;
 
416
        while (uniqueRoutesFound < maxNumberOfRoutes)
 
417
        {
 
418
            Route route = data.getMap().getRoute_IgnoreEnd(ter1, ter2, new CompositeMatchAnd<Territory>(Matches.territoryIsNotInList(allRouteTers), Matches.TerritoryIsLand));
 
419
            if (route != null)
 
420
            {
 
421
                if (!result.isEmpty() && !(result.get(0).getLength() + 2 >= route.getLength() && result.get(0).getLength() - 2 <= route.getLength())) //If this route is not similar in length to the first route, break loop
 
422
                    break;
 
423
 
 
424
                result.add(route);
 
425
                uniqueRoutesFound++;
 
426
                for (Territory ter : route.getTerritories())
 
427
                {
 
428
                    if (ter.getName().equals(route.getStart().getName()))
 
429
                        continue;
 
430
                    if (ter.getName().equals(route.getEnd().getName()))
 
431
                        continue;
 
432
                    if(!allRouteTers.contains(ter))
 
433
                        allRouteTers.add(ter);
 
434
                }
 
435
            }
 
436
            else
 
437
                break;
 
438
        }
 
439
 
 
440
        return result;
 
441
    }
 
442
    public static Unit GetRandomUnitForPlayerMatching(PlayerID player, Match<Unit> match)
 
443
    {
 
444
        if (player == null)
 
445
            player = PlayerID.NULL_PLAYERID;
 
446
 
 
447
        ProductionFrontier frontier = player.getProductionFrontier();
 
448
        if(frontier == null)
 
449
            frontier = GlobalCenter.GetMergedAndAveragedProductionFrontier();
 
450
        List<ProductionRule> rules = new ArrayList<ProductionRule>(frontier.getRules());
 
451
        Collections.shuffle(rules);
 
452
        for (ProductionRule rule : rules)
 
453
        {
 
454
            Unit unit = ((UnitType) rule.getResults().keySet().toArray()[0]).create(player);
 
455
            if (match.match(unit))
 
456
                return unit;
 
457
        }
 
458
        return null;
 
459
    }
 
460
    public static List<UnitType> GetAllPurchasableUnitTypesForPlayer(PlayerID player, Match<Unit> match)
 
461
    {
 
462
        List<UnitType> result = new ArrayList<UnitType>();
 
463
 
 
464
        if (player == null)
 
465
            player = PlayerID.NULL_PLAYERID;
 
466
 
 
467
        ProductionFrontier frontier = player.getProductionFrontier();
 
468
        if(frontier == null)
 
469
            frontier = GlobalCenter.GetMergedAndAveragedProductionFrontier();
 
470
        List<ProductionRule> rules = new ArrayList<ProductionRule>(frontier.getRules());
 
471
        Collections.shuffle(rules);
 
472
        for (ProductionRule rule : rules)
 
473
        {
 
474
            Unit unit = ((UnitType) rule.getResults().keySet().toArray()[0]).create(player);
 
475
            if (match.match(unit))
 
476
                result.add(unit.getUnitType());
 
477
        }
 
478
 
 
479
        return result;
 
480
    }
 
481
    public static List<Unit> CreateDefendUnitsTillTakeoverChanceIsLessThanX(Collection<Unit> attackers, Collection<Unit> alreadyDefending, GameData data, Territory testTer, float maxChance)
 
482
    {
 
483
        PlayerID defender = testTer.getOwner();
 
484
        if(alreadyDefending.size() > 0)
 
485
            defender = alreadyDefending.iterator().next().getOwner();
 
486
        List<Unit> result = new ArrayList<Unit>();
 
487
        AggregateResults lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 50, true);
 
488
        while (lastResults.getAttackerWinPercent() > maxChance)
 
489
        {
 
490
            for (UnitType ut : GlobalCenter.AllMapUnitTypes)
 
491
            {
 
492
                lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 1, true);
 
493
 
 
494
                result.add(ut.create(defender));
 
495
                if(lastResults.getAttackerWinPercent() <= maxChance)
 
496
                    break;
 
497
            }
 
498
        }
 
499
        lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 50, true);
 
500
        while (lastResults.getAttackerWinPercent() > maxChance)
 
501
        {
 
502
            for (UnitType ut : GlobalCenter.AllMapUnitTypes)
 
503
            {
 
504
                lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 5, true);
 
505
 
 
506
                result.add(ut.create(defender));
 
507
                if(lastResults.getAttackerWinPercent() <= maxChance)
 
508
                    break;
 
509
            }
 
510
        }
 
511
        return result;
 
512
    }
 
513
    public static List<Unit> MultiplyDefenderUnitsTillTakeoverChanceIsLessThanX(Collection<Unit> attackers, Collection<Unit> defenders, GameData data, Territory testTer, float maxChance)
 
514
    {
 
515
        if (Match.getMatches(defenders, new CompositeMatchAnd<Unit>(Matches.UnitIsNotAA, Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1))).isEmpty())
 
516
        {
 
517
            Match<Unit> randUnitMatch;
 
518
            if(testTer.isWater())
 
519
                randUnitMatch = new CompositeMatchAnd<Unit>(DUtils.CompMatchOr(Matches.UnitIsSea, Matches.UnitIsAir));
 
520
            else
 
521
                randUnitMatch = new CompositeMatchAnd<Unit>(Matches.UnitIsLand, Matches.UnitIsNotAA, Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1));
 
522
            Unit randUnit = GetRandomUnitForPlayerMatching(testTer.getOwner(), randUnitMatch);
 
523
            if(randUnit == null)
 
524
                return new ArrayList<Unit>();
 
525
            defenders.add(randUnit);
 
526
        }
 
527
        PlayerID defender = testTer.getOwner();
 
528
        if(defenders.size() > 0)
 
529
            defender = defenders.iterator().next().getOwner();
 
530
        List<Unit> result = new ArrayList<Unit>();
 
531
        AggregateResults lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 50, true);
 
532
        while (lastResults.getAttackerWinPercent() > maxChance)
 
533
        {
 
534
            for (Unit unit : defenders)
 
535
            {
 
536
                lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 1, true);
 
537
 
 
538
                result.add(unit.getUnitType().create(defender));
 
539
                if(lastResults.getAttackerWinPercent() <= maxChance)
 
540
                    break;
 
541
            }
 
542
        }
 
543
        lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 50, true);
 
544
        while (lastResults.getAttackerWinPercent() > maxChance)
 
545
        {
 
546
            for (Unit unit : defenders)
 
547
            {
 
548
                lastResults = DUtils.GetBattleResults(attackers, result, testTer, data, 5, true);
 
549
 
 
550
                result.add(unit.getUnitType().create(defender));
 
551
                if (lastResults.getAttackerWinPercent() <= maxChance)
 
552
                    break;
 
553
            }
 
554
        }
 
555
        return result;
 
556
    }
 
557
    
 
558
    /**
 
559
     * this can return null, if either ters is empty or none of the territories match
 
560
     */
 
561
    public static Territory GetRandomTerritoryMatchingXInList(Collection<Territory> ters, Match<Territory> match)
 
562
    {
 
563
        List<Territory> list = new ArrayList<Territory>();
 
564
        for (Territory ter : ters)
 
565
        {
 
566
            if (!match.match(ter))
 
567
                continue;
 
568
 
 
569
            list.add(ter);
 
570
        }
 
571
        if (list.isEmpty())
 
572
                return null;
 
573
        
 
574
        return list.get(new Random().nextInt(list.size()));
 
575
    }
 
576
    public static Match CompMatchAnd(Match ... matches)
 
577
    {
 
578
        return new CompositeMatchAnd(matches);
 
579
    }
 
580
    public static Match CompMatchOr(Match ... matches)
 
581
    {
 
582
        return new CompositeMatchOr(matches);
 
583
    }
 
584
    public static Match CompMatchAnd(List<Match> matches)
 
585
    {
 
586
        return new CompositeMatchAnd(matches);
 
587
    }
 
588
    public static Match CompMatchOr(List<Match> matches)
 
589
    {
 
590
        return new CompositeMatchOr(matches);
 
591
    }
 
592
    public static List<Territory> GetEnemyTersThatCanBeAttackedByUnitsOwnedBy(GameData data, PlayerID player)
 
593
    {
 
594
        return GetTersMatchingXThatCanBeAttackedByUnitsMatchingYInTersMatchingZ(data, player, CompMatchAnd(Matches.TerritoryIsPassableAndNotRestricted(player), CompMatchOr(DMatches.territoryIsOwnedByEnemy(data, player), Matches.territoryHasUnitsThatMatch(CompMatchAnd(Matches.unitIsEnemyOf(data, player), Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1))))), DMatches.territoryIsOwnedByEnemy(data, player).invert(), Matches.unitIsOwnedBy(player));
 
595
    }
 
596
    public static List<Territory> GetEnemyLandTersThatCanBeAttackedByLandUnitsOwnedBy(GameData data, PlayerID player)
 
597
    {
 
598
        return GetTersMatchingXThatCanBeAttackedByUnitsMatchingYInTersMatchingZ(data, player, CompMatchAnd(Matches.TerritoryIsLand, DMatches.territoryIsOwnedByEnemy(data, player)), DMatches.territoryIsOwnedByEnemy(data, player).invert(), Matches.unitIsLandAndOwnedBy(player));
 
599
    }
 
600
    public static List<Territory> GetEnemySeaTersThatCanBeAttackedByUnitsOwnedBy(GameData data, PlayerID player)
 
601
    {
 
602
        return GetTersMatchingXThatCanBeAttackedByUnitsMatchingYInTersMatchingZ(data, player, CompMatchAnd(Matches.TerritoryIsWater, Matches.territoryHasEnemyUnits(player, data)), DMatches.territoryIsOwnedByEnemy(data, player).invert(), Matches.unitIsOwnedBy(player));
 
603
    }
 
604
    public static List<Territory> GetTersMatchingXThatCanBeAttackedByUnitsMatchingYInTersMatchingZ(GameData data, PlayerID player, Match<Territory> terMatch, Match<Territory> attackFromTerMatch, Match<Unit> unitMatch)
 
605
    {
 
606
        List<Territory> result = new ArrayList<Territory>();
 
607
        for (Territory ter : data.getMap().getTerritories())
 
608
        {
 
609
            if (!terMatch.match(ter))
 
610
                continue;
 
611
 
 
612
            List<Unit> possibleAttackers = DUtils.GetUnitsMatchingXThatCanReach(data, ter, attackFromTerMatch, unitMatch);
 
613
 
 
614
            if (possibleAttackers.size() > 0)
 
615
                result.add(ter);
 
616
        }
 
617
        return result;
 
618
    }
 
619
    public static List<Territory> GetEnemyLandTersThatCanBeAttackedByLandUnitsInList(GameData data, PlayerID player, List<Unit> units, Territory territory)
 
620
    {
 
621
        List<Territory> attackLocs = DUtils.GetTersThatUnitsCanReach(data, Match.getMatches(units, Matches.UnitIsLand), territory, player, new CompositeMatchAnd<Territory>(DMatches.territoryIsOwnedByNNEnemy(data, player), Matches.TerritoryIsLand));
 
622
 
 
623
        List<Territory> result = new ArrayList<Territory>();
 
624
        for (Territory ter : attackLocs)
 
625
        {
 
626
            if (data.getAllianceTracker().isAllied(ter.getOwner(), player))
 
627
                continue;
 
628
            if(ter.isWater())
 
629
                continue;
 
630
            if(ter.getOwner().isNull())
 
631
                continue;
 
632
 
 
633
            result.add(ter);
 
634
        }
 
635
        return result;
 
636
    }
 
637
    public static List<Territory> GetLandTersThatCanBeReinforcedByUnitsOwnedBy(GameData data, PlayerID player)
 
638
    {
 
639
        return GetTersMatchingXThatCanBeReinforcedByUnitsMatchingYInTersMatchingZ(data, player, DMatches.territoryIsOwnedByXOrAlly(data, player), DMatches.territoryIsOwnedByXOrAlly(data, player), Matches.unitIsOwnedBy(player));
 
640
    }
 
641
    public static List<Territory> GetLandTersThatCanBeReinforcedByLandUnitsOwnedBy(GameData data, PlayerID player)
 
642
    {
 
643
        return GetTersMatchingXThatCanBeReinforcedByUnitsMatchingYInTersMatchingZ(data, player, DMatches.territoryIsOwnedByXOrAlly(data, player), DMatches.territoryIsOwnedByXOrAlly(data, player), Matches.unitIsLandAndOwnedBy(player));
 
644
    }
 
645
    public static List<Territory> GetTersMatchingXThatCanBeReinforcedByUnitsMatchingYInTersMatchingZ(GameData data, PlayerID player, Match<Territory> terMatch, Match<Territory> moveFromTerMatch, Match<Unit> unitMatch)
 
646
    {
 
647
        List<Territory> result = new ArrayList<Territory>();
 
648
        for (Territory ter : data.getMap().getTerritories())
 
649
        {
 
650
            if (!terMatch.match(ter))
 
651
                continue;
 
652
 
 
653
            List<Unit> possibleAttackers = DUtils.GetUnitsMatchingXThatCanReach(data, ter, moveFromTerMatch, unitMatch);
 
654
 
 
655
            if (possibleAttackers.size() > 0)
 
656
                result.add(ter);
 
657
        }
 
658
        return result;
 
659
    }
 
660
    public static int GetProduction_PlusExtra(Territory ter)
 
661
    {
 
662
        if (ter == null)
 
663
            return 0;
 
664
        GameData data = ter.getData();
 
665
        int result = TerritoryAttachment.get(ter).getProduction();
 
666
        if (DMatches.territoryIsCapitalAndOwnedByEnemy(data, GlobalCenter.CurrentPlayer).match(ter))
 
667
        {
 
668
            int puGainIfWeConquer = 0;
 
669
            for (PlayerID enemy : data.getPlayerList().getPlayers())
 
670
            {
 
671
                List<Territory> enemyCapList = DUtils.GetAllOurCaps_ThatWeOwn(data, enemy);
 
672
                if (enemyCapList.size() == 1 && enemyCapList.get(0).equals(ter)) //If the enemy only has one cap left, and it's the ter we're checking
 
673
                    puGainIfWeConquer += enemy.getResources().getQuantity(GlobalCenter.GetPUResource());
 
674
            }
 
675
            result += puGainIfWeConquer;
 
676
        }
 
677
        return result;
 
678
    }
 
679
    public static int GetCheckedUnitProduction(Territory ter)
 
680
    {
 
681
        if(ter.getOwner().getRepairFrontier() != null)
 
682
            return TerritoryAttachment.get(ter).getUnitProduction();
 
683
        else
 
684
            return TerritoryAttachment.get(ter).getProduction();
 
685
    }
 
686
    public static List<Territory> SortTerritoriesByNNEnemyNeighbors_A(List<Territory> list, final GameData data, final PlayerID player)
 
687
    {
 
688
        return DSorting.SortListByX(list, new Comparator<Territory>()
 
689
        {
 
690
            public int compare(Territory ter1, Territory ter2)
 
691
            {
 
692
                int val1 = DUtils.GetTersThatMatchXThatUnitsOnTerCanAttack(data, ter1, DMatches.territoryIsOwnedByNNEnemy(data, player), player).size();
 
693
                int val2 = DUtils.GetTersThatMatchXThatUnitsOnTerCanAttack(data, ter2, DMatches.territoryIsOwnedByNNEnemy(data, player), player).size();
 
694
 
 
695
                return val1 - val2;
 
696
            }
 
697
        });        
 
698
    }
 
699
    public static List<Territory> GetTerritoriesInListThatAreNotInRoute(List<Territory> list, Route exludeRoute)
 
700
    {
 
701
        List<Territory> result = new ArrayList<Territory>();
 
702
        for (Territory ter : list)
 
703
        {
 
704
            if (!exludeRoute.getTerritories().contains(ter))
 
705
                result.add(ter);
 
706
        }
 
707
        return result;
 
708
    }
 
709
    public static HashMap<Object, Object> ReverseHashMap(HashMap<Object, Object> map, final GameData data, final PlayerID player)
 
710
    {
 
711
        HashMap<Object, Object> result = new HashMap<Object, Object>();
 
712
        List<Object> invertedKeys = new ArrayList<Object>();
 
713
        for (Object obj : map.keySet())
 
714
            invertedKeys.add(obj);
 
715
        Collections.reverse(invertedKeys);
 
716
        for (Object obj : invertedKeys)
 
717
        {
 
718
            result.put(obj, map.get(obj));
 
719
        }
 
720
        return result;
 
721
    }
 
722
    public static int GetSlowestMovementUnitInList(Collection<Unit> list)
 
723
    {
 
724
        int lowestMovement = Integer.MAX_VALUE;
 
725
        for (Unit unit : list)
 
726
        {
 
727
            TripleAUnit tu = TripleAUnit.get(unit);
 
728
            if (tu.getMovementLeft() < lowestMovement)
 
729
            {
 
730
                lowestMovement = tu.getMovementLeft();
 
731
            }
 
732
        }
 
733
        if(lowestMovement == Integer.MAX_VALUE)
 
734
            return -1;
 
735
        return lowestMovement;
 
736
    }
 
737
    public static int GetFastestMovementUnitInList(Collection<Unit> list)
 
738
    {
 
739
        int fastestMovement = Integer.MIN_VALUE;
 
740
        for (Unit unit : list)
 
741
        {
 
742
            TripleAUnit tu = TripleAUnit.get(unit);
 
743
            if (tu.getMovementLeft() > fastestMovement)
 
744
            {
 
745
                fastestMovement = tu.getMovementLeft();
 
746
            }
 
747
        }
 
748
        if(fastestMovement == Integer.MIN_VALUE)
 
749
            return -1;
 
750
        return fastestMovement;
 
751
    }
 
752
    public static Unit GetCheapestUnitInList(Collection<Unit> list)
 
753
    {
 
754
        int cheapest = Integer.MAX_VALUE;
 
755
        Unit cheapestUnit = null;
 
756
        for (Unit unit : list)
 
757
        {
 
758
            int cost = DUtils.GetTUVOfUnit(unit, GlobalCenter.GetPUResource());
 
759
            if (cost < cheapest)
 
760
            {
 
761
                cheapest = cost;
 
762
                cheapestUnit = unit;
 
763
            }
 
764
        }
 
765
        return cheapestUnit;
 
766
    }
 
767
    /**
 
768
     * Separates all the units in the list into separate lists and puts them into a hashmap with the key as the movement speed and the value as the list of units with that speed.
 
769
     * @param list - List of units to seperate
 
770
     * @return - a hashmap containing all the units separated by speed.
 
771
     */
 
772
    public static HashMap<Integer, List<Unit>> SeperateUnitsInListIntoSeperateMovementLists(List<Unit> list)
 
773
    {
 
774
        HashMap<Integer, List<Unit>> result = new HashMap<Integer, List<Unit>>();
 
775
        for (Unit unit : list)
 
776
        {
 
777
            TripleAUnit ta = TripleAUnit.get(unit);
 
778
            int movement = ta.getMovementLeft();
 
779
            if (result.containsKey(movement))
 
780
            {
 
781
                List<Unit> oldUnits = result.get(movement);
 
782
                oldUnits.add(unit);
 
783
                result.put(movement, oldUnits);
 
784
            }
 
785
            else
 
786
            {
 
787
                List<Unit> oldUnits = new ArrayList<Unit>();
 
788
                oldUnits.add(unit);
 
789
                result.put(movement, oldUnits);
 
790
            }
 
791
        }
 
792
        return result;
 
793
    }
 
794
    public static List<Territory> GetXClosestTersInList(GameData data, List<Territory> ters, Territory target, int count)
 
795
    {
 
796
        if (ters == null || ters.isEmpty())
 
797
            return new ArrayList<Territory>();
 
798
        return DSorting.SortTerritoriesByLandThenNoCondDistance_A(ters, data, target).subList(0, count);
 
799
    }
 
800
    public static Territory GetClosestTerInList(GameData data, List<Territory> ters, Territory target)
 
801
    {
 
802
        List<Territory> xClosest = GetXClosestTersInList(data, ters, target, 1);
 
803
        if(xClosest.isEmpty())
 
804
            return null;
 
805
        return xClosest.get(0);
 
806
    }
 
807
    
 
808
    ///////////////////////////Territory finding methods///////////////////////////
 
809
    public static Territory GetClosestTerMatchingX(GameData data, Territory target, Match<Territory> match)
 
810
    {
 
811
        List<Territory> matching = GetTerritoriesWithinXDistanceOfYMatchingZ(data, target, Integer.MAX_VALUE, match);
 
812
        if(matching.isEmpty())
 
813
            return null;
 
814
        return matching.get(0);
 
815
    }
 
816
    public static Territory GetClosestTerMatchingXAndHavingRouteMatchingY(GameData data, Territory target, Match<Territory> match, Match<Territory> routeMatch)
 
817
    {
 
818
        List<Territory> matching = GetTerritoriesWithinXDistanceOfYMatchingZAndHavingRouteMatchingA(data, target, Integer.MAX_VALUE, match, routeMatch);
 
819
        if(matching.isEmpty())
 
820
            return null;
 
821
        return matching.get(0);
 
822
    }
 
823
 
 
824
    /**
 
825
     * Returns all capitals.
 
826
     */
 
827
    public static List<Territory> GetAllCapitals(GameData data)
 
828
    {
 
829
        return Match.getMatches(data.getMap().getTerritories(), DMatches.territoryIsCapital);
 
830
    }
 
831
 
 
832
    ///////////////////////////Our cap methods///////////////////////////
 
833
    /**
 
834
     * Returns all capitals currently owned by player.
 
835
     */
 
836
    public static List<Territory> GetAllCapsOwnedBy(GameData data, PlayerID player)
 
837
    {
 
838
        return Match.getMatches(GetAllCapitals(data), DMatches.territoryIsOwnedBy(player));
 
839
    }
 
840
    /**
 
841
     * Returns all capitals originally owned by player.
 
842
     */
 
843
    public static List<Territory> GetAllOurCaps(GameData data, PlayerID player)
 
844
    {
 
845
        return TerritoryAttachment.getAllCapitals(player, data);
 
846
    }
 
847
    /**
 
848
     * Returns all capitals originally owned by player that we own currently.
 
849
     */
 
850
    public static List<Territory> GetAllOurCaps_ThatWeOwn(GameData data, PlayerID player)
 
851
    {
 
852
        return Match.getMatches(GetAllOurCaps(data, player), Matches.isTerritoryOwnedBy(player));
 
853
    }
 
854
    /**
 
855
     * Returns the closest capital originally owned by player.
 
856
     */
 
857
    public static Territory GetOurClosestCap(GameData data, PlayerID player, Territory ter)
 
858
    {
 
859
        return GetClosestTerInList(data, GetAllOurCaps(data, player), ter);
 
860
    }
 
861
    /**
 
862
     * Returns the closest capital originally owned by player that we own currently.
 
863
     */
 
864
    public static Territory GetOurClosestCap_ThatWeOwn(GameData data, PlayerID player, Territory ter)
 
865
    {
 
866
        return GetClosestTerInList(data, GetAllOurCaps_ThatWeOwn(data, player), ter);
 
867
    }
 
868
 
 
869
    ///////////////////////////Enemy cap methods///////////////////////////
 
870
    /**
 
871
     * Returns all capitals currently owned by enemies.
 
872
     */
 
873
    public static List<Territory> GetAllCapsOwnedByEnemies(GameData data, PlayerID player)
 
874
    {
 
875
        return Match.getMatches(GetAllCapitals(data), Matches.isTerritoryEnemy(player, data));
 
876
    }
 
877
    /**
 
878
     * Returns all capitals originally owned by enemies.
 
879
     */
 
880
    public static List<Territory> GetAllEnemyCaps(GameData data, PlayerID player)
 
881
    {
 
882
        List<Territory> result = new ArrayList<Territory>();
 
883
        for(PlayerID enemy : data.getPlayerList().getPlayers())
 
884
        {
 
885
            if(data.getAllianceTracker().isAllied(enemy, player))
 
886
                continue;
 
887
 
 
888
            result.addAll(GetAllOurCaps(data, enemy));
 
889
        }
 
890
        return result;
 
891
    }
 
892
    /**
 
893
     * Returns all capitals originally owned by enemies that the original owner currently owns.
 
894
     */
 
895
    public static List<Territory> GetAllEnemyCaps_ThatAreOwnedByOriginalOwner(GameData data, PlayerID player)
 
896
    {
 
897
        List<Territory> result = new ArrayList<Territory>();
 
898
        for (PlayerID enemy : data.getPlayerList().getPlayers())
 
899
        {
 
900
            if (data.getAllianceTracker().isAllied(enemy, player))
 
901
                continue;
 
902
 
 
903
            result.addAll(GetAllOurCaps_ThatWeOwn(data, enemy));
 
904
        }
 
905
        return result;
 
906
    }
 
907
 
 
908
    public static List<Territory> GetTerritoriesMatching(GameData data, Match<Territory> match)
 
909
    {
 
910
        return Match.getMatches(data.getMap().getTerritories(), match);
 
911
    }
 
912
    public static Territory GetUnitLocation(GameData data, Unit unit)
 
913
    {
 
914
        for(Territory ter : data.getMap().getTerritories())
 
915
        {
 
916
            if(ter.getUnits().getUnits().contains(unit))
 
917
                return ter;
 
918
        }
 
919
 
 
920
        return null;
 
921
    }
 
922
    public static List<Territory> GetUnitLocations(GameData data, List<Unit> units)
 
923
    {
 
924
        List<Territory> result = new ArrayList<Territory>();
 
925
 
 
926
        for(Territory ter : data.getMap().getTerritories())
 
927
        {
 
928
            for (Unit unit : units)
 
929
            {
 
930
                if (ter.getUnits().getUnits().contains(unit))
 
931
                {
 
932
                    result.add(ter);
 
933
                    break;
 
934
                }
 
935
            }
 
936
        }
 
937
 
 
938
        return result;
 
939
    }
 
940
    public static boolean CanUnitReachTer(GameData data, Territory ter, Unit unit, Territory target)
 
941
    {
 
942
        return CanUnitReachTer(data, ter, unit, target, new ArrayList<Territory>());
 
943
    }
 
944
    public static boolean CanUnitReachTer(GameData data, Territory ter, Unit unit, Territory target, List<Territory> passthroughTers)
 
945
    {
 
946
        PlayerID player = unit.getOwner();
 
947
 
 
948
        if(GlobalCenter.CurrentPhaseType == PhaseType.Combat_Move)
 
949
        {
 
950
            if (ter == target)
 
951
                return true;
 
952
 
 
953
            if (DMatches.territoryContainsMultipleAlliances(data).match(ter))
 
954
                return false; //We don't consider units in a battle ter to be able to reach anything
 
955
            if(TacticalCenter.get(data, GlobalCenter.CurrentPlayer).GetFrozenUnits().contains(unit))
 
956
                return false;
 
957
            if (ThreatInvalidationCenter.get(data, GlobalCenter.CurrentPlayer).IsUnitInvalidatedForTer(unit, target))
 
958
                return false;
 
959
        }
 
960
        else
 
961
        {
 
962
            if (ter == target)
 
963
                return true;
 
964
 
 
965
            if(TacticalCenter.get(data, GlobalCenter.CurrentPlayer).GetFrozenUnits().contains(unit))
 
966
                return false;
 
967
            if (ThreatInvalidationCenter.get(data, GlobalCenter.CurrentPlayer).IsUnitInvalidatedForTer(unit, target))
 
968
                return false;
 
969
        }
 
970
 
 
971
        UnitAttachment ua = UnitAttachment.get(unit.getUnitType());
 
972
        TripleAUnit ta = TripleAUnit.get(unit);
 
973
 
 
974
        Route noCondRoute = CachedCalculationCenter.GetRoute(data, ter, target);
 
975
        if (noCondRoute == null)
 
976
            return false; //Yikes, must be a map with entirely disconnected ters... :(
 
977
        if (noCondRoute.getLength() > ta.getMovementLeft()) //If the unit can't even get from ter to territory on a condition-less route, we know it can't make it
 
978
            return false;
 
979
 
 
980
        if (ua.isAir())
 
981
        {
 
982
            if(DMatches.territoryIsOwnedByXOrAlly(data, player).match(target))
 
983
            {
 
984
                Route route = data.getMap().getRoute(ter, target, Matches.TerritoryIsNotImpassable);
 
985
                if(route != null)
 
986
                {
 
987
                    if(ta.getMovementLeft() >= route.getLength())
 
988
                        return true;
 
989
                }
 
990
            }
 
991
            else
 
992
            {
 
993
                if (CanAirUnitLandWithXSurvivalChanceIfAttackingFromXToY(data, ter, target, unit, DUtils.ToFloat(DSettings.LoadSettings().AA_survivalChanceOfLandingTerRequiredForPlaneRecruit)))
 
994
                    return true;
 
995
            }
 
996
        }
 
997
        else if (ua.isSea())
 
998
        {
 
999
            if (ter.isWater())
 
1000
            {
 
1001
                Route route = DUtils.GetAttackRouteFromXToY_BySea(data, player, ter, target);
 
1002
                if (route != null && ta.getMovementLeft() >= route.getLength())
 
1003
                    return true;
 
1004
            }
 
1005
            else
 
1006
            {
 
1007
            }
 
1008
        }
 
1009
        else
 
1010
        {
 
1011
            if (ter.isWater())
 
1012
            {
 
1013
            }
 
1014
            else
 
1015
            {
 
1016
                if(ua.isAA() && GlobalCenter.CurrentPhaseType != GlobalCenter.CurrentPhaseType.Non_Combat_Move)
 
1017
                    return false; //AA's can't move unless in ncm phase
 
1018
                Route route = DUtils.GetAttackRouteFromXToY_ByLand_CountZAsPassthroughs(data, player, ter, target, passthroughTers);
 
1019
                if (route != null && ta.getMovementLeft() >= route.getLength())
 
1020
                    return true;
 
1021
            }
 
1022
        }
 
1023
        return false;
 
1024
    }
 
1025
    public static List<Unit> GetUnitsMatching(List<Unit> units, Match<Unit> match)
 
1026
    {
 
1027
        return Match.getMatches(units, match);
 
1028
    }
 
1029
    public static List<Territory> GetEnemyTerritoriesWithinXLandDistanceThatHaveEnemyUnitsThatCanAttack(Territory target, GameData data, PlayerID player, int maxJumpDist)
 
1030
    {
 
1031
        List<Territory> result = new ArrayList<Territory>();
 
1032
        for (Territory ter : data.getMap().getTerritories())
 
1033
        {
 
1034
            if (ter == target)
 
1035
                continue;
 
1036
            if (data.getAllianceTracker().isAllied(player, ter.getOwner()))
 
1037
                continue;
 
1038
            List<Unit> enemyUnits = ter.getUnits().getMatches(Matches.unitIsEnemyOf(data, player));
 
1039
            int dist = DUtils.GetJumpsFromXToY_PassableLand(data, ter, target);
 
1040
            if(dist < 1 || dist > maxJumpDist)
 
1041
                continue;
 
1042
 
 
1043
            Route noCondRoute = CachedCalculationCenter.GetRoute(data, ter, target);
 
1044
            if (noCondRoute == null)
 
1045
                continue; //Yikes, must be a map with entirely disconnected ters... :(
 
1046
            if (noCondRoute.getLength() > GlobalCenter.FastestUnitMovement) //If the unit can't even get from ter to territory on a condition-less route, we know it can't make it
 
1047
                continue;
 
1048
 
 
1049
            for (Unit u : enemyUnits)
 
1050
            {
 
1051
                if(CanUnitReachTer(data, ter, u, target))
 
1052
                {
 
1053
                    result.add(ter);
 
1054
                    break;
 
1055
                }
 
1056
            }
 
1057
        }
 
1058
        return result;
 
1059
    }
 
1060
    public static int GetHighestTerProduction(GameData data)
 
1061
    {
 
1062
        int result = 0;
 
1063
        for (Territory ter : data.getMap().getTerritories())
 
1064
        {
 
1065
            if (ter.isWater())
 
1066
                continue;
 
1067
            TerritoryAttachment ta = TerritoryAttachment.get(ter);
 
1068
            if (ta == null)
 
1069
                continue;
 
1070
            if (ta.getProduction() > result)
 
1071
                result = ta.getProduction();
 
1072
        }
 
1073
        return result;
 
1074
    }
 
1075
    public static float GetValueOfLandTer(Territory target, GameData data, PlayerID player)
 
1076
    {
 
1077
        float result = 1;
 
1078
 
 
1079
        Territory ourCap = GetOurClosestCap(data, player, target);
 
1080
        int jumps = DUtils.GetJumpsFromXToY_PassableLand(data, target, ourCap);
 
1081
 
 
1082
        //3) If this ter is 1 jump away from our cap, add 90, if 2 jumps away, add 40, if three jumps away, add 10
 
1083
        if(jumps == 1)
 
1084
            result += 90;
 
1085
        else if(jumps == 2)
 
1086
            result += 40;
 
1087
        else if(jumps == 3)
 
1088
            result += 10;
 
1089
 
 
1090
        List<Territory> enemyCaps = DUtils.GetAllEnemyCaps(data, player);
 
1091
        if(enemyCaps.contains(target))
 
1092
            result += 250; //Give enemy caps a large score boost
 
1093
 
 
1094
        if(ourCap.getName().equals(target.getName()))
 
1095
            result += 500; //Give our cap a huge score boost
 
1096
 
 
1097
        if(target.getUnits().getMatches(Matches.UnitIsFactory).size() > 0)
 
1098
            result += 100; //Give enemy factory ters a boost
 
1099
 
 
1100
        result += TerritoryAttachment.get(target).getProduction() * 10;
 
1101
        result += data.getMap().getNeighbors(target, Matches.TerritoryIsLand).size() * 2;
 
1102
        result += data.getMap().getNeighbors(target, DMatches.territoryIsOwnedByNNEnemy(data, player)).size();
 
1103
 
 
1104
        return result;
 
1105
    }
 
1106
    public static HashMap<PlayerID, StrategyType> CalculateStrategyAssignments(GameData data, PlayerID player)
 
1107
    {
 
1108
        HashMap<PlayerID, StrategyType> result = new HashMap<PlayerID, StrategyType>();
 
1109
        if (GlobalCenter.IsFFAGame)
 
1110
        {
 
1111
            List<Territory> ourTersConnectedToCap = new ArrayList<Territory>();
 
1112
            List<Territory> ourCaps = DUtils.GetAllOurCaps(data, player);
 
1113
            for (Territory cap : ourCaps)
 
1114
                ourTersConnectedToCap.addAll(DUtils.GetTerritoriesWithinXDistanceOfYMatchingZAndHavingRouteMatchingA(data, cap, Integer.MAX_VALUE, DMatches.territoryIsOwnedBy(player), DMatches.territoryIsOwnedBy(player)));
 
1115
 
 
1116
            HashSet<PlayerID> enemiesTouchingOurCapConnectedTers = new HashSet<PlayerID>(); //Btw, I use HashSet because it doesn't allow duplicates in a list
 
1117
 
 
1118
            for(Territory enemyTer : data.getMap().getTerritories())
 
1119
            {
 
1120
                if(!DMatches.territoryIsOwnedByNNEnemy(data, player).match(enemyTer))
 
1121
                    continue; //If this ter is not an enemy
 
1122
                if(Match.getMatches(data.getMap().getNeighbors(enemyTer), DMatches.territoryIsInList(ourTersConnectedToCap)).isEmpty())
 
1123
                    continue; //If none of this ters neighbors are part of our 'ters connected to cap' list, don't count it
 
1124
                if(!DMatches.territoryHasRouteMatchingXToTerritoryMatchingY(data, DMatches.territoryIsOwnedBy(enemyTer.getOwner()), CompMatchAnd(DMatches.territoryIsOwnedBy(enemyTer.getOwner()), DMatches.territoryIsInList(DUtils.GetAllOurCaps(data, enemyTer.getOwner())))).match(enemyTer))
 
1125
                    continue; //If this enemy ter does not have a route to it's capital
 
1126
 
 
1127
                //We passed all the checks, so we count this enemy as a 'neighbor'
 
1128
                enemiesTouchingOurCapConnectedTers.add(enemyTer.getOwner());
 
1129
            }
 
1130
            
 
1131
            PlayerID weakestEnemyNeighbor = null;
 
1132
            int weakestEnemyTUV = Integer.MAX_VALUE;
 
1133
            for(PlayerID enemy : enemiesTouchingOurCapConnectedTers)
 
1134
            {
 
1135
                int tuv = GetTUVOfUnits(GetUnitsMatchingXInTerritoriesMatchingY(data, Matches.unitIsOwnedBy(enemy), Matches.TerritoryIsLandOrWater), GlobalCenter.GetPUResource());
 
1136
                if(tuv < weakestEnemyTUV)
 
1137
                {
 
1138
                    weakestEnemyNeighbor = enemy;
 
1139
                    weakestEnemyTUV = tuv;
 
1140
                }
 
1141
            }
 
1142
 
 
1143
            if(weakestEnemyNeighbor != null)
 
1144
                result.put(weakestEnemyNeighbor, StrategyType.Enemy_Offensive); //For now, only go on the offensive on our weakest neighbor
 
1145
            
 
1146
            for (PlayerID otherPlayer : data.getPlayerList())
 
1147
            {
 
1148
                if(otherPlayer == player)
 
1149
                    continue;
 
1150
                if (data.getAllianceTracker().isAtWar(player, otherPlayer))
 
1151
                {
 
1152
                    if(result.containsKey(otherPlayer)) //If this player already has an assignment
 
1153
                        continue;
 
1154
 
 
1155
                    result.put(otherPlayer, StrategyType.Enemy_Defensive);
 
1156
                }
 
1157
                else //Shouldn't ever happen in FFA's...
 
1158
                    result.put(otherPlayer, StrategyType.Ally_OffensiveAssist);
 
1159
            }
 
1160
        }
 
1161
        else
 
1162
        {
 
1163
            List<Territory> ourTersConnectedToCap = new ArrayList<Territory>();
 
1164
            List<Territory> ourCaps = DUtils.GetAllOurCaps(data, player);
 
1165
            for (Territory cap : ourCaps)
 
1166
                ourTersConnectedToCap.addAll(DUtils.GetTerritoriesWithinXDistanceOfYMatchingZAndHavingRouteMatchingA(data, cap, Integer.MAX_VALUE, DMatches.territoryIsOwnedBy(player), DMatches.territoryIsOwnedBy(player)));
 
1167
 
 
1168
            HashSet<PlayerID> enemiesTouchingOurCapConnectedTers = new HashSet<PlayerID>(); //Btw, I use HashSet because it doesn't allow duplicates in a list
 
1169
 
 
1170
            for(Territory enemyTer : data.getMap().getTerritories())
 
1171
            {
 
1172
                if(!DMatches.territoryIsOwnedByNNEnemy(data, player).match(enemyTer))
 
1173
                    continue; //If this ter is not an enemy
 
1174
                if(Match.getMatches(data.getMap().getNeighbors(enemyTer), DMatches.territoryIsInList(ourTersConnectedToCap)).isEmpty())
 
1175
                    continue; //If none of this ters neighbors are part of our 'ters connected to cap' list, don't count it
 
1176
                if(!DMatches.territoryHasRouteMatchingXToTerritoryMatchingY(data, DMatches.territoryIsOwnedBy(enemyTer.getOwner()), CompMatchAnd(DMatches.territoryIsOwnedBy(enemyTer.getOwner()), DMatches.territoryIsInList(DUtils.GetAllOurCaps(data, enemyTer.getOwner())))).match(enemyTer))
 
1177
                    continue; //If this enemy ter does not have a route to it's capital
 
1178
 
 
1179
                //We passed all the checks, so we count this enemy as a 'neighbor'
 
1180
                enemiesTouchingOurCapConnectedTers.add(enemyTer.getOwner());
 
1181
            }
 
1182
 
 
1183
            PlayerID weakestEnemyNeighbor = null;
 
1184
            int weakestEnemyTUV = Integer.MAX_VALUE;
 
1185
            for(PlayerID enemy : enemiesTouchingOurCapConnectedTers)
 
1186
            {
 
1187
                int tuv = GetTUVOfUnits(GetUnitsMatchingXInTerritoriesMatchingY(data, Matches.unitIsOwnedBy(enemy), Matches.TerritoryIsLandOrWater), GlobalCenter.GetPUResource());
 
1188
                if(tuv < weakestEnemyTUV)
 
1189
                {
 
1190
                    weakestEnemyNeighbor = enemy;
 
1191
                    weakestEnemyTUV = tuv;
 
1192
                }
 
1193
            }
 
1194
 
 
1195
            if(weakestEnemyNeighbor != null)
 
1196
                result.put(weakestEnemyNeighbor, StrategyType.Enemy_Offensive); //For now, only go on the offensive on our weakest neighbor
 
1197
 
 
1198
            for (PlayerID otherPlayer : data.getPlayerList())
 
1199
            {
 
1200
                if(otherPlayer == player)
 
1201
                    continue;
 
1202
                if (data.getAllianceTracker().isAtWar(player, otherPlayer))
 
1203
                {
 
1204
                    if(result.containsKey(otherPlayer)) //If this player already has an assignment
 
1205
                        continue;
 
1206
 
 
1207
                    result.put(otherPlayer, StrategyType.Enemy_Defensive);
 
1208
                }
 
1209
                else //Shouldn't ever happen in FFA's...
 
1210
                    result.put(otherPlayer, StrategyType.Ally_OffensiveAssist);
 
1211
            }
 
1212
        }
 
1213
 
 
1214
        DUtils.Log(Level.FINE, "    Calculated strategy assignments: {0}", result);
 
1215
        return result;
 
1216
    }
 
1217
    public static float GetCMTaskPriority_LandGrab(GameData data, PlayerID player, Territory ter)
 
1218
    {
 
1219
        float priority = 1000000F;
 
1220
        priority += DUtils.GetValueOfLandTer(ter, data, player);
 
1221
 
 
1222
        StrategyType strategyType = StrategyCenter.get(data, player).GetCalculatedStrategyAssignments().get(ter.getOwner());
 
1223
        if(strategyType == StrategyType.Enemy_Offensive)
 
1224
            priority += 100;
 
1225
        return priority;
 
1226
    }
 
1227
    public static float GetCMTaskPriority_Stabalization(GameData data, PlayerID player, Territory ter)
 
1228
    {
 
1229
        float priority = 100F;
 
1230
        priority += DUtils.GetValueOfLandTer(ter, data, player);
 
1231
        if(TerritoryAttachment.getAllCapitals(player, data).contains(ter))
 
1232
            priority = 1000F;
 
1233
 
 
1234
        return priority;
 
1235
    }
 
1236
    public static float GetCMTaskPriority_Offensive(GameData data, PlayerID player, Territory ter)
 
1237
    {
 
1238
        float priority = 0F;
 
1239
        final Territory ourCap = GetOurClosestCap(data, player, ter);
 
1240
        priority += DUtils.GetValueOfLandTer(ter, data, player);
 
1241
 
 
1242
        Territory neighborWeAreInThatsClosestToOurCap = null; //Atm, we use this to tell 'where we are attacking from'
 
1243
        int closestToCapDist = Integer.MAX_VALUE;
 
1244
        for (Territory neighbor : data.getMap().getNeighbors(ter, Matches.territoryHasUnitsOwnedBy(player)))
 
1245
        {
 
1246
            Route routeToCap = data.getMap().getLandRoute(neighbor, ourCap);
 
1247
            if (routeToCap == null)
 
1248
                continue;
 
1249
 
 
1250
            int dist = routeToCap.getLength();
 
1251
            if (dist < closestToCapDist)
 
1252
            {
 
1253
                neighborWeAreInThatsClosestToOurCap = neighbor;
 
1254
                closestToCapDist = dist;
 
1255
            }
 
1256
        }
 
1257
 
 
1258
        if (neighborWeAreInThatsClosestToOurCap != null) //This code block will not run if ter does not have a land path to our cap
 
1259
        {
 
1260
            Territory closestEnemyCapForOurTer = GetClosestTerInList(data, DUtils.GetAllCapsOwnedByEnemies(data, player), neighborWeAreInThatsClosestToOurCap);
 
1261
            Territory closestEnemyCapForTarget = GetClosestTerInList(data, DUtils.GetAllCapsOwnedByEnemies(data, player), ter);
 
1262
 
 
1263
            if (closestEnemyCapForOurTer != null && closestEnemyCapForTarget != null) //If there aren't capitals left to take, skip this part
 
1264
            {
 
1265
                //2) Is the territory-we-would-move-to closer to the closest enemy capital than the territory-we-are-currently-in?  (ie: are we moving towards any enemy capital at all)  If so, give 6 points.
 
1266
                if (closestEnemyCapForOurTer.getName().equals(closestEnemyCapForTarget.getName()))
 
1267
                {
 
1268
                    if (DUtils.GetJumpsFromXToY_PassableLand(data, ter, closestEnemyCapForTarget) < DUtils.GetJumpsFromXToY_PassableLand(data, neighborWeAreInThatsClosestToOurCap, closestEnemyCapForTarget))
 
1269
                    {
 
1270
                        priority += 6.0F;
 
1271
                    }
 
1272
                }
 
1273
            }
 
1274
 
 
1275
            Territory closestFactToOurTer = DUtils.GetClosestTerMatchingXAndHavingRouteMatchingY(data, neighborWeAreInThatsClosestToOurCap, Matches.territoryHasUnitsThatMatch(new CompositeMatchAnd<Unit>(Matches.UnitIsFactory, Matches.unitIsEnemyOf(data, player))), Matches.TerritoryIsLand);
 
1276
            //3) Are we moving towards the closest enemy factory?
 
1277
            if (DUtils.GetJumpsFromXToY_PassableLand(data, ter, closestFactToOurTer) < DUtils.GetJumpsFromXToY_PassableLand(data, neighborWeAreInThatsClosestToOurCap, closestFactToOurTer))
 
1278
            {
 
1279
                float productionPercentOfHighest = (float) TerritoryAttachment.get(closestFactToOurTer).getProduction() / (float) GlobalCenter.HighestTerProduction;
 
1280
                priority += productionPercentOfHighest * 5;  //If so, add 2 to 5 points, depending on value of factory and territory.
 
1281
            }
 
1282
        }
 
1283
 
 
1284
        List<Territory> enemyCaps = GetAllCapsOwnedByEnemies(data, player);
 
1285
        if(enemyCaps.contains(ter))
 
1286
            priority += 25;
 
1287
 
 
1288
        if(ter.getOwner().isNull())
 
1289
            priority = (priority * .75F); //Neutral attacks aren't as important
 
1290
 
 
1291
        int enemyNeighbors = data.getMap().getNeighbors(ter, CompMatchAnd(Matches.TerritoryIsLand, DMatches.territoryIsOwnedByEnemy(data, player))).size();
 
1292
        if(ter.getOwner().isNull())
 
1293
            priority = priority / (enemyNeighbors * 2);
 
1294
 
 
1295
        int friendlyNeighbors = data.getMap().getNeighbors(ter, CompMatchAnd(Matches.TerritoryIsLand, DMatches.territoryIsOwnedByXOrAlly(data, player))).size();
 
1296
        priority += friendlyNeighbors;
 
1297
 
 
1298
        StrategyType strategyType = StrategyCenter.get(data, player).GetCalculatedStrategyAssignments().get(ter.getOwner());
 
1299
        if(strategyType == StrategyType.Enemy_Offensive)
 
1300
            priority += 100;
 
1301
 
 
1302
        return priority;
 
1303
    }
 
1304
    public static float GetCMTaskPriority_Trade(GameData data, PlayerID player, Territory ter)
 
1305
    {
 
1306
        float priority = 1000F;
 
1307
        priority += DUtils.GetValueOfLandTer(ter, data, player);
 
1308
 
 
1309
        StrategyType strategyType = StrategyCenter.get(data, player).GetCalculatedStrategyAssignments().get(ter.getOwner());
 
1310
        if(strategyType == StrategyType.Enemy_Offensive)
 
1311
            priority += 100;
 
1312
        return priority;
 
1313
    }
 
1314
 
 
1315
    //As a note to any developers reading this, these priority deciding methods need a lot more work.
 
1316
    public static float GetNCMTaskPriority_Block(GameData data, PlayerID player, Territory ter)
 
1317
    {
 
1318
        float priority = 1000F; //TODO
 
1319
        priority += GetValueOfLandTer(ter, data, player);
 
1320
        return priority;
 
1321
    }
 
1322
    public static float GetNCMTaskPriority_Frontline(GameData data, PlayerID player, Territory ter)
 
1323
    {        
 
1324
        float priority = 500F; //TODO
 
1325
        priority += GetValueOfLandTer(ter, data, player);
 
1326
        priority += data.getMap().getNeighbors(ter, DMatches.territoryIsOwnedByNNEnemy(data, player)).size() * 5;
 
1327
        return priority;
 
1328
    }
 
1329
    public static float GetNCMTaskPriority_Stabalize(GameData data, PlayerID player, Territory ter)
 
1330
    {
 
1331
        float priority = 750F; //TODO
 
1332
        priority += GetValueOfLandTer(ter, data, player);
 
1333
        return priority;
 
1334
    }
 
1335
    public static float GetNCMCallPriority_ForLandGrab(GameData data, PlayerID player, Territory ter)
 
1336
    {
 
1337
        float priority = 1000F; //TODO
 
1338
        priority += GetValueOfLandTer(ter, data, player);
 
1339
        return priority;
 
1340
    }
 
1341
    public static float GetNCMCallPriority_ForDefensiveFront(GameData data, PlayerID player, Territory ter)
 
1342
    {
 
1343
        float priority = 1000000F;
 
1344
        priority += GetValueOfLandTer(ter, data, player);
 
1345
        return priority;
 
1346
    }    
 
1347
    public static float GetNCMCallPriority_ForCapitalDefense(GameData data, PlayerID player, Territory ter)
 
1348
    {
 
1349
        float priority = 0F; //TODO
 
1350
        priority += GetValueOfLandTer(ter, data, player);
 
1351
        return priority;
 
1352
    }
 
1353
    public static int GetDistance_ForLandThenNoCondComparison(GameData data, Territory ter1, Territory ter2)
 
1354
    {
 
1355
        Route route1 = CachedCalculationCenter.GetLandRoute(data, ter1, ter2);
 
1356
        Route route1_nc = CachedCalculationCenter.GetRoute(data, ter1, ter2);
 
1357
 
 
1358
        if (route1_nc == null)
 
1359
            return DConstants.Integer_HalfMax; //If there's no route, we want ones with a route to come first
 
1360
 
 
1361
        int distance1 = route1_nc.getLength() * 100;
 
1362
        if (route1 != null)
 
1363
            distance1 = route1.getLength();
 
1364
 
 
1365
        return distance1;
 
1366
    }
 
1367
    /**
 
1368
     * Basically, this method uses the battle calculator to sort the units so while adding each unit into the new list, the attack score is the highest possible at each unit add.
 
1369
     * (Atm, this method does not work well...)
 
1370
     */
 
1371
    public static List<Unit> InterleaveUnits_ForBestAttack(List<Unit> units)
 
1372
    {
 
1373
        GameData data = units.get(0).getData();
 
1374
        Territory battleTer = (Territory)data.getMap().getTerritories().toArray()[0];
 
1375
        PlayerID player = GlobalCenter.CurrentPlayer;
 
1376
 
 
1377
        List<Unit> defendStack = MultiplyDefenderUnitsTillTakeoverChanceIsLessThanX(units, Collections.singletonList(GetRandomUnitForPlayerMatching(battleTer.getOwner(), DMatches.UnitCanDefend)), data, battleTer, .9F);
 
1378
 
 
1379
        List<Unit> leftToAdd = new ArrayList<Unit>(units);
 
1380
        ArrayList<Unit> result = new ArrayList<Unit>();
 
1381
 
 
1382
        while(result.size() < units.size())
 
1383
        {
 
1384
            Unit nextToAdd = CalculateUnitThatWillHelpWinAttackOnArmyTheMostPerPU(battleTer, data, player, result, leftToAdd, defendStack, Match.ALWAYS_MATCH, DSettings.LoadSettings().CA_CMNCM_sortsPossibleTaskRecruitsForOptimalAttackDefense);
 
1385
 
 
1386
            result.add(nextToAdd);
 
1387
            leftToAdd.remove(nextToAdd);
 
1388
        }
 
1389
 
 
1390
        return result;
 
1391
    }
 
1392
    public static List<Unit> InterleaveUnits_SoWhileSortingYPercentOfUnitsMatchX(List<Unit> units, Match<Unit> match, float percentage)
 
1393
    {
 
1394
        List<Unit> result = new ArrayList<Unit>();
 
1395
        double xToOthersRatio = 0.5F;
 
1396
        while(result.size() < units.size())
 
1397
        {
 
1398
            Unit nextToAdd = null;
 
1399
            if (xToOthersRatio < percentage) //If less than Y% of units are matching x, seek x matching unit
 
1400
                nextToAdd = GetFirstUnitMatching(units, CompMatchAnd(match, DMatches.unitIsNotInList(result)), 0);
 
1401
            else if (xToOthersRatio > percentage) //If more than Y% of units are matching x, seek non-x matching unit
 
1402
                nextToAdd = GetFirstUnitMatching(units, CompMatchAnd(match.invert(), DMatches.unitIsNotInList(result)), 0);
 
1403
            if(nextToAdd == null) //If we can no longer keep up this ratio, add leftover units, then break and return
 
1404
            {
 
1405
                result.addAll(Match.getMatches(units, DMatches.unitIsNotInList(result)));
 
1406
                break;
 
1407
            }
 
1408
 
 
1409
            result.add(nextToAdd);
 
1410
            //Update ratio's
 
1411
            if (match.match(nextToAdd))
 
1412
            {
 
1413
                double dif = 1.0F - xToOthersRatio;
 
1414
                xToOthersRatio += (dif / (double)result.size());
 
1415
            }
 
1416
            else
 
1417
            {
 
1418
                double dif = 0.0F - xToOthersRatio; //Yes, I know this is the same as -airToLandRatio...
 
1419
                xToOthersRatio += (dif / (double)result.size());
 
1420
            }
 
1421
        }
 
1422
        return result;
 
1423
    }
 
1424
    public static List<Unit> InterleaveUnits_CarriersAndPlanes(List<Unit> units, int planesThatDontNeedToLand)
 
1425
    {
 
1426
        if (!(Match.someMatch(units, Matches.UnitIsCarrier) && Match.someMatch(units, Matches.UnitCanLandOnCarrier)))
 
1427
            return units;
 
1428
 
 
1429
        //Clone the current list
 
1430
        ArrayList<Unit> result = new ArrayList<Unit>(units);
 
1431
 
 
1432
        Unit seekedCarrier = null;
 
1433
        int indexToPlaceCarrierAt = -1;
 
1434
        int spaceLeftOnSeekedCarrier = -1;
 
1435
        int processedPlaneCount = 0;
 
1436
        List<Unit> filledCarriers = new ArrayList<Unit>();
 
1437
        //Loop through all units, starting from the right, and rearrange units
 
1438
        for (int i = result.size() - 1; i >= 0; i--)
 
1439
        {
 
1440
            Unit unit = result.get(i);
 
1441
            UnitAttachment ua = UnitAttachment.get(unit.getUnitType());
 
1442
            if (ua.getCarrierCost() > 0) //If this is a plane
 
1443
            {
 
1444
                if(processedPlaneCount < planesThatDontNeedToLand) //If we haven't ignored enough trailing planes
 
1445
                {
 
1446
                    processedPlaneCount++; //Increase number of trailing planes ignored
 
1447
                    continue; //And skip any processing
 
1448
                }
 
1449
 
 
1450
                if (seekedCarrier == null) //If this is the first carrier seek
 
1451
                {
 
1452
                    int seekedCarrierIndex = GetIndexOfLastUnitMatching(result, CompMatchAnd(Matches.UnitIsCarrier, DMatches.unitIsNotInList(filledCarriers)), result.size() - 1);
 
1453
                    if (seekedCarrierIndex == -1)
 
1454
                        break; //No carriers left
 
1455
                    seekedCarrier = units.get(seekedCarrierIndex);
 
1456
 
 
1457
                    indexToPlaceCarrierAt = i + 1; //Tell the code to insert carrier to the right of this plane
 
1458
                    spaceLeftOnSeekedCarrier = UnitAttachment.get(seekedCarrier.getUnitType()).getCarrierCapacity();
 
1459
                }
 
1460
                spaceLeftOnSeekedCarrier -= ua.getCarrierCost();
 
1461
                if(spaceLeftOnSeekedCarrier <= 0) //If the carrier has been filled or overflowed
 
1462
                {
 
1463
                    if(spaceLeftOnSeekedCarrier < 0) //If we over-filled the old carrier
 
1464
                        i++; //Move current unit index up one, so we re-process this unit (since it can't fit on the current seeked carrier)
 
1465
 
 
1466
                    if (result.indexOf(seekedCarrier) < i) //If the seeked carrier is earlier in the list
 
1467
                    {
 
1468
                        //Move the carrier up to the planes by: removing carrier, then reinserting it (index decreased cause removal of carrier reduced indexes)
 
1469
                        result.remove(seekedCarrier);
 
1470
                        result.add(indexToPlaceCarrierAt - 1, seekedCarrier);
 
1471
                        i--; //We removed carrier in earlier part of list, so decrease index
 
1472
                        filledCarriers.add(seekedCarrier);
 
1473
 
 
1474
                        //Find the next carrier
 
1475
                        seekedCarrier = GetLastUnitMatching(result, CompMatchAnd(Matches.UnitIsCarrier, DMatches.unitIsNotInList(filledCarriers)), result.size() - 1);
 
1476
                        if (seekedCarrier == null)
 
1477
                            break; //No carriers left
 
1478
                        indexToPlaceCarrierAt = i; //Place next carrier right before this plane (which just filled the old carrier that was just moved)
 
1479
                        spaceLeftOnSeekedCarrier = UnitAttachment.get(seekedCarrier.getUnitType()).getCarrierCapacity();
 
1480
                    }
 
1481
                    else //If it's later in the list
 
1482
                    {
 
1483
                        int oldIndex = result.indexOf(seekedCarrier);
 
1484
                        int carrierPlaceLocation = indexToPlaceCarrierAt;
 
1485
                        //Place carrier where it's supposed to go
 
1486
                        result.remove(seekedCarrier);
 
1487
                        if (oldIndex < indexToPlaceCarrierAt)
 
1488
                            carrierPlaceLocation--;
 
1489
                        result.add(carrierPlaceLocation, seekedCarrier);
 
1490
                        filledCarriers.add(seekedCarrier);
 
1491
 
 
1492
                        //Move the planes down to the carrier
 
1493
                        List<Unit> planesBetweenHereAndCarrier = new ArrayList<Unit>();
 
1494
                        for(int i2 = i;i2 < carrierPlaceLocation;i2++)
 
1495
                        {
 
1496
                            Unit unit2 = result.get(i2);
 
1497
                            UnitAttachment ua2 = UnitAttachment.get(unit2.getUnitType());
 
1498
                            if (ua2.getCarrierCost() > 0)
 
1499
                                planesBetweenHereAndCarrier.add(unit2);
 
1500
                        }
 
1501
                        planesBetweenHereAndCarrier = InvertList(planesBetweenHereAndCarrier); //Invert list, so they are inserted in the same order
 
1502
                        int planeMoveCount = 0;
 
1503
                        for (Unit plane : planesBetweenHereAndCarrier)
 
1504
                        {
 
1505
                            result.remove(plane);
 
1506
                            result.add(carrierPlaceLocation - 1, plane); //Insert each plane right before carrier (index decreased cause removal of carrier reduced indexes)
 
1507
                            planeMoveCount++;
 
1508
                        }
 
1509
 
 
1510
                        //Find the next carrier
 
1511
                        seekedCarrier = GetLastUnitMatching(result, CompMatchAnd(Matches.UnitIsCarrier, DMatches.unitIsNotInList(filledCarriers)), result.size() - 1);
 
1512
                        if (seekedCarrier == null)
 
1513
                            break; //No carriers left
 
1514
                        indexToPlaceCarrierAt = carrierPlaceLocation - planeMoveCount; //Since we only moved planes up, just reduce next carrier place index by plane move count
 
1515
                        spaceLeftOnSeekedCarrier = UnitAttachment.get(seekedCarrier.getUnitType()).getCarrierCapacity();
 
1516
                    }                    
 
1517
                }
 
1518
            }
 
1519
        }
 
1520
 
 
1521
        return result;
 
1522
    }
 
1523
    public static List<Unit> InterleaveUnits_InfantryAndArtillery(List<Unit> units, int infantryPerArtillery, int infantryThatDontNeedArtillery)
 
1524
    {
 
1525
        if (!(Match.someMatch(units, Matches.UnitIsArtillery) && Match.someMatch(units, Matches.UnitIsArtillerySupportable)))
 
1526
            return units;
 
1527
 
 
1528
        //Clone the current list
 
1529
        ArrayList<Unit> result = new ArrayList<Unit>(units);
 
1530
 
 
1531
        Unit seekedArtillery = null;
 
1532
        int indexToPlaceArtAt = -1;
 
1533
        int spaceLeftOnSeekedArt = -1;
 
1534
        int processedInfantryCount = 0;
 
1535
        List<Unit> filledArts = new ArrayList<Unit>();
 
1536
        //Loop through all units, starting from the right, and rearrange units
 
1537
        for (int i = result.size() - 1; i >= 0; i--)
 
1538
        {
 
1539
            Unit unit = result.get(i);
 
1540
            UnitAttachment ua = UnitAttachment.get(unit.getUnitType());
 
1541
            if (ua.isArtillerySupportable()) //If this is an infantry
 
1542
            {
 
1543
                if(processedInfantryCount < infantryThatDontNeedArtillery) //If we haven't ignored enough trailing infantry
 
1544
                {
 
1545
                    processedInfantryCount++; //Increase number of trailing infantry ignored
 
1546
                    continue; //And skip any processing
 
1547
                }
 
1548
 
 
1549
                if (seekedArtillery == null) //If this is the first artillery seek
 
1550
                {
 
1551
                    int seekedArtIndex = GetIndexOfLastUnitMatching(result, CompMatchAnd(Matches.UnitIsArtillery, DMatches.unitIsNotInList(filledArts)), result.size() - 1);
 
1552
                    if (seekedArtIndex == -1)
 
1553
                        break; //No artilleries left
 
1554
                    seekedArtillery = units.get(seekedArtIndex);
 
1555
 
 
1556
                    indexToPlaceArtAt = i + 1; //Tell the code to insert artillery to the right of this infantry
 
1557
                    spaceLeftOnSeekedArt = infantryPerArtillery;
 
1558
                }
 
1559
                spaceLeftOnSeekedArt -= 1;
 
1560
                if(spaceLeftOnSeekedArt <= 0) //If the artillery has been filled or overflowed
 
1561
                {
 
1562
                    if(spaceLeftOnSeekedArt < 0) //If we over-filled the old artillery
 
1563
                        i++; //Move current unit index up one, so we re-process this unit (since it can't fit on the current seeked artillery)
 
1564
 
 
1565
                    if (result.indexOf(seekedArtillery) < i) //If the seeked artillery is earlier in the list
 
1566
                    {
 
1567
                        //Move the artillery up to the infantry by: removing artillery, then reinserting it (index decreased cause removal of artillery reduced indexes)
 
1568
                        result.remove(seekedArtillery);
 
1569
                        result.add(indexToPlaceArtAt - 1, seekedArtillery);
 
1570
                        i--; //We removed artillery in earlier part of list, so decrease index
 
1571
                        filledArts.add(seekedArtillery);
 
1572
 
 
1573
                        //Find the next artillery
 
1574
                        seekedArtillery = GetLastUnitMatching(result, CompMatchAnd(Matches.UnitIsArtillery, DMatches.unitIsNotInList(filledArts)), result.size() - 1);
 
1575
                        if (seekedArtillery == null)
 
1576
                            break; //No artillery left
 
1577
                        indexToPlaceArtAt = i; //Place next artillery right before this infantry (which just filled the old artillery that was just moved)
 
1578
                        spaceLeftOnSeekedArt = infantryPerArtillery;
 
1579
                    }
 
1580
                    else //If it's later in the list
 
1581
                    {
 
1582
                        int oldIndex = result.indexOf(seekedArtillery);
 
1583
                        int artPlaceLocation = indexToPlaceArtAt;
 
1584
                        //Place artillery where it's supposed to go
 
1585
                        result.remove(seekedArtillery);
 
1586
                        if (oldIndex < indexToPlaceArtAt)
 
1587
                            artPlaceLocation--;
 
1588
                        result.add(artPlaceLocation, seekedArtillery);
 
1589
                        filledArts.add(seekedArtillery);
 
1590
 
 
1591
                        //Move the infantry down to the artillery
 
1592
                        List<Unit> infantryBetweenHereAndArtillery = new ArrayList<Unit>();
 
1593
                        for(int i2 = i;i2 < artPlaceLocation;i2++)
 
1594
                        {
 
1595
                            Unit unit2 = result.get(i2);
 
1596
                            UnitAttachment ua2 = UnitAttachment.get(unit2.getUnitType());
 
1597
                            if (ua2.isArtillerySupportable())
 
1598
                                infantryBetweenHereAndArtillery.add(unit2);
 
1599
                        }
 
1600
                        infantryBetweenHereAndArtillery = InvertList(infantryBetweenHereAndArtillery); //Invert list, so they are inserted in the same order
 
1601
                        int infantryMoveCount = 0;
 
1602
                        for (Unit infantry : infantryBetweenHereAndArtillery)
 
1603
                        {
 
1604
                            result.remove(infantry);
 
1605
                            result.add(artPlaceLocation - 1, infantry); //Insert each infantry right before artillery (index decreased cause removal of artillery reduced indexes)
 
1606
                            infantryMoveCount++;
 
1607
                        }
 
1608
 
 
1609
                        //Find the next artillery
 
1610
                        seekedArtillery = GetLastUnitMatching(result, CompMatchAnd(Matches.UnitIsArtillery, DMatches.unitIsNotInList(filledArts)), result.size() - 1);
 
1611
                        if (seekedArtillery == null)
 
1612
                            break; //No artillery left
 
1613
                        indexToPlaceArtAt = artPlaceLocation - infantryMoveCount; //Since we only moved infantry up, just reduce next artillery place index by infantry move count
 
1614
                        spaceLeftOnSeekedArt = infantryPerArtillery;
 
1615
                    }
 
1616
                }
 
1617
            }
 
1618
        }
 
1619
 
 
1620
        return result;
 
1621
    }
 
1622
    public static Unit GetLastUnitMatching(List<Unit> units, Match<Unit> match, int endIndex)
 
1623
    {
 
1624
        int index = GetIndexOfLastUnitMatching(units, match, endIndex);
 
1625
        if (index == -1)
 
1626
            return null;
 
1627
        return units.get(index);
 
1628
    }
 
1629
    public static int GetIndexOfLastUnitMatching(List<Unit> units, Match<Unit> match, int endIndex)
 
1630
    {
 
1631
        for (int i = endIndex; i >= 0; i--)
 
1632
        {
 
1633
            Unit unit = units.get(i);
 
1634
            if (match.match(unit))
 
1635
                return i;
 
1636
        }
 
1637
        return -1;
 
1638
    }
 
1639
    public static Unit GetFirstUnitMatching(List<Unit> units, Match<Unit> match, int startIndex)
 
1640
    {
 
1641
        int index = GetIndexOfFirstUnitMatching(units, match, startIndex);
 
1642
        if(index == -1)
 
1643
            return null;
 
1644
        return units.get(index);
 
1645
    }
 
1646
    public static int GetIndexOfFirstUnitMatching(List<Unit> units, Match<Unit> match, int startIndex)
 
1647
    {
 
1648
        for(int i = startIndex;i < units.size(); i++)
 
1649
        {
 
1650
            Unit unit = units.get(i);
 
1651
            if(match.match(unit))
 
1652
                return i;
 
1653
        }
 
1654
        return -1;
 
1655
    }
 
1656
    public static int HowWellIsUnitSuitedToTask(GameData data, CM_Task task, Territory ter, Unit unit)
 
1657
    {
 
1658
        if(TacticalCenter.get(data, GlobalCenter.CurrentPlayer).GetFrozenUnits().contains(unit) && ter != task.GetTarget())
 
1659
            return Integer.MIN_VALUE;
 
1660
 
 
1661
        int result = 0;
 
1662
        Route route = CachedCalculationCenter.GetRoute(data, ter, task.GetTarget());
 
1663
        if (route == null)
 
1664
            return Integer.MIN_VALUE;
 
1665
        int dist = route.getLength();
 
1666
        
 
1667
        UnitAttachment ua = UnitAttachment.get(unit.getUnitType());
 
1668
        TripleAUnit ta = TripleAUnit.get(unit);
 
1669
        int tuv = DUtils.GetTUVOfUnit(unit, GlobalCenter.GetPUResource());
 
1670
        int movementSpeed = ua.getMovement(unit.getOwner());
 
1671
        int movementLeft = ta.getMovementLeft();
 
1672
        
 
1673
        if(TacticalCenter.get(data, GlobalCenter.CurrentPlayer).GetFrozenUnits().contains(unit))
 
1674
            movementLeft = 0;
 
1675
 
 
1676
        if (task.GetTaskType() == CM_TaskType.Attack_Offensive)
 
1677
        {
 
1678
            List<Territory> targets = DUtils.GetTersThatUnitsCanReach(data, Collections.singletonList(unit), ter, GlobalCenter.CurrentPlayer, CompMatchAnd(Matches.TerritoryIsLand, DMatches.territoryIsOwnedByNNEnemy(data, GlobalCenter.CurrentPlayer)));
 
1679
            result -= targets.size(); //We want low-attack target units to attack
 
1680
        }
 
1681
        else if (task.GetTaskType() == CM_TaskType.Attack_Stabilize)
 
1682
        {
 
1683
            List<Territory> targets = DUtils.GetTersThatUnitsCanReach(data, Collections.singletonList(unit), ter, GlobalCenter.CurrentPlayer, CompMatchAnd(Matches.TerritoryIsLand, DMatches.territoryIsOwnedByNNEnemy(data, GlobalCenter.CurrentPlayer)));
 
1684
            result -= targets.size(); //We want low-attack target units to attack
 
1685
        }
 
1686
        else if (task.GetTaskType() == CM_TaskType.LandGrab)
 
1687
        {
 
1688
            if(ua.isAir())
 
1689
                return Integer.MIN_VALUE;
 
1690
            //There's currently a flaw... A blitz unit that's already blitzed a ter is considered in it's 'start' territory for the next blitz (for 3 and up speed units)
 
1691
            int movementAfterBlitz = movementLeft - dist;
 
1692
            if(DSettings.LoadSettings().TR_attackLandGrab_onlyGrabLandIfWeCanBlitzIt)
 
1693
            {
 
1694
                if(!ua.getCanBlitz() || movementAfterBlitz < dist) //If this unit can't blitz, or it can't take ter and get back
 
1695
                    return Integer.MIN_VALUE;
 
1696
            }
 
1697
 
 
1698
            result += movementAfterBlitz * 10; //We want ones that can blitz away the most to attack
 
1699
            result -= dist; //If two halftracks match, we want the closer but with less movement one to blitz it
 
1700
        }
 
1701
        else if(task.GetTaskType() == CM_TaskType.Attack_Trade)
 
1702
        {
 
1703
            //Unit pairing or interlacing is, or will be, done in the CM_Task class for trade attacks
 
1704
        }
 
1705
        return result;
 
1706
    }
 
1707
    public static int HowWellIsUnitSuitedToTask(GameData data, NCM_Task task, Territory ter, Unit unit)
 
1708
    {
 
1709
        if(TacticalCenter.get(data, GlobalCenter.CurrentPlayer).GetFrozenUnits().contains(unit) && ter != task.GetTarget())
 
1710
            return Integer.MIN_VALUE;
 
1711
 
 
1712
        int result = 0;
 
1713
        Route route = CachedCalculationCenter.GetRoute(data, ter, task.GetTarget());
 
1714
        if (route == null)
 
1715
            return Integer.MIN_VALUE;
 
1716
        int dist = route.getLength();
 
1717
 
 
1718
        UnitAttachment ua = UnitAttachment.get(unit.getUnitType());
 
1719
        TripleAUnit ta = TripleAUnit.get(unit);
 
1720
        int tuv = DUtils.GetTUVOfUnit(unit, GlobalCenter.GetPUResource());
 
1721
        int movementSpeed = ua.getMovement(unit.getOwner());
 
1722
        int movementLeft = ta.getMovementLeft();
 
1723
 
 
1724
        if(TacticalCenter.get(data, GlobalCenter.CurrentPlayer).GetFrozenUnits().contains(unit))
 
1725
            movementLeft = 0;
 
1726
 
 
1727
        if(task.GetTaskType().equals(NCM_TaskType.Reinforce_Block))
 
1728
        {
 
1729
            if (ua.isAir())
 
1730
                return Integer.MIN_VALUE;
 
1731
            result -= tuv; //We will lose unit, so send cheapest
 
1732
        }
 
1733
        else if(task.GetTaskType().equals(NCM_TaskType.Reinforce_FrontLine))
 
1734
        {
 
1735
            if (ua.isAir())
 
1736
                return Integer.MIN_VALUE;
 
1737
            if (movementLeft == 0 && ter != task.GetTarget())
 
1738
                return Integer.MIN_VALUE;
 
1739
 
 
1740
            int turnsToGetThere = (int)Math.ceil((double)dist / (double)movementSpeed);
 
1741
 
 
1742
            result -= turnsToGetThere; //We want to reinforce as quickly as possible
 
1743
 
 
1744
            //If this is an AA, and we're reinforcing a ter with a factory and no AA yet, we boost the score for this AA
 
1745
            if(ua.isAA() && task.GetTarget().getUnits().getMatches(Matches.UnitIsFactory).size() > 0 && task.GetTarget().getUnits().getMatches(Matches.UnitIsAA).isEmpty())
 
1746
                result += 10;
 
1747
        }
 
1748
        else if (task.GetTaskType().equals(NCM_TaskType.Reinforce_Stabilize))
 
1749
        {
 
1750
            if (ua.isAir())
 
1751
                return Integer.MIN_VALUE;
 
1752
            if (movementLeft == 0 && ter != task.GetTarget())
 
1753
                return Integer.MIN_VALUE;
 
1754
 
 
1755
            int turnsToGetThere = (int)Math.ceil((double)dist / (double)movementSpeed);
 
1756
 
 
1757
            result -= turnsToGetThere; //We want to reinforce as quickly as possible
 
1758
 
 
1759
            //If this is an AA, and we're reinforcing a ter with a factory and no AA yet, we boost the score for this AA
 
1760
            if(ua.isAA() && task.GetTarget().getUnits().getMatches(Matches.UnitIsFactory).size() > 0 && task.GetTarget().getUnits().getMatches(Matches.UnitIsAA).isEmpty())
 
1761
                result += 10;
 
1762
        }
 
1763
        return result;
 
1764
    }
 
1765
    public static int HowWellIsUnitSuitedToCall(GameData data, NCM_Call call, Territory ter, Unit unit)
 
1766
    {
 
1767
        int result = 0;
 
1768
        Route route = CachedCalculationCenter.GetRoute(data, ter, call.GetTarget());
 
1769
        if (route == null)
 
1770
            return Integer.MIN_VALUE;
 
1771
        int dist = route.getLength();
 
1772
 
 
1773
        UnitAttachment ua = UnitAttachment.get(unit.getUnitType());
 
1774
        TripleAUnit ta = TripleAUnit.get(unit);
 
1775
        int tuv = DUtils.GetTUVOfUnit(unit, GlobalCenter.GetPUResource());
 
1776
        int movementSpeed = ua.getMovement(unit.getOwner());
 
1777
        int movementLeft = ta.getMovementLeft();
 
1778
 
 
1779
        //With calls, we'll consider recruiting a unit even if it is currently frozen (we don't need the unit to be able to attack or defend somewhere this round)
 
1780
        if(TacticalCenter.get(data, GlobalCenter.CurrentPlayer).GetFrozenUnits().contains(unit))
 
1781
            movementLeft = 0;
 
1782
 
 
1783
        if(call.GetCallType().equals(NCM_CallType.Call_ForDefensiveFront))
 
1784
        {
 
1785
            if (ua.isAir())
 
1786
                return Integer.MIN_VALUE;
 
1787
            //With calls, we'll consider recruiting a unit even if it is currently frozen (we don't need the unit to be able to attack or defend somewhere this round)
 
1788
            //if (movementLeft == 0 && ter != call.GetTarget())
 
1789
            //    return Integer.MIN_VALUE;
 
1790
            
 
1791
            int turnsToGetThere = (int)Math.ceil((double)dist / (double)movementSpeed);
 
1792
            
 
1793
            result -= turnsToGetThere; //We want to call units there as quickly as possible
 
1794
        }
 
1795
        else if(call.GetCallType().equals(NCM_CallType.Call_ForLandGrab))
 
1796
        {
 
1797
            if (ua.isAir())
 
1798
                return Integer.MIN_VALUE;
 
1799
            //With calls, we'll consider recruiting a unit even if it is currently frozen (we don't need the unit to be able to attack or defend somewhere this round)
 
1800
            //if (movementLeft == 0 && ter != call.GetTarget())
 
1801
            //    return Integer.MIN_VALUE;
 
1802
            
 
1803
            int turnsToGetThere = (int)Math.ceil((double)dist / (double)movementSpeed);
 
1804
            
 
1805
            result -= turnsToGetThere; //We want to call units there as quickly as possible
 
1806
        }
 
1807
        else if(call.GetCallType().equals(NCM_CallType.Call_ForCapitalDefense))
 
1808
        {
 
1809
            if (ua.isAir())
 
1810
                return Integer.MIN_VALUE;
 
1811
            //With calls, we'll consider recruiting a unit even if it is currently frozen (we don't need the unit to be able to attack or defend somewhere this round)
 
1812
            //if (movementLeft == 0 && ter != call.GetTarget())
 
1813
            //    return Integer.MIN_VALUE;
 
1814
            
 
1815
            int turnsToGetThere = (int)Math.ceil((double)dist / (double)movementSpeed);
 
1816
            
 
1817
            result -= turnsToGetThere; //We want to call units there as quickly as possible
 
1818
        }
 
1819
 
 
1820
        return result;
 
1821
    }
 
1822
    public static HashMap ToHashMap(Collection keys, Collection values)
 
1823
    {
 
1824
        HashMap result = new HashMap();
 
1825
        Iterator valueIter = values.iterator();
 
1826
        for(Object key : keys)
 
1827
            result.put(key, valueIter.next());
 
1828
        return result;
 
1829
    }
 
1830
    public static IntegerMap ToIntegerMap(HashMap map)
 
1831
    {
 
1832
        IntegerMap result = new IntegerMap(map.size());
 
1833
        for(Object key : map.keySet())
 
1834
            result.add(key, Integer.parseInt(map.get(key).toString()));
 
1835
        return result;
 
1836
    }
 
1837
    public static TreeMap ToTreeMap_AutoSortingByValues_A(Map map)
 
1838
    {
 
1839
        TreeMap result = new TreeMap(new ValueComparator_A(map));
 
1840
        result.putAll(map);
 
1841
        return result;
 
1842
    }
 
1843
    public static TreeMap ToTreeMap_AutoSortingByValues_D(Map map)
 
1844
    {
 
1845
        TreeMap result = new TreeMap(new ValueComparator_D(map));
 
1846
        result.putAll(map);
 
1847
        return result;
 
1848
    }
 
1849
    static class ValueComparator_A implements Comparator
 
1850
    {
 
1851
        Map base;
 
1852
        public ValueComparator_A(Map base)
 
1853
        {
 
1854
            this.base = base;
 
1855
        }
 
1856
        public int compare(Object a, Object b)
 
1857
        {
 
1858
            if ((Double) base.get(a) < (Double) base.get(b))
 
1859
                return 1;
 
1860
            else if ((Double) base.get(a) == (Double) base.get(b))
 
1861
                return 0;
 
1862
            else
 
1863
                return -1;
 
1864
        }
 
1865
    }
 
1866
    static class ValueComparator_D implements Comparator
 
1867
    {
 
1868
        Map base;
 
1869
        public ValueComparator_D(Map base)
 
1870
        {
 
1871
            this.base = base;
 
1872
        }
 
1873
        public int compare(Object a, Object b)
 
1874
        {
 
1875
            if ((Double) base.get(a) > (Double) base.get(b))
 
1876
                return 1;
 
1877
            else if ((Double) base.get(a) == (Double) base.get(b))
 
1878
                return 0;
 
1879
            else
 
1880
                return -1;
 
1881
        }
 
1882
    }
 
1883
    /**
 
1884
     * Determines the TUV lost by the attacker and defender based on the average battle outcome contained in the AggregateResults object.
 
1885
     * @param initialAttackers - The list of attackers before any casualties
 
1886
     * @param initialDefenders - The list of defenders before any casualties
 
1887
     * @param results - The AggregateResults object that contains the average battle outcome.
 
1888
     * @return a list of two integers. The first being the attacker's average TUV loss, the second being the defender's average TUV loss.
 
1889
     */
 
1890
    public static List<Integer> GetTUVChangeOfAttackerAndDefender(List<Unit> initialAttackers, List<Unit> initialDefenders, AggregateResults results)
 
1891
    {
 
1892
        PlayerID attacker = null;
 
1893
        if(!initialAttackers.isEmpty())
 
1894
            attacker = initialAttackers.get(0).getOwner();
 
1895
        PlayerID defender = null;
 
1896
        if(!initialDefenders.isEmpty())
 
1897
            defender = initialDefenders.get(0).getOwner();
 
1898
 
 
1899
        List<Unit> oldAttackerUnits = Match.getMatches(initialAttackers, DMatches.UnitCanAttack);
 
1900
        List<Unit> oldDefenderUnits = Match.getMatches(initialDefenders, DMatches.UnitCanDefend);
 
1901
 
 
1902
        List<Unit> newAttackerUnits = Match.getMatches(results.GetAverageAttackingUnitsRemaining(), DMatches.UnitCanAttack);
 
1903
        List<Unit> newDefenderUnits = Match.getMatches(results.GetAverageDefendingUnitsRemaining(), DMatches.UnitCanDefend);
 
1904
 
 
1905
        float oldAttackerTUV = DUtils.GetTUVOfUnits(oldAttackerUnits, GlobalCenter.GetPUResource());
 
1906
        float oldDefenderTUV = DUtils.GetTUVOfUnits(oldDefenderUnits, GlobalCenter.GetPUResource());
 
1907
 
 
1908
        float newAttackerTUV = DUtils.GetTUVOfUnits(newAttackerUnits, GlobalCenter.GetPUResource());
 
1909
        float newDefenderTUV = DUtils.GetTUVOfUnits(newDefenderUnits, GlobalCenter.GetPUResource());
 
1910
 
 
1911
        float attackerTUVGainOrLoss = newAttackerTUV - oldAttackerTUV;
 
1912
        float defenderTUVGainOrLoss = newDefenderTUV - oldDefenderTUV;
 
1913
 
 
1914
        List<Integer> result = new ArrayList<Integer>();
 
1915
        result.add((int)attackerTUVGainOrLoss);
 
1916
        result.add((int)defenderTUVGainOrLoss);
 
1917
        return result;
 
1918
    }
 
1919
    /**
 
1920
     * Returns defender's tuv loss minus attacker's TUV loss. TUV losses are contained in the array supplied, where first array int represents tuv loss for attacker, second for defender.
 
1921
     */
 
1922
    public static int GetTUVSwingForTUVChange(List<Integer> attackerAndDefenderTUVChanges)
 
1923
    {
 
1924
        return attackerAndDefenderTUVChanges.get(0) - attackerAndDefenderTUVChanges.get(1);
 
1925
    }
 
1926
    public static float GetSwingForBeforeAndAfterChange(List<Integer> beforeAndAfter)
 
1927
    {
 
1928
        return beforeAndAfter.get(1) - beforeAndAfter.get(0);
 
1929
    }
 
1930
    public static float GetSwingForBeforeAndAfterChange_F(List<Float> beforeAndAfter)
 
1931
    {
 
1932
        return beforeAndAfter.get(1) - beforeAndAfter.get(0);
 
1933
    }
 
1934
    public static List<Territory> GetTerritoriesWithinXLandJumpsOfTer(GameData data, Territory territory, int maxJumps, Match<Territory> resultTerMatch)
 
1935
    {
 
1936
        List<Territory> result = new ArrayList<Territory>();
 
1937
        for (Territory ter : data.getMap().getTerritories())
 
1938
        {
 
1939
            if (DUtils.CanWeGetFromXToY_ByPassableLand(data, ter, territory))
 
1940
            {
 
1941
                if (DUtils.GetJumpsFromXToY_PassableLand(data, ter, territory) <= maxJumps)
 
1942
                    result.add(ter);
 
1943
            }
 
1944
        }
 
1945
        return result;
 
1946
    }
 
1947
    /**
 
1948
     * Returns the chance of destruction of the supplied army if StrongestPlayerNonNullEnemyUnits that can reach army attack.
 
1949
     */
 
1950
    public static float GetVulnerabilityOfArmy(GameData data, PlayerID player, Territory ter, List<Unit> defendUnits, int calcRuns)
 
1951
    {
 
1952
        List<Unit> possibleAttackers = DUtils.GetSPNNEnemyUnitsThatCanReach(data, ter, player, Matches.TerritoryIsLand);
 
1953
        possibleAttackers = Match.getMatches(possibleAttackers, new CompositeMatchOr<Unit>(Matches.UnitIsLand, Matches.UnitIsAir));
 
1954
        AggregateResults results = DUtils.GetBattleResults(possibleAttackers, defendUnits, ter, data, calcRuns, true);
 
1955
 
 
1956
        float result = (float)results.getAttackerWinPercent();
 
1957
        return result;
 
1958
    }
 
1959
    /**
 
1960
     * Returns the chance of survival of the supplied army if StrongestPlayerNonNullEnemyUnits that can reach army attack.
 
1961
     */
 
1962
    public static float GetSurvivalChanceOfArmy(GameData data, PlayerID player, Territory ter, Collection<Unit> defendUnits, int calcRuns)
 
1963
    {
 
1964
        List<Unit> possibleAttackers = DUtils.GetSPNNEnemyUnitsThatCanReach(data, ter, player, Matches.TerritoryIsLand);
 
1965
        possibleAttackers = Match.getMatches(possibleAttackers, new CompositeMatchOr<Unit>(Matches.UnitIsLand, Matches.UnitIsAir));
 
1966
        AggregateResults results = DUtils.GetBattleResults(possibleAttackers, defendUnits, ter, data, calcRuns, true);
 
1967
 
 
1968
        float result = (float)results.getDefenderWinPercent();
 
1969
        return result;
 
1970
    }
 
1971
    public static float average(Float ... values)
 
1972
    {
 
1973
        float total = 0.0F;
 
1974
        for(Float val : values)
 
1975
            total += val;
 
1976
        return total / values.length;
 
1977
    }
 
1978
    public static List<Territory> GetTerritoriesWithinXDistanceOfY(GameData data, Territory start, int maxDistance)
 
1979
    {
 
1980
        return GetTerritoriesWithinXDistanceOfYMatchingZ(data, start, maxDistance, Match.ALWAYS_MATCH);
 
1981
    }
 
1982
    public static List<Territory> GetTerritoriesWithinXDistanceOfYMatchingZ(GameData data, Territory start, int maxDistance, Match<Territory> match)
 
1983
    {
 
1984
        return GetTerritoriesWithinXDistanceOfYMatchingZAndHavingRouteMatchingA(data, start, maxDistance, match, Match.ALWAYS_MATCH);
 
1985
    }
 
1986
    public static List<Territory> GetTerritoriesWithinXDistanceOfYMatchingZAndHavingRouteMatchingA(GameData data, Territory start, int maxDistance, Match<Territory> match, Match<Territory> routeMatch)
 
1987
    {
 
1988
        HashSet<Territory> processed = new HashSet<Territory>();
 
1989
        processed.add(start);
 
1990
 
 
1991
        List<Territory> result = new ArrayList<Territory>();        
 
1992
        HashSet<Territory> nextSet = new HashSet<Territory>(data.getMap().getNeighbors(start));
 
1993
        if(match.match(start))
 
1994
            result.add(start);
 
1995
        int dist = 1;
 
1996
        while(nextSet.size() > 0 && dist <= maxDistance)
 
1997
        {
 
1998
            HashSet<Territory> newSet = new HashSet<Territory>();
 
1999
            for(Territory ter : nextSet)
 
2000
            {
 
2001
                processed.add(ter);
 
2002
                if(routeMatch.match(ter))
 
2003
                {
 
2004
                    List<Territory> neighbors = DUtils.ToList(data.getMap().getNeighbors(ter));
 
2005
                    newSet.addAll(neighbors); //Add all this ter's neighbors to the next set for checking
 
2006
                    //(don't worry, neighbors already processed or in this current nextSet will be removed)
 
2007
                }
 
2008
                if (match.match(ter))
 
2009
                    result.add(ter);
 
2010
            }
 
2011
            newSet.removeAll(processed); //Don't check any that have been processed
 
2012
            nextSet = newSet;
 
2013
            dist++;
 
2014
        }
 
2015
        return result;
 
2016
    }
 
2017
    public static List<Territory> GetEnemySeaTersThatCanBeAttackedBySeaOrAirUnitsOwnedBy(GameData data, PlayerID player)
 
2018
    {
 
2019
        List<Territory> result = new ArrayList<Territory>();
 
2020
        for (Territory ter : data.getMap().getTerritories())
 
2021
        {
 
2022
            if(!ter.isWater())
 
2023
                continue;
 
2024
 
 
2025
            List<Unit> possibleAttackers = DUtils.GetUnitsOwnedByPlayerThatCanReach(data, ter, player, Matches.TerritoryIsLand);
 
2026
            possibleAttackers = Match.getMatches(possibleAttackers, new CompositeMatchOr<Unit>(Matches.UnitIsSea, Matches.UnitIsAir));
 
2027
 
 
2028
            if(possibleAttackers.size() > 0)
 
2029
                result.add(ter);
 
2030
        }
 
2031
        return result;
 
2032
    }
 
2033
    public static List CloneList(Collection list)
 
2034
    {
 
2035
        return new ArrayList(list);
 
2036
    }
 
2037
    public static UnitType GetRandomUnitType()
 
2038
    {
 
2039
        return (UnitType)GlobalCenter.GetMergedAndAveragedProductionFrontier().getRules().get(0).getResults().keySet().iterator().next();
 
2040
    }
 
2041
    /**
 
2042
     * Runs simulated battles numerous times and returns an AggregateResults object that lists the percent of times the attacker won, lost, etc.
 
2043
     * @param attacking - The units that are attacking in this battle
 
2044
     * @param defending - The units that are defending in this battle
 
2045
     * @param testingTer  - The territory this battle will be simulated on. (You might be able to use any territory, I'm unsure)
 
2046
     * @param data - The game data containing the map, units, players, etc.
 
2047
     * @param runCount - How many times to simulate the battle. The more it's simulated, the more accurate the results will be
 
2048
     * @param toTake - Whether the attacker needs to have a unit left over after the attack to take the territory for a battle simulation to be counted as a win
 
2049
     * @return Returns an AggregateResults object that lists the percent of times the attacker won, lost, etc.
 
2050
     */
 
2051
    public static AggregateResults GetBattleResults(Collection<Unit> attacking, Collection<Unit> defending, Territory testingTer, GameData data, int runCount, boolean toTake)
 
2052
    {
 
2053
        if (attacking == null || attacking.isEmpty())
 
2054
        {
 
2055
            if (defending == null || defending.isEmpty())
 
2056
            {
 
2057
                if (toTake) //If the calculation is to check for takeover and armies are empty, never set as draw, set it as defender win(defender keeps ter)
 
2058
                    return CreateDefenderWinsAggregateResults(data, testingTer, defending); //Signal as defender wins
 
2059
                else
 
2060
                    return CreateDrawAggregateResults(data, testingTer); //Signal as draw
 
2061
            }            
 
2062
            return CreateDefenderWinsAggregateResults(data, testingTer, defending);//Signal as defender wins
 
2063
        }
 
2064
        else if(defending == null || defending.isEmpty())
 
2065
        {
 
2066
            if(toTake && Match.getNMatches(attacking, 1, Matches.UnitIsLand).isEmpty()) //If we're supposed to take ter, but we don't have any land attacking
 
2067
                return CreateDefenderWinsAggregateResults(data, testingTer, defending); //Signal as defender wins(defender keeps ter)
 
2068
            return CreateAttackerWinsAggregateResults(data, testingTer, attacking); //Signal as attacker wins
 
2069
        }
 
2070
 
 
2071
        PlayerID attacker = attacking.iterator().next().getOwner();
 
2072
        PlayerID defender = defending.iterator().next().getOwner();
 
2073
 
 
2074
        if (runCount != 1)
 
2075
        {
 
2076
            if (DSettings.LoadSettings().AllowCalcingDecrease && Dynamix_AI.GetTimeTillNextScheduledActionDisplay() == 0) //Hmmm... Let's try to speed things up to reach the user-specified action length
 
2077
                runCount = (int) DUtils.Limit(runCount * DUtils.ToFloat(DSettings.LoadSettings().CalcingDecreaseToPercentage), 1.0F, Integer.MAX_VALUE);
 
2078
 
 
2079
            float attackerUnitsStrength = DUtils.GetAttackScoreOfUnits(attacking);
 
2080
            float defenderUnitsStrength = DUtils.GetDefenseScoreOfUnits(defending);
 
2081
 
 
2082
            if (attackerUnitsStrength > defenderUnitsStrength * 2) //If attacker has a huge attack/defense score advantage
 
2083
                runCount = (int) DUtils.Limit(runCount / ((attackerUnitsStrength / defenderUnitsStrength) * 5), 1.0F, Integer.MAX_VALUE); //Then reduce calcing count, as we're pretty sure attacker will win
 
2084
            else if (defenderUnitsStrength > attackerUnitsStrength * 2)
 
2085
                runCount = (int) DUtils.Limit(runCount / ((defenderUnitsStrength / attackerUnitsStrength) * 5), 1.0F, Integer.MAX_VALUE); //Then reduce calcing count, as we're pretty sure defender will win
 
2086
 
 
2087
            if(Properties.getLow_Luck(data))
 
2088
                runCount = (int) DUtils.Limit(runCount / 5.0F, 10.0F, Integer.MAX_VALUE); //Reduce calcing count, as we're playing with LL
 
2089
        }
 
2090
 
 
2091
        DOddsCalculator calc = new DOddsCalculator();
 
2092
        calc.setKeepOneAttackingLandUnit(toTake);
 
2093
 
 
2094
        AggregateResults results = calc.calculate(data, attacker, defender, testingTer, attacking, defending, new ArrayList<Unit>(), runCount);
 
2095
        if(toTake) //If we're supposed to 'take' ter
 
2096
        {
 
2097
            //But the attackers averaged without a land unit left (or there are no attackers left after battle)
 
2098
            if(results == null || results.GetAverageAttackingUnitsRemaining() == null || Match.getNMatches(results.GetAverageAttackingUnitsRemaining(), 1, Matches.unitIsLandAndOwnedBy(attacker)).isEmpty())
 
2099
                return CreateDefenderWinsAggregateResults(data, testingTer, defending); //Signal as defender wins
 
2100
        }
 
2101
        return results;
 
2102
    }
 
2103
    public static AggregateResults CreateAttackerWinsAggregateResults(GameData data, Territory ter, Collection<Unit> attacking)
 
2104
    {        
 
2105
        MustFightBattle battle = new MustFightBattle(ter, PlayerID.NULL_PLAYERID, data, null);
 
2106
        battle.setUnits(new ArrayList<Unit>(), attacking, new ArrayList<Unit>(), PlayerID.NULL_PLAYERID);
 
2107
        BattleResults result = new BattleResults(battle);
 
2108
        AggregateResults dWins = new AggregateResults(1);
 
2109
        dWins.addResult(result);
 
2110
        return dWins;
 
2111
    }
 
2112
    public static AggregateResults CreateDefenderWinsAggregateResults(GameData data, Territory ter, Collection<Unit> defending)
 
2113
    {
 
2114
        MustFightBattle battle = new MustFightBattle(ter, PlayerID.NULL_PLAYERID, data, null);
 
2115
        battle.setUnits(defending, new ArrayList<Unit>(), new ArrayList<Unit>(), PlayerID.NULL_PLAYERID);
 
2116
        BattleResults result = new BattleResults(battle);
 
2117
        AggregateResults dWins = new AggregateResults(1);
 
2118
        dWins.addResult(result);
 
2119
        return dWins;
 
2120
    }
 
2121
    public static AggregateResults CreateDrawAggregateResults(GameData data, Territory ter)
 
2122
    {
 
2123
        MustFightBattle battle = new MustFightBattle(ter, PlayerID.NULL_PLAYERID, data, null);
 
2124
        battle.setUnits(new ArrayList<Unit>(), new ArrayList<Unit>(), new ArrayList<Unit>(), PlayerID.NULL_PLAYERID);
 
2125
        BattleResults result = new BattleResults(battle);
 
2126
        AggregateResults dWins = new AggregateResults(1);
 
2127
        dWins.addResult(result);
 
2128
        return dWins;
 
2129
    }
 
2130
    public static List<Unit> getMoveableUnits(List<Unit> units)
 
2131
    {
 
2132
        List<Unit> values = new ArrayList<Unit>();
 
2133
        Iterator<Unit> iter = units.iterator();
 
2134
        while (iter.hasNext())
 
2135
        {
 
2136
            Unit unit = iter.next();
 
2137
            if (Matches.unitHasMovementLeft.match(unit))
 
2138
                values.add(unit);
 
2139
        }
 
2140
        return values;
 
2141
    }
 
2142
    public static int GetTaskTradeScore(GameData data, Territory target, List<Unit> attackers, List<Unit> defenders, AggregateResults simulatedAttack, List<Unit> responseAttackers, List<Unit> responseDefenders, AggregateResults simulatedResponse)
 
2143
    {
 
2144
        if(simulatedAttack.getAttackerWinPercent() < .5F)
 
2145
            return DConstants.Integer_HalfMin;
 
2146
 
 
2147
        List<Integer> tuvLosses = DUtils.GetTUVChangeOfAttackerAndDefender(attackers, defenders, simulatedAttack);
 
2148
        int tuvSwing = DUtils.GetTUVSwingForTUVChange(tuvLosses);
 
2149
        int responseTUVSwing = 0;
 
2150
        if (simulatedResponse != null) //Will be null if the caller didn't want this method to care about counter-attacks
 
2151
        {
 
2152
            List<Integer> responseTUVLosses = DUtils.GetTUVChangeOfAttackerAndDefender(responseAttackers, responseDefenders, simulatedResponse);
 
2153
            responseTUVSwing = DUtils.GetTUVSwingForTUVChange(responseTUVLosses);
 
2154
        }
 
2155
        int terProduction = GetProduction_PlusExtra(target);
 
2156
        if(Match.getMatches(simulatedAttack.GetAverageAttackingUnitsRemaining(), Matches.UnitIsLand).isEmpty()) //If no land units survive, on average
 
2157
            terProduction = 0; //Don't count in ter production
 
2158
 
 
2159
        int score = terProduction + tuvSwing;
 
2160
        if(responseTUVSwing > 0) //If it makes sense for the enemy to counter-attack us
 
2161
            score = score - (responseTUVSwing / 2); //Then count our loss in this counter attack against us
 
2162
 
 
2163
        return score;
 
2164
    }
 
2165
    public static List<UnitGroup> TrimRecruits_NonMovedOnes(List<UnitGroup> list, int toTrim)
 
2166
    {
 
2167
        int trimmed = 0;
 
2168
        List<UnitGroup> result = new ArrayList<UnitGroup>();
 
2169
        for(int i = list.size() - 1;i >= 0;i--)
 
2170
        {
 
2171
            UnitGroup ug = list.get(i);
 
2172
            if(ug.GetMovedTo() != null || trimmed >= toTrim)
 
2173
                result.add(ug);
 
2174
            else
 
2175
                trimmed++;
 
2176
        }
 
2177
        return result;
 
2178
    }
 
2179
    public static Route TrimRoute_AtFirstTerWithEnemyUnits(Route route, int newRouteJumpCount, PlayerID player, GameData data)
 
2180
    {
 
2181
        return TrimRoute_AtFirstTerMatchingX(route, newRouteJumpCount, player, data, Matches.territoryHasUnitsThatMatch(new CompositeMatchAnd<Unit>(Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1), Matches.unitIsEnemyOf(data, player))));
 
2182
    }
 
2183
    public static Route TrimRoute_AtFirstTerMatchingX(Route route, int newRouteJumpCount, PlayerID player, GameData data, Match<Territory> match)
 
2184
    {
 
2185
        List<Territory> newTers = new ArrayList<Territory>();
 
2186
        int i = 0;
 
2187
        for (Territory ter : route.getTerritories())
 
2188
        {
 
2189
            newTers.add(ter);
 
2190
            if (match.match(ter))
 
2191
                break;
 
2192
            i++;
 
2193
            if (i > newRouteJumpCount)
 
2194
                break;
 
2195
        }
 
2196
        return new Route(newTers);
 
2197
    }
 
2198
    public static Route TrimRoute_AtLastFriendlyTer(Route route, int newRouteJumpCount, PlayerID player, GameData data)
 
2199
    {
 
2200
        return TrimRoute_BeforeFirstTerMatching(route, newRouteJumpCount, player, data, DMatches.territoryIsOwnedByEnemy(data, player));
 
2201
    }
 
2202
    public static Route TrimRoute_BeforeFirstTerMatching(Route route, int newRouteJumpCount, PlayerID player, GameData data, Match<Territory> match)
 
2203
    {
 
2204
        List<Territory> newTers = new ArrayList<Territory>();
 
2205
        int i = 0;
 
2206
        for(Territory ter : route.getTerritories())
 
2207
        {
 
2208
            if(match.match(ter))
 
2209
                break;
 
2210
            newTers.add(ter);
 
2211
            i++;
 
2212
            if(i > newRouteJumpCount)
 
2213
                break;
 
2214
        }
 
2215
        if(newTers.size() < 2)
 
2216
            return null;
 
2217
        return new Route(newTers);
 
2218
    }
 
2219
    public static Route TrimRoute_BeforeFirstTerWithEnemyUnits(Route route, int newRouteJumpCount, PlayerID player, GameData data)
 
2220
    {
 
2221
        return TrimRoute_BeforeFirstTerMatching(route, newRouteJumpCount, player, data, Matches.territoryHasUnitsThatMatch(new CompositeMatchAnd<Unit>(Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1), Matches.unitIsEnemyOf(data, player))));
 
2222
    }
 
2223
    public static Route TrimRoute_ToLength(Route route, int newRouteJumpCount, PlayerID player, GameData data)
 
2224
    {
 
2225
        List<Territory> newTers = new ArrayList<Territory>();
 
2226
        int i = 0;
 
2227
        for(Territory ter : route.getTerritories())
 
2228
        {
 
2229
            newTers.add(ter);
 
2230
            i++;
 
2231
            if(i > newRouteJumpCount)
 
2232
                break;
 
2233
        }
 
2234
        if(newTers.size() < 2)
 
2235
            return null;
 
2236
        return new Route(newTers);
 
2237
    }
 
2238
    public static int GetJumpsFromXToY_NoCond(GameData data, Territory ter1, Territory ter2)
 
2239
    {
 
2240
        Route route = CachedCalculationCenter.GetRoute(data, ter1, ter2);
 
2241
        if(route == null)
 
2242
            return Integer.MAX_VALUE;
 
2243
        return route.getLength();
 
2244
    }
 
2245
    public static int GetJumpsFromXToY_AirPassable(GameData data, Territory ter1, Territory ter2)
 
2246
    {
 
2247
        Route route = CachedCalculationCenter.GetAirPassableRoute(data, ter1, ter2);
 
2248
        if(route == null)
 
2249
            return Integer.MAX_VALUE;
 
2250
        return route.getLength();
 
2251
    }
 
2252
    /**
 
2253
     * Almost always, you'll want to use GetJumpsFromXToY_PassableLand instead of this
 
2254
     */
 
2255
    public static int GetJumpsFromXToY_Land(GameData data, Territory ter1, Territory ter2)
 
2256
    {
 
2257
        Route route = CachedCalculationCenter.GetLandRoute(data, ter1, ter2);
 
2258
        if(route == null)
 
2259
            return DConstants.Integer_HalfMax;
 
2260
        if(ter1.getName().equals(ter2.getName()) || route.getLength() < 1)
 
2261
            return DConstants.Integer_HalfMax;
 
2262
        return route.getLength();
 
2263
    }
 
2264
    public static int GetJumpsFromXToY_PassableLand(GameData data, Territory ter1, Territory ter2)
 
2265
    {
 
2266
        Route route = CachedCalculationCenter.GetPassableLandRoute(data, ter1, ter2);
 
2267
        if(route == null)
 
2268
            return DConstants.Integer_HalfMax;
 
2269
        if(ter1.getName().equals(ter2.getName()) || route.getLength() < 1)
 
2270
            return DConstants.Integer_HalfMax;
 
2271
        return route.getLength();
 
2272
    }
 
2273
    public static int GetJumpsFromXToY_Sea(GameData data, Territory ter1, Territory ter2)
 
2274
    {
 
2275
        Route route = CachedCalculationCenter.GetSeaRoute(data, ter1, ter2);
 
2276
        if(route == null)
 
2277
            return DConstants.Integer_HalfMax;
 
2278
        if(ter1.getName().equals(ter2.getName()) || route.getLength() < 1)
 
2279
            return DConstants.Integer_HalfMax;
 
2280
        return route.getLength();
 
2281
    }
 
2282
    /**
 
2283
     * Almost always, you'll want to use CanWeGetFromXToY_ByPassableLand instead of this
 
2284
     */
 
2285
    public static boolean CanWeGetFromXToY_ByLand(GameData data, Territory ter1, Territory ter2)
 
2286
    {
 
2287
        if(ter1 == null || ter2 == null)
 
2288
            return false;
 
2289
        return CachedCalculationCenter.GetLandRoute(data, ter1, ter2) != null;
 
2290
    }
 
2291
    public static boolean CanWeGetFromXToY_ByPassableLand(GameData data, Territory ter1, Territory ter2)
 
2292
    {
 
2293
        if(ter1 == null || ter2 == null)
 
2294
            return false;
 
2295
        return CachedCalculationCenter.GetPassableLandRoute(data, ter1, ter2) != null;
 
2296
    }
 
2297
    public static boolean CanWeGetFromXToY_BySea(GameData data, Territory ter1, Territory ter2)
 
2298
    {
 
2299
        if(ter1 == null || ter2 == null)
 
2300
            return false;
 
2301
        return CachedCalculationCenter.GetSeaRoute(data, ter1, ter2) != null;
 
2302
    }
 
2303
    public static boolean CanWeAttackFromXToY_ByLand(GameData data, PlayerID player, Territory ter1, Territory ter2)
 
2304
    {
 
2305
        return GetAttackRouteFromXToY_ByLand(data, player, ter1, ter2) != null;
 
2306
    }
 
2307
    public static Route GetAttackRouteFromXToY_ByLand(GameData data, PlayerID player, Territory ter1, Territory ter2)
 
2308
    {
 
2309
        if(ter2.isWater())
 
2310
            return null;
 
2311
        if(ter1 == null || ter2 == null)
 
2312
            return null;
 
2313
        return data.getMap().getRoute_IgnoreEnd(ter1, ter2, new CompositeMatchAnd<Territory>(Matches.TerritoryIsLand, Matches.TerritoryIsNotImpassable, new InverseMatch<Territory>(Matches.territoryHasUnitsThatMatch(new CompositeMatchAnd<Unit>(Matches.unitIsEnemyOf(data, player), Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1))))));
 
2314
    }
 
2315
    public static Route GetAttackRouteFromXToY_ByLand_CountZAsPassthroughs(GameData data, PlayerID player, Territory ter1, Territory ter2, List<Territory> passthroughTers)
 
2316
    {
 
2317
        if(ter2.isWater())
 
2318
            return null;
 
2319
        if(ter1 == null || ter2 == null)
 
2320
            return null;
 
2321
        List<Match> matches = new ArrayList<Match>();
 
2322
        matches.add(new CompositeMatchAnd<Territory>(Matches.TerritoryIsLand, Matches.TerritoryIsNotImpassable, new InverseMatch<Territory>(Matches.territoryHasUnitsThatMatch(new CompositeMatchAnd<Unit>(Matches.unitIsEnemyOf(data, player), Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1))))));
 
2323
        for(Territory ter : passthroughTers)
 
2324
            matches.add(Matches.territoryIs(ter));
 
2325
        return data.getMap().getRoute_IgnoreEnd(ter1, ter2, CompMatchOr(matches));
 
2326
    }
 
2327
    public static Route GetAttackRouteFromXToY_BySea(GameData data, PlayerID player, Territory ter1, Territory ter2)
 
2328
    {
 
2329
        if(!ter2.isWater())
 
2330
            return null;
 
2331
        if(ter1 == null || ter2 == null)
 
2332
            return null;
 
2333
        return data.getMap().getRoute_IgnoreEnd(ter1, ter2, new CompositeMatchAnd<Territory>(Matches.TerritoryIsWater, new InverseMatch<Territory>(Matches.territoryHasUnitsThatMatch(new CompositeMatchAnd<Unit>(Matches.unitIsEnemyOf(data, player), Matches.unitHasDefenseThatIsMoreThanOrEqualTo(1))))));
 
2334
    }
 
2335
    public static Route GetNCMRouteFromXToY_ByLand(GameData data, PlayerID player, Territory ter1, Territory ter2)
 
2336
    {
 
2337
        if(ter2.isWater())
 
2338
            return null;
 
2339
        if(ter1 == null || ter2 == null)
 
2340
            return null;
 
2341
        return data.getMap().getRoute_IgnoreEnd(ter1, ter2, new CompositeMatchAnd<Territory>(Matches.TerritoryIsLand, Matches.TerritoryIsNotImpassable, DMatches.territoryIsOwnedByXOrAlly(data, player)));
 
2342
    }
 
2343
    public static boolean HasNeighborsThatMatch(GameMap map, Territory ter, Match<Territory> match)
 
2344
    {
 
2345
        return Match.someMatch(map.getNeighbors(ter), match);
 
2346
    }
 
2347
    public static int GetTotalProductionOfTerritoriesInList(List<Territory> territories)
 
2348
    {
 
2349
        int result = 0;
 
2350
        for(Territory ter : territories)
 
2351
        {
 
2352
            TerritoryAttachment ta = TerritoryAttachment.get(ter);
 
2353
            if(ta == null)
 
2354
                continue;
 
2355
 
 
2356
            result += ta.getProduction();
 
2357
        }
 
2358
        return result;
 
2359
    }
 
2360
 
 
2361
 
 
2362
 
 
2363
 
 
2364
 
 
2365
    /**
 
2366
     * Returns all the units matching X that can reach target.
 
2367
     */
 
2368
    public static List<Unit> GetUnitsMatchingXThatCanReach(final GameData data, Territory target, Match<Territory> terMatch, Match<Unit> unitMatch)
 
2369
    {
 
2370
        return GetNUnitsMatchingXThatCanReach(data, target, terMatch, unitMatch, Integer.MAX_VALUE);
 
2371
    }
 
2372
    /**
 
2373
     * Returns all the units matching X that can reach target, or could reach target if passthroughTers were empty.
 
2374
     */
 
2375
    public static List<Unit> GetUnitsMatchingXThatCanReach_CountYAsPassthroughs(final GameData data, Territory target, Match<Territory> terMatch, Match<Unit> unitMatch, List<Territory> passthroughTers)
 
2376
    {
 
2377
        return GetNUnitsMatchingXThatCanReach_CountYAsPassthroughs(data, target, terMatch, unitMatch, passthroughTers, Integer.MAX_VALUE);
 
2378
    }
 
2379
    /**
 
2380
     * Returns the n first units matching X that can reach target.
 
2381
     */
 
2382
    public static List<Unit> GetNUnitsMatchingXThatCanReach(final GameData data, Territory target, Match<Territory> terMatch, Match<Unit> unitMatch, int maxResults)
 
2383
    {
 
2384
        return GetNUnitsMatchingXThatCanReach_CountYAsPassthroughs(data, target, terMatch, unitMatch, new ArrayList<Territory>(), maxResults);
 
2385
    }
 
2386
    /**
 
2387
     * Returns the n first units matching X that can reach target, or could reach target if passthroughTers were empty.
 
2388
     */
 
2389
    public static List<Unit> GetNUnitsMatchingXThatCanReach_CountYAsPassthroughs(final GameData data, Territory target, Match<Territory> terMatch, Match<Unit> unitMatch, List<Territory> passthroughTers, int maxResults)
 
2390
    {
 
2391
        List<Unit> result = new ArrayList<Unit>();
 
2392
        for (Territory ter : data.getMap().getTerritories())
 
2393
        {
 
2394
            if(!terMatch.match(ter))
 
2395
                continue;
 
2396
 
 
2397
            List<Unit> matchingUnits = Match.getMatches(ToList(ter.getUnits().getUnits()), unitMatch);
 
2398
            for(Unit unit : matchingUnits)
 
2399
            {
 
2400
                if(CanUnitReachTer(data, ter, unit, target, passthroughTers))
 
2401
                {
 
2402
                    result.add(unit);
 
2403
                    if(result.size() >= maxResults)
 
2404
                        return result;
 
2405
                }
 
2406
            }
 
2407
        }
 
2408
        return result;
 
2409
    }
 
2410
    /**
 
2411
     * Returns all the units matching X that can reach target.
 
2412
     */
 
2413
    public static HashMap<Territory, List<Unit>> GetUnitsMatchingXThatCanReach_Mapped(final GameData data, Territory target, Match<Territory> terMatch, Match<Unit> unitMatch)
 
2414
    {
 
2415
        return GetUnitsMatchingXThatCanReach_Mapped_CountYAsPassthroughs(data, target, terMatch, unitMatch, new ArrayList<Territory>());
 
2416
    }
 
2417
    /**
 
2418
     * Returns all the units matching X that can reach target.
 
2419
     */
 
2420
    public static HashMap<Territory, List<Unit>> GetUnitsMatchingXThatCanReach_Mapped_CountYAsPassthroughs(final GameData data, Territory target, Match<Territory> terMatch, Match<Unit> unitMatch, List<Territory> passthroughTers)
 
2421
    {
 
2422
        HashMap<Territory, List<Unit>> result = new HashMap<Territory, List<Unit>>();
 
2423
        for (Territory ter : data.getMap().getTerritories())
 
2424
        {
 
2425
            if(!terMatch.match(ter))
 
2426
                continue;
 
2427
 
 
2428
            List<Unit> matchingUnits = Match.getMatches(ToList(ter.getUnits().getUnits()), unitMatch);
 
2429
            for(Unit unit : matchingUnits)
 
2430
            {
 
2431
                if(CanUnitReachTer(data, ter, unit, target, passthroughTers))
 
2432
                    AddObjToListValueForKeyInMap(result, ter, unit);
 
2433
            }
 
2434
        }
 
2435
        return result;
 
2436
    }
 
2437
    /**
 
2438
     * Returns all the units owned by player that can reach target.
 
2439
     */
 
2440
    public static List<Unit> GetUnitsOwnedByPlayerThatCanReach(final GameData data, Territory target, final PlayerID playerToCheckFor, Match<Territory> terMatch)
 
2441
    {
 
2442
        return GetUnitsMatchingXThatCanReach(data, target, terMatch, Matches.unitIsOwnedBy(playerToCheckFor));
 
2443
    }
 
2444
    /**
 
2445
     * (GetStrongestPlayerNonNullEnemyUnitsThatCanReach_Mapped)
 
2446
     * First, determines all the enemy units that can reach territory.
 
2447
     * Then, it estimates the attack score of the units owned by each player.
 
2448
     * Then, it returns all the units owned by that player that can reach territory, in a ter mapped hashmap.
 
2449
     */
 
2450
    public static HashMap<Territory, List<Unit>> GetSPNNEnemyUnitsThatCanReach_Mapped(final GameData data, Territory territory, final PlayerID playerToCheckFor, Match<Territory> terSearchMatch)
 
2451
    {
 
2452
        HashMap<Territory, List<Unit>> result = GetNNEnemyUnitsThatCanReach_Mapped(data, territory, playerToCheckFor, terSearchMatch);
 
2453
        return GetTheUnitsOfTheStrongestPlayerContainedInMap_Mapped(result);
 
2454
    }    
 
2455
    /**
 
2456
     * (GetNonNullEnemyUnitsThatCanReach)
 
2457
     * Returns all the non-null-enemy units that can reach territory.
 
2458
     */
 
2459
    public static List<Unit> GetNNEnemyUnitsThatCanReach(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch)
 
2460
    {
 
2461
        return GetNNEnemyUnitsThatCanReach_CountXAsPassthroughs(data, target, player, terMatch, new ArrayList<Territory>());
 
2462
    }
 
2463
    /**
 
2464
     * (GetNNonNullEnemyUnitsThatCanReach)
 
2465
     * Returns the n first non-null-enemy units that can reach territory.
 
2466
     */
 
2467
    public static List<Unit> GetNNNEnemyUnitsThatCanReach(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch, int maxResults)
 
2468
    {
 
2469
        return GetNNNEnemyUnitsThatCanReach_CountXAsPassthroughs(data, target, player, terMatch, new ArrayList<Territory>(), maxResults);
 
2470
    }
 
2471
    /**
 
2472
     * (GetNonNullEnemyUnitsThatCanReach_Mapped)
 
2473
     * Returns a hashmap that lists the enemy units that can reach <code>territory</code>, where key is the ter with attackers, and value is the list of attackers in that ter.
 
2474
     */
 
2475
    public static HashMap<Territory, List<Unit>> GetNNEnemyUnitsThatCanReach_Mapped(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch)
 
2476
    {
 
2477
        return GetUnitsMatchingXThatCanReach_Mapped(data, target, terMatch, DMatches.unitIsNNEnemyOf(data, player));
 
2478
    }
 
2479
    /**
 
2480
     * (GetNonNullEnemyUnitsThatCanReach_CountXAsPassthroughs)
 
2481
     * Returns all the enemy units that can reach territory, or could reach territory if passthrough ters were empty of enemies.
 
2482
     */
 
2483
    public static List<Unit> GetNNEnemyUnitsThatCanReach_CountXAsPassthroughs(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch, List<Territory> passthroughTers)
 
2484
    {
 
2485
        return GetNNNEnemyUnitsThatCanReach_CountXAsPassthroughs(data, target, player, terMatch, passthroughTers, Integer.MAX_VALUE);
 
2486
    }
 
2487
    /**
 
2488
     * (GetNNonNullEnemyUnitsThatCanReach_CountXAsPassthroughs)
 
2489
     * Returns the n first enemy units that can reach territory, or could reach territory if passthrough ters were empty of enemies.
 
2490
     */
 
2491
    public static List<Unit> GetNNNEnemyUnitsThatCanReach_CountXAsPassthroughs(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch, List<Territory> passthroughTers, int maxResults)
 
2492
    {
 
2493
        return GetNUnitsMatchingXThatCanReach_CountYAsPassthroughs(data, target, terMatch, DMatches.unitIsNNEnemyOf(data, player), passthroughTers, maxResults);
 
2494
    }
 
2495
    /**
 
2496
     * (GetNonNullEnemyLandUnitsThatCanReach)
 
2497
     * Returns all the non-null-enemy land units that can reach territory.
 
2498
     */
 
2499
    public static List<Unit> GetNNEnemyLUnitsThatCanReach(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch)
 
2500
    {
 
2501
        return GetNNNEnemyLUnitsThatCanReach(data, target, player, terMatch, Integer.MAX_VALUE);
 
2502
    }
 
2503
    /**
 
2504
     * (GetNNonNullEnemyLandUnitsThatCanReach)
 
2505
     * Returns the n first non-null-enemy land units that can reach territory.
 
2506
     */
 
2507
    public static List<Unit> GetNNNEnemyLUnitsThatCanReach(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch, int maxResults)
 
2508
    {
 
2509
        return GetNUnitsMatchingXThatCanReach(data, target, terMatch, CompMatchAnd(Matches.UnitIsLand, DMatches.unitIsNNEnemyOf(data, player)), maxResults);
 
2510
    }
 
2511
    /**
 
2512
     * (GetStrongestPlayerUnitsMatchingXThatCanReach)
 
2513
     * First, determines all the units matching X that can reach territory.
 
2514
     * Then, it estimates the attack score of the units owned by each player.
 
2515
     * Then, it returns all the units that can reach territory that are also owned by the player whose units had the highest total score.
 
2516
     */
 
2517
    public static List<Unit> GetSPUnitsMatchingXThatCanReach(final GameData data, Territory territory, final PlayerID playerToCheckFor, Match<Territory> terSearchMatch, Match<Unit> unitMatch)
 
2518
    {
 
2519
        List<Unit> result = GetUnitsMatchingXThatCanReach(data, territory, terSearchMatch, unitMatch);
 
2520
        return GetSPUnitsInList(result);
 
2521
    }
 
2522
 
 
2523
    /**
 
2524
     * (GetStrongestPlayerNonNullEnemyUnitsThatCanReach)
 
2525
     * First, determines all the enemy units that can reach territory.
 
2526
     * Then, it estimates the attack score of the units owned by each player.
 
2527
     * Then, it returns all the units that can reach territory that are also owned by the player whose units had the highest total score.
 
2528
     */
 
2529
    public static List<Unit> GetSPNNEnemyUnitsThatCanReach(final GameData data, Territory territory, final PlayerID playerToCheckFor, Match<Territory> terMatch)
 
2530
    {
 
2531
        List<Unit> result = GetNNEnemyUnitsThatCanReach(data, territory, playerToCheckFor, terMatch);
 
2532
        return GetSPUnitsInList(result);
 
2533
    }
 
2534
 
 
2535
    /**
 
2536
     * (GetStrongestPlayerNonNullEnemyUnitsThatCanReach_CountXAsPassthroughs)
 
2537
     * First, determines all the enemy units that can reach territory, or could reach territory if passthrough ters were empty of enemies.
 
2538
     * Then, it estimates the attack score of the units owned by each player.
 
2539
     * Then, it returns all the units that can reach territory that are also owned by the player whose units had the highest total score.
 
2540
     */
 
2541
    public static List<Unit> GetSPNNEnemyUnitsThatCanReach_CountXAsPassthroughs(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch, List<Territory> passthroughTers)
 
2542
    {
 
2543
        List<Unit> result = GetNNEnemyUnitsThatCanReach_CountXAsPassthroughs(data, target, player, terMatch, passthroughTers);
 
2544
        return GetSPUnitsInList(result);
 
2545
    }
 
2546
 
 
2547
    /**
 
2548
     * (GetStrongestPlayerNonNullEnemyWithLandUnitsThatCanReach)
 
2549
     * First, determines all the enemy units that can reach territory.
 
2550
     * Then, it estimates the attack score of the units owned by each player. (And owns land units in the list)
 
2551
     * Then, it returns all the units that can reach territory that are also owned by the player whose units had the highest total score.
 
2552
     */
 
2553
    public static List<Unit> GetSPNNEnemyWithLUnitsThatCanReach(final GameData data, Territory target, final PlayerID playerToCheckFor, Match<Territory> terMatch)
 
2554
    {
 
2555
        List<Unit> result = GetNNEnemyUnitsThatCanReach(data, target, playerToCheckFor, terMatch);
 
2556
        return GetTheUnitsOfTheStrongestPlayerWithLUContainedInList(result);
 
2557
    }
 
2558
 
 
2559
    /**
 
2560
     * (GetStrongestPlayerNonNullEnemyWithLandUnitsThatCanReach_CountXAsPassthrough)
 
2561
     * First, determines all the enemy units that can reach territory, or could reach territory if passthrough ter was empty of enemies.
 
2562
     * Then, it estimates the attack score of the units owned by each player. (And owns land units in the list)
 
2563
     * Then, it returns all the units that can reach territory that are also owned by the player whose units had the highest total score.
 
2564
     */
 
2565
    public static List<Unit> GetSPNNEnemyWithLUnitsThatCanReach_CountXAsPassthrough(final GameData data, Territory target, final PlayerID player, Match<Territory> terMatch, Territory passthroughTer)
 
2566
    {
 
2567
        return GetSPNNEnemyWithLUnitsThatCanReach_CountXAsPassthroughs(data, target, player, terMatch, Collections.singletonList(passthroughTer));
 
2568
    }
 
2569
 
 
2570
    /**
 
2571
     * (GetStrongestPlayerNonNullEnemyWithLandUnitsThatCanReach_CountXAsPassthroughs)
 
2572
     * First, determines all the enemy units that can reach territory, or could reach territory if passthrough ters were empty of enemies.
 
2573
     * Then, it estimates the attack score of the units owned by each player. (And owns land units in the list)
 
2574
     * Then, it returns all the units that can reach territory that are also owned by the player whose units had the highest total score.
 
2575
     */
 
2576
    public static List<Unit> GetSPNNEnemyWithLUnitsThatCanReach_CountXAsPassthroughs(final GameData data, Territory territory, final PlayerID playerToCheckFor, Match<Territory> terSearchMatch, List<Territory> passthroughTers)
 
2577
    {
 
2578
        List<Unit> result = GetNNEnemyUnitsThatCanReach_CountXAsPassthroughs(data, territory, playerToCheckFor, terSearchMatch, passthroughTers);
 
2579
        return GetTheUnitsOfTheStrongestPlayerWithLUContainedInList(result);
 
2580
    }
 
2581
 
 
2582
    /**
 
2583
     * (GetStrongestPlayerNonNullEnemyBasedOnLUnitsOnlyThatCanReach)
 
2584
     * First, determines all the enemy land units that can reach territory.
 
2585
     * Then, it estimates the attack score of the units owned by each player.
 
2586
     * Then, it returns all the land units that can reach territory that are also owned by the player whose land units had the highest total score.
 
2587
     */
 
2588
    public static List<Unit> GetSPNNEnemyBasedOnLUnitsOnlyThatCanReach(final GameData data, Territory territory, final PlayerID playerToCheckFor, Match<Territory> terSearchMatch)
 
2589
    {
 
2590
        List<Unit> result = GetNNEnemyLUnitsThatCanReach(data, territory, playerToCheckFor, terSearchMatch);
 
2591
        return GetTheUnitsOfTheStrongestPlayerWithLUContainedInList(result);
 
2592
    }
 
2593
 
 
2594
 
 
2595
 
 
2596
 
 
2597
    /** (GetStrongestPlayerUnitsInList)
 
2598
     * First, groups the units in the list by their owners.
 
2599
     * Then, it determines the total estimated 'attack score' of each group.
 
2600
     * Then, it returns the group of units that has the highest estimated attack score.
 
2601
     */
 
2602
    public static List<Unit> GetSPUnitsInList(List<Unit> unitsToSearch)
 
2603
    {
 
2604
        HashMap<String, List<Unit>> attackersUnits = new HashMap<String, List<Unit>>();
 
2605
        List<Unit> highestAttackerUnits = new ArrayList<Unit>();
 
2606
        for (Unit u : unitsToSearch)
 
2607
        {
 
2608
            if (!attackersUnits.containsKey(u.getOwner().getName()))
 
2609
            {
 
2610
                List<Unit> newList = new ArrayList<Unit>();
 
2611
                newList.add(u);
 
2612
                attackersUnits.put(u.getOwner().getName(), newList);
 
2613
            }
 
2614
            else
 
2615
            {
 
2616
                List<Unit> newList = attackersUnits.get(u.getOwner().getName());
 
2617
                newList.add(u);
 
2618
                attackersUnits.put(u.getOwner().getName(), newList);
 
2619
            }
 
2620
        }
 
2621
        float highestAttackerUStrength = Integer.MIN_VALUE;
 
2622
        for (String key : attackersUnits.keySet())
 
2623
        {
 
2624
            List<Unit> units = attackersUnits.get(key);
 
2625
            float strength = DUtils.GetAttackScoreOfUnits(units);
 
2626
            if (strength > highestAttackerUStrength)
 
2627
            {
 
2628
                highestAttackerUStrength = strength;
 
2629
                highestAttackerUnits = units;
 
2630
            }
 
2631
        }
 
2632
        return highestAttackerUnits;
 
2633
    }
 
2634
    /**
 
2635
     * First, determines which player owns most of the units. (And owns land units that are in the list)
 
2636
     * Then, it returns all the units owned by that player.
 
2637
     */
 
2638
    public static List<Unit> GetTheUnitsOfTheStrongestPlayerWithLUContainedInList(List<Unit> unitsToSearch)
 
2639
    {
 
2640
        HashMap<String, List<Unit>> attackersUnits = new HashMap<String, List<Unit>>();
 
2641
        List<Unit> highestAttackerUnits = new ArrayList<Unit>();
 
2642
        for (Unit u : unitsToSearch)
 
2643
        {
 
2644
            if (!attackersUnits.containsKey(u.getOwner().getName()))
 
2645
            {
 
2646
                List<Unit> newList = new ArrayList<Unit>();
 
2647
                newList.add(u);
 
2648
                attackersUnits.put(u.getOwner().getName(), newList);
 
2649
            }
 
2650
            else
 
2651
            {
 
2652
                List<Unit> newList = attackersUnits.get(u.getOwner().getName());
 
2653
                newList.add(u);
 
2654
                attackersUnits.put(u.getOwner().getName(), newList);
 
2655
            }
 
2656
        }
 
2657
        float highestAttackerUStrength = Integer.MIN_VALUE;
 
2658
        for (String key : attackersUnits.keySet())
 
2659
        {
 
2660
            List<Unit> units = attackersUnits.get(key);
 
2661
            boolean foundLand = Match.someMatch(units, Matches.UnitIsLand);
 
2662
            if(!foundLand)
 
2663
                continue;
 
2664
            float strength = DUtils.GetAttackScoreOfUnits(units);
 
2665
            if (strength > highestAttackerUStrength)
 
2666
            {
 
2667
                highestAttackerUStrength = strength;
 
2668
                highestAttackerUnits = units;
 
2669
            }
 
2670
        }
 
2671
        return highestAttackerUnits;
 
2672
    }
 
2673
    /**
 
2674
     * First, determines which player owns most of the units.
 
2675
     * Then, it returns all the units owned by that player.
 
2676
     */
 
2677
    public static HashMap<Territory, List<Unit>> GetTheUnitsOfTheStrongestPlayerContainedInMap_Mapped(HashMap<Territory, List<Unit>> unitsToSearch)
 
2678
    {
 
2679
        HashMap<String, List<Unit>> attackersUnits = new HashMap<String, List<Unit>>();
 
2680
        for (Territory ter : unitsToSearch.keySet())
 
2681
        {
 
2682
            for (Unit unit : unitsToSearch.get(ter))
 
2683
            {
 
2684
                AddObjToListValueForKeyInMap(attackersUnits, unit.getOwner().getName(), unit);
 
2685
            }
 
2686
        }
 
2687
 
 
2688
        HashSet<Unit> highestAttackerUnits = new HashSet<Unit>();
 
2689
        float highestAttackerUStrength = Integer.MIN_VALUE;
 
2690
        for (String key : attackersUnits.keySet())
 
2691
        {
 
2692
            List<Unit> units = attackersUnits.get(key);
 
2693
            float strength = DUtils.GetAttackScoreOfUnits(units);
 
2694
            if (strength > highestAttackerUStrength)
 
2695
            {
 
2696
                highestAttackerUStrength = strength;
 
2697
                highestAttackerUnits = ToHashSet(units);
 
2698
            }
 
2699
        }
 
2700
 
 
2701
        HashMap<Territory, List<Unit>> highestAttackerUnits_Mapped = new HashMap<Territory, List<Unit>>();
 
2702
        for(Territory ter : unitsToSearch.keySet())
 
2703
        {
 
2704
            for(Unit terUnit : unitsToSearch.get(ter))
 
2705
            {
 
2706
                if(highestAttackerUnits.contains(terUnit))
 
2707
                    AddObjToListValueForKeyInMap(highestAttackerUnits_Mapped, ter, terUnit);
 
2708
            }
 
2709
        }
 
2710
        return highestAttackerUnits_Mapped;
 
2711
    }
 
2712
    public static boolean CanAirUnitLandWithXSurvivalChanceIfAttackingFromXToY(GameData data, final Territory from, Territory to, Unit airUnit, float survivalChance)
 
2713
    {
 
2714
        TripleAUnit ta = TripleAUnit.get(airUnit);
 
2715
        int jumpDist = DUtils.GetJumpsFromXToY_AirPassable(data, from, to);
 
2716
        int movementAfterAttack = ta.getMovementLeft() - jumpDist;
 
2717
        for (Territory ter : data.getMap().getTerritories())
 
2718
        {
 
2719
            if (!data.getAllianceTracker().isAllied(ter.getOwner(), airUnit.getOwner()))
 
2720
                continue;
 
2721
            int dist = DUtils.GetJumpsFromXToY_AirPassable(data, ter, to);
 
2722
            if(dist > movementAfterAttack)
 
2723
                continue;
 
2724
 
 
2725
            if (survivalChance != 0.0F)
 
2726
            {
 
2727
                //TODO: If we find all attackers, we cause airplane determining endless loop. Current hack: Only figure in land units that can attack
 
2728
                List<Unit> attackers = GetSPNNEnemyBasedOnLUnitsOnlyThatCanReach(data, ter, airUnit.getOwner(), Matches.TerritoryIsLand);
 
2729
                List<Unit> defenders = ToList(ter.getUnits().getUnits());
 
2730
                if(data.getAllianceTracker().isAtWar(airUnit.getOwner(), GlobalCenter.CurrentPlayer)) //If we're checking if an enemy plane can land
 
2731
                {
 
2732
                    List<Territory> neighbors = DUtils.GetTerritoriesWithinXDistanceOfY(data, ter, 1);
 
2733
                    defenders = DUtils.GetUnitsMatchingXInTerritories(neighbors, Matches.unitIsLandAndOwnedBy(airUnit.getOwner()));
 
2734
                }
 
2735
                defenders.remove(airUnit);
 
2736
                defenders.add(airUnit);
 
2737
                AggregateResults results = DUtils.GetBattleResults(attackers, defenders, ter, data, 1, false); //False, so we don't leave air in our land but going to get killed by enemy air
 
2738
                if(results.getDefenderWinPercent() >= survivalChance)
 
2739
                    return true; //We found a place to land
 
2740
            }
 
2741
            else
 
2742
                return true; //We found a place to land
 
2743
        }
 
2744
        return false;
 
2745
    }
 
2746
    public static int CountLandUnits(List<Unit> units)
 
2747
    {
 
2748
        int result = 0;
 
2749
        for (Unit u : units)
 
2750
        {
 
2751
            UnitAttachment ua = UnitAttachment.get(u.getUnitType());
 
2752
            if (!ua.isSea() && !ua.isAir())
 
2753
            {
 
2754
                result++;
 
2755
            }
 
2756
        }
 
2757
        return result;
 
2758
    }
 
2759
    public static HashSet ToHashSet(Collection collection)
 
2760
    {
 
2761
        HashSet result = new HashSet();
 
2762
        for(Object obj : collection)
 
2763
            result.add(obj);
 
2764
        return result;
 
2765
    }
 
2766
    public static List InvertList(Collection list)
 
2767
    {
 
2768
        ArrayList result = new ArrayList(list);
 
2769
        Collections.reverse(result);
 
2770
        return result;
 
2771
    }
 
2772
    public static List ShuffleList(Collection list)
 
2773
    {
 
2774
        ArrayList result = new ArrayList(list);
 
2775
        Collections.shuffle(result);
 
2776
        return result;
 
2777
    }
 
2778
    public static List GetXPercentOfTheItemsInList(Collection list, float percentageToKeep)
 
2779
    {
 
2780
        if(percentageToKeep == 1.0F)
 
2781
            return new ArrayList(list);
 
2782
        if(percentageToKeep == 0.0F)
 
2783
            return new ArrayList();
 
2784
        ArrayList result = new ArrayList();
 
2785
        for (Object obj : list)
 
2786
        {
 
2787
            if(Math.random() < percentageToKeep)
 
2788
                result.add(obj);
 
2789
        }
 
2790
        return result;
 
2791
    }
 
2792
    public static List<Unit> GetXPercentOfTheUnitsInList_CreateMoreIfNeeded(Collection<Unit> units, float percentageToResultIn)
 
2793
    {
 
2794
        if(percentageToResultIn == 1.0F)
 
2795
            return new ArrayList<Unit>(units);
 
2796
        if(percentageToResultIn == 0.0F)
 
2797
            return new ArrayList<Unit>();
 
2798
        ArrayList<Unit> result = new ArrayList<Unit>();
 
2799
        while (percentageToResultIn > 1.0F)
 
2800
        {
 
2801
            for (Unit unit : units)
 
2802
                result.add(unit.getUnitType().create(unit.getOwner())); //We have to create a new instance of that unit type
 
2803
            percentageToResultIn -= 1.0F;
 
2804
        }
 
2805
        for (Unit unit : units)
 
2806
        {
 
2807
            if(Math.random() < percentageToResultIn)
 
2808
                result.add(unit.getUnitType().create(unit.getOwner())); //We have to create a new instance of that unit type
 
2809
        }
 
2810
        return result;
 
2811
    }
 
2812
    public static List<Unit> RecreateXPercentOfTheUnitsInList_CreateMoreIfNeeded(Collection<Unit> units, float percentageToResultIn)
 
2813
    {
 
2814
        if(percentageToResultIn == 1.0F)
 
2815
            return new ArrayList<Unit>(units);
 
2816
        if(percentageToResultIn == 0.0F)
 
2817
            return new ArrayList<Unit>();
 
2818
        ArrayList<Unit> result = new ArrayList<Unit>();
 
2819
        while (percentageToResultIn > 1.0F)
 
2820
        {
 
2821
            for (Unit unit : units)
 
2822
                result.add(unit.getUnitType().create(unit.getOwner())); //We have to create a new instance of that unit type
 
2823
            percentageToResultIn -= 1.0F;
 
2824
        }
 
2825
        for (Unit unit : units)
 
2826
        {
 
2827
            if(Math.random() < percentageToResultIn)
 
2828
                result.add(unit.getUnitType().create(unit.getOwner())); //We have to create a new instance of that unit type
 
2829
        }
 
2830
        return result;
 
2831
    }
 
2832
    public static List<Unit> ToUnitList(Collection<UnitGroup> ugs)
 
2833
    {
 
2834
        List<Unit> result = new ArrayList<Unit>();
 
2835
        for(UnitGroup ug : ugs)
 
2836
            result.addAll(ug.GetUnits());
 
2837
        return result;
 
2838
    }
 
2839
    /**
 
2840
     * Formats the units in a list of unit groups.
 
2841
     * Before: "infantry owned by Americans, infantry owned by Americans, infantry owned by Americans, armour owned by Americans, fighter owned by Americans"
 
2842
     * After:  "3 infantry, armour, and fighter owned by Americans"
 
2843
     */
 
2844
    public static String UnitGroupList_ToString(Collection<UnitGroup> ugs)
 
2845
    {
 
2846
        List<Unit> units = ToUnitList(ugs);
 
2847
 
 
2848
        return UnitList_ToString(units);
 
2849
    }
 
2850
    /**
 
2851
     * Formats the list of units provided.
 
2852
     * Before: "infantry owned by Americans, infantry owned by Americans, infantry owned by Americans, armour owned by Americans, fighter owned by Americans"
 
2853
     * After:  "3 infantry, armour, and fighter owned by Americans"
 
2854
     */
 
2855
    public static String UnitList_ToString(Collection<Unit> units)
 
2856
    {
 
2857
        if (units.isEmpty())
 
2858
            return "(Empty)";
 
2859
        if(units.size() == 1)
 
2860
            return units.iterator().next().toString();
 
2861
 
 
2862
        StringBuilder builder = new StringBuilder();
 
2863
        builder.append("[");
 
2864
        HashMap<String, List<Unit>> unitsByOwner = new HashMap<String, List<Unit>>();
 
2865
        for(Unit unit : units)
 
2866
            AddObjToListValueForKeyInMap(unitsByOwner, unit.getOwner().getName(), unit);
 
2867
 
 
2868
        for(String owner : unitsByOwner.keySet())
 
2869
        {
 
2870
            int unitGroups = 0;
 
2871
            String lastUnitType = null;
 
2872
            int lastUnitTypeCount = 0;
 
2873
            for(Unit unit : unitsByOwner.get(owner))
 
2874
            {
 
2875
                if(lastUnitType == null) //First unit
 
2876
                {
 
2877
                    unitGroups = 1;
 
2878
                    lastUnitType = unit.getUnitType().getName();
 
2879
                    lastUnitTypeCount = 1;
 
2880
                }
 
2881
                else if(unit.getUnitType().getName().equals(lastUnitType)) //Part of a group
 
2882
                    lastUnitTypeCount++;
 
2883
                else //End of last group, start of next
 
2884
                {
 
2885
                    if(unitGroups != 1) //If this is not the end of the first group
 
2886
                        builder.append(", ");
 
2887
                    if(lastUnitTypeCount == 1) //If the last group was only one unit
 
2888
                        builder.append(lastUnitType);
 
2889
                    else
 
2890
                        builder.append(lastUnitTypeCount).append(" ").append(MyFormatter.pluralize(lastUnitType));
 
2891
 
 
2892
                    lastUnitType = unit.getUnitType().getName();
 
2893
                    lastUnitTypeCount = 1;
 
2894
                    unitGroups++;
 
2895
                }
 
2896
            }
 
2897
            if (unitGroups > 1)
 
2898
                builder.append(", and ");
 
2899
            if (lastUnitTypeCount == 1)
 
2900
                builder.append(lastUnitType).append(" owned by ").append(owner);
 
2901
            else
 
2902
                builder.append(lastUnitTypeCount).append(" ").append(MyFormatter.pluralize(lastUnitType)).append(" owned by ").append(owner);
 
2903
        }
 
2904
        builder.append("]");
 
2905
        return builder.toString();
 
2906
    }
 
2907
    public static List<Unit> DetermineResponseAttackers(GameData data, PlayerID player, Territory battleTer, AggregateResults results)
 
2908
    {
 
2909
        List<Unit> responseAttackers = DUtils.GetSPNNEnemyUnitsThatCanReach(data, battleTer, player, Matches.TerritoryIsLand);
 
2910
        responseAttackers.removeAll(battleTer.getUnits().getUnits());
 
2911
        return responseAttackers;
 
2912
    }    
 
2913
    /**
 
2914
     * Returns the units that will be on ter after factory units are placed at end of turn.
 
2915
     */
 
2916
    public static List<Unit> GetUnitsGoingToBePlacedAtX(GameData data, PlayerID player, Territory ter)
 
2917
    {
 
2918
        PurchaseGroup terPG = FactoryCenter.get(data, player).TurnTerritoryPurchaseGroups.get(ter);
 
2919
        List<Unit> goingToBePlaced = new ArrayList<Unit>();
 
2920
        if (terPG != null)
 
2921
            goingToBePlaced = terPG.GetSampleUnits();
 
2922
 
 
2923
        return goingToBePlaced;
 
2924
    }
 
2925
    /**
 
2926
     * Returns the chances ter would get taken over if (SPNNEnemyWithLUnits)'s units that can reach ter attack.
 
2927
     */
 
2928
    public static float GetTerTakeoverChance(GameData data, PlayerID player, Territory ter)
 
2929
    {
 
2930
        List<Unit> oldCapDefenders = new ArrayList<Unit>(ter.getUnits().getUnits()); //Cap defenders before move
 
2931
        List<Unit> oldCapAttackers = DUtils.GetSPNNEnemyWithLUnitsThatCanReach(data, ter, player, Matches.TerritoryIsLand); //Cap attackers before move
 
2932
 
 
2933
        AggregateResults oldResults = DUtils.GetBattleResults(oldCapAttackers, oldCapDefenders, ter, data, 1000, true); //Takeover results before move
 
2934
 
 
2935
        return (float)oldResults.getAttackerWinPercent();
 
2936
    }
 
2937
    /**
 
2938
     * Returns the chances ter would get taken over after factory units were placed at end of turn if (SPNNEnemyWithLUnits)'s units that can reach ter attack.
 
2939
     */
 
2940
    public static float GetTerTakeoverChanceAtEndOfTurn(GameData data, PlayerID player, Territory ter)
 
2941
    {
 
2942
        List<Unit> oldTerDefenders = GetTerUnitsAtEndOfTurn(data, player, ter);
 
2943
        List<Unit> oldTerAttackers = DUtils.GetSPNNEnemyWithLUnitsThatCanReach(data, ter, player, Matches.TerritoryIsLand); //Cap attackers before move
 
2944
 
 
2945
        AggregateResults oldResults = DUtils.GetBattleResults(oldTerAttackers, oldTerDefenders, ter, data, 1000, true); //Takeover results before move
 
2946
 
 
2947
        return (float)oldResults.getAttackerWinPercent();
 
2948
    }
 
2949
    /**
 
2950
     * Returns the units that will be placed down on ter at the end of the turn.
 
2951
     */
 
2952
    public static List<Unit> GetTerUnitsGoingToBePlacedAt(GameData data, PlayerID player, Territory ter)
 
2953
    {
 
2954
        PurchaseGroup terPG = FactoryCenter.get(data, player).TurnTerritoryPurchaseGroups.get(ter);
 
2955
        List<Unit> goingToBePlaced = new ArrayList<Unit>();
 
2956
        if (terPG != null)
 
2957
            goingToBePlaced = terPG.GetSampleUnits();
 
2958
 
 
2959
        return goingToBePlaced;
 
2960
    }
 
2961
    /**
 
2962
     * Returns the units that will be on ter after factory units are placed at end of turn.
 
2963
     */
 
2964
    public static List<Unit> GetTerUnitsAtEndOfTurn(GameData data, PlayerID player, Territory ter)
 
2965
    {
 
2966
        List<Unit> goingToBePlaced = GetTerUnitsGoingToBePlacedAt(data, player, ter);
 
2967
 
 
2968
        List<Unit> result = new ArrayList<Unit>(ter.getUnits().getUnits()); //Current ter units
 
2969
        result.addAll(goingToBePlaced);
 
2970
        return result;
 
2971
    }
 
2972
    /**
 
2973
     * Returns the chances ter would get taken over after move if (SPNNEnemyWithLUnits)'s units that can reach ter attack.
 
2974
     * List item 1: Takeover chance before move
 
2975
     * List item 2: Takeover chance after move
 
2976
     * List item 3: Average number of attack units left before move
 
2977
     * List item 4: Average number of attack units left after move
 
2978
     */
 
2979
    public static List<Float> GetTerTakeoverChanceBeforeAndAfterMove(GameData data, PlayerID player, Territory terToCheck, Territory movedTo, List<Unit> unitsToMove, int calcAmount)
 
2980
    {
 
2981
        return GetTerTakeoverChanceBeforeAndAfterMoves(data, player, terToCheck, Collections.singletonList(movedTo), unitsToMove, calcAmount);
 
2982
    }
 
2983
    /**
 
2984
     * Returns the chances ter would get taken over after moves if (SPNNEnemyWithLUnits)'s units that can reach ter attack.
 
2985
     * List item 1: Takeover chance before moves
 
2986
     * List item 2: Takeover chance after moves
 
2987
     * List item 3: Average number of attack units left before moves
 
2988
     * List item 4: Average number of attack units left after moves
 
2989
     */
 
2990
    public static List<Float> GetTerTakeoverChanceBeforeAndAfterMoves(GameData data, PlayerID player, Territory terToCheck, List<Territory> movedToTers, List<Unit> unitsToMove, int calcAmount)
 
2991
    {
 
2992
        List<Float> result = new ArrayList<Float>();
 
2993
 
 
2994
        List<Territory> movedFromTersThatBecomeEmpty = new ArrayList<Territory>();
 
2995
        for(Territory ter : data.getMap().getTerritories())
 
2996
        {
 
2997
            if(ter.isWater())
 
2998
                continue;
 
2999
            if(data.getAllianceTracker().isAtWar(ter.getOwner(), player))
 
3000
                continue;
 
3001
            if(ter.getUnits().isEmpty())
 
3002
                continue;
 
3003
            List<Unit> unitsOnTerBeingMoved = Match.getMatches(ter.getUnits().getUnits(), DMatches.unitIsInList(unitsToMove));
 
3004
            if(unitsOnTerBeingMoved.size() == ter.getUnits().size() && GetUnitsGoingToBePlacedAtX(data, player, ter).isEmpty()) //If all the units on this ter will be gone after this move
 
3005
                movedFromTersThatBecomeEmpty.add(ter);
 
3006
        }
 
3007
 
 
3008
        List<Unit> unitsToMoveThatAreOnTerToCheck = new ArrayList<Unit>(unitsToMove);
 
3009
        unitsToMoveThatAreOnTerToCheck.retainAll(terToCheck.getUnits().getUnits());
 
3010
 
 
3011
        PurchaseGroup terPG = FactoryCenter.get(data, player).TurnTerritoryPurchaseGroups.get(terToCheck);
 
3012
        List<Unit> goingToBePlaced = new ArrayList<Unit>();
 
3013
        if(terPG != null)
 
3014
            goingToBePlaced = terPG.GetSampleUnits();
 
3015
 
 
3016
        List<Unit> oldTerDefenders = new ArrayList<Unit>(terToCheck.getUnits().getUnits()); //Ter defenders before move
 
3017
        oldTerDefenders.addAll(goingToBePlaced);
 
3018
        List<Unit> oldTerAttackers = DUtils.GetSPNNEnemyWithLUnitsThatCanReach(data, terToCheck, player, Matches.TerritoryIsLand); //Ter attackers before move
 
3019
        
 
3020
        AggregateResults oldResults = DUtils.GetBattleResults(oldTerAttackers, oldTerDefenders, terToCheck, data, calcAmount, true); //Takeover results before move
 
3021
        
 
3022
        List<Unit> newTerDefenders = new ArrayList<Unit>(oldTerDefenders); //Ter defenders after move
 
3023
        newTerDefenders.removeAll(unitsToMoveThatAreOnTerToCheck);
 
3024
        if(movedToTers.contains(terToCheck))
 
3025
        {
 
3026
            newTerDefenders.removeAll(unitsToMove); //Don't double add
 
3027
            newTerDefenders.addAll(unitsToMove);
 
3028
        }
 
3029
 
 
3030
        List<Unit> newTerAttackers = DUtils.GetSPNNEnemyWithLUnitsThatCanReach_CountXAsPassthroughs(data, terToCheck, player, CompMatchAnd(Matches.TerritoryIsLand, Matches.territoryIsNotInList(movedToTers)), movedFromTersThatBecomeEmpty); //Ter attackers after move
 
3031
        
 
3032
        //Now look through the old attack-from enemy territories, and remove the units from the list of new attackers if the attack route will be blocked after the move
 
3033
        List<Territory> attackFromLocs = DUtils.GetEnemyTerritoriesWithinXLandDistanceThatHaveEnemyUnitsThatCanAttack(terToCheck, data, player, GlobalCenter.FastestUnitMovement);
 
3034
        for (Territory from : attackFromLocs)
 
3035
        {
 
3036
            Route route = DUtils.GetAttackRouteFromXToY_ByLand(data, from.getOwner(), from, terToCheck);
 
3037
            if(route != null)
 
3038
            {
 
3039
                boolean doTheMovesBlockThisAttack = false;
 
3040
                for(Territory to : movedToTers) //Look through each move
 
3041
                {
 
3042
                    if (route.getTerritories().contains(to) && !route.getEnd().equals(to)) //And check if this attack route is blocked by it
 
3043
                    {
 
3044
                        doTheMovesBlockThisAttack = true;
 
3045
                        break;
 
3046
                    }
 
3047
                }
 
3048
                if(doTheMovesBlockThisAttack)
 
3049
                    newTerAttackers.removeAll(from.getUnits().getUnits());
 
3050
            }
 
3051
        }
 
3052
 
 
3053
        AggregateResults newResults = DUtils.GetBattleResults(newTerAttackers, newTerDefenders, terToCheck, data, calcAmount, true); //Takeover results after move
 
3054
 
 
3055
        result.add((float)oldResults.getAttackerWinPercent());
 
3056
        result.add((float)newResults.getAttackerWinPercent());
 
3057
        result.add((float)oldResults.getAverageAttackingUnitsLeft());
 
3058
        result.add((float)newResults.getAverageAttackingUnitsLeft());
 
3059
 
 
3060
        return result;
 
3061
    }
 
3062
    /**
 
3063
     * Returns the number that is the farthest from 0.
 
3064
     */
 
3065
    public static float GetMostExtremeNum(List<Float> numbers)
 
3066
    {
 
3067
        float result = 0;
 
3068
        float farthestNumDist = 0F;
 
3069
        for(Float num : numbers)
 
3070
        {
 
3071
            if (MNN(num) > farthestNumDist)
 
3072
            {
 
3073
                farthestNumDist = MNN(num);
 
3074
                result = num;
 
3075
            }
 
3076
        }
 
3077
        return result;
 
3078
    }
 
3079
    /**
 
3080
     * (ScaleNumbersTillWithinRange_Positive)
 
3081
     * Scales the numbers provided so the numbers range from 0 to ceiling, whether by scaling up or scaling down.
 
3082
     * (If highest number is below ceiling, numbers are 'stretched' till max number reaches ceiling, if highest number is above, numbers are 'compacted' till max number reaches ceiling)
 
3083
     */
 
3084
    public static List<Float> ScaleNumbersTillWithinRange_P(float ceiling, float ... numbers)
 
3085
    {
 
3086
        float mostExtremeNum = GetMostExtremeNum(ToList(ToArray(numbers)));
 
3087
 
 
3088
        float numberScaleToRange = mostExtremeNum / ceiling;
 
3089
 
 
3090
        List<Float> result = new ArrayList<Float>();
 
3091
        for(Float number : numbers)
 
3092
        {
 
3093
            result.add(number / numberScaleToRange);
 
3094
        }
 
3095
        return result;
 
3096
    }
 
3097
    /**
 
3098
     * (Divide_Safe_Limit)
 
3099
     * Performs a divide using 'safe' versions of the quotient and divisor, and returns the value as a 'limited' number. (0.0F-1.0F)
 
3100
     */
 
3101
    public static float Divide_SL(float quotient, float divisor)
 
3102
    {
 
3103
        return Limit(Divide_S(quotient, divisor));
 
3104
    }
 
3105
    /**
 
3106
     * (Divide_Safe)
 
3107
     * Performs a divide using 'safe' versions of the quotient and divisor and returns the value.
 
3108
     */
 
3109
    public static float Divide_S(float quotient, float divisor)
 
3110
    {
 
3111
        quotient = MNZ(quotient);
 
3112
        divisor = MNZ(divisor);
 
3113
 
 
3114
        return quotient / divisor;
 
3115
    }
 
3116
    /**
 
3117
     * @return Returns a limited version of the number. (On or between min and max)
 
3118
     */
 
3119
    public static float Limit(float value, float min, float max)
 
3120
    {
 
3121
        return Math.min(Math.max(value, min), max);
 
3122
    }
 
3123
    /**
 
3124
     * @return Returns a limited version of the number. (On or between 0.0F and 1.0F)
 
3125
     */
 
3126
    public static float Limit(float value)
 
3127
    {
 
3128
        return Limit(value, 0.0F, 1.0F);
 
3129
    }
 
3130
    /**
 
3131
     * (MakeNonZero)
 
3132
     * @param value - The number to make non-zero
 
3133
     * @return - Returns 0.001F if the number is 0.0F, otherwise returns the number itself
 
3134
     */
 
3135
    public static float MNZ(float value)
 
3136
    {
 
3137
        if (value == 0.0F)
 
3138
            value = 0.001F;
 
3139
        return value;
 
3140
    }
 
3141
    /**
 
3142
     * (MakeNonNegative)
 
3143
     * @param value - The number to make non-negative
 
3144
     * @return - Returns an unsigned version of the number (removes the - sign, if it exists)
 
3145
     */
 
3146
    public static float MNN(float value)
 
3147
    {
 
3148
        if (value < 0.0F)
 
3149
            value = -value;
 
3150
        return value;
 
3151
    }
 
3152
    /**
 
3153
     * Returns the territories that units in list can attack.
 
3154
     */
 
3155
    public static List<Territory> GetTersThatUnitsCanReach(final GameData data, List<Unit> units, Territory territory, final PlayerID playerToCheckFor, Match<Territory> terSearchMatch)
 
3156
    {
 
3157
        List<Territory> result = new ArrayList<Territory>();
 
3158
        for (Territory ter : data.getMap().getTerritories())
 
3159
        {
 
3160
            if(!terSearchMatch.match(ter))
 
3161
                continue;
 
3162
            for (Unit u : units)
 
3163
            {
 
3164
                if(CanUnitReachTer(data, ter, u, territory))
 
3165
                    result.add(ter);
 
3166
            }
 
3167
        }
 
3168
 
 
3169
        return result;
 
3170
    }
 
3171
    /**
 
3172
     * Returns the territories matching X that the units on territory can attack.
 
3173
     */
 
3174
    public static List<Territory> GetTersThatMatchXThatUnitsOnTerCanAttack(final GameData data, Territory territory, Match<Territory> terMatch, final PlayerID player)
 
3175
    {
 
3176
        List<Territory> reachableMatches = new ArrayList<Territory>();
 
3177
        for (Territory ter : data.getMap().getTerritories())
 
3178
        {
 
3179
            if(!terMatch.match(ter))
 
3180
                continue;
 
3181
            for (Unit u : ter.getUnits().getMatches(Matches.unitIsOwnedBy(player)))
 
3182
            {
 
3183
                if(CanUnitReachTer(data, ter, u, territory))
 
3184
                {
 
3185
                    reachableMatches.add(ter);
 
3186
                    break;
 
3187
                }
 
3188
            }
 
3189
        }
 
3190
 
 
3191
        return reachableMatches;
 
3192
    }
 
3193
    /**
 
3194
     * Determines which unit in the list will increase the chance of battle winning the most, if chances before were already 1.0F, bases it off of how many attacking units are saved by adding this unit.
 
3195
     * (More powerful units should destroy enemy units faster, thereby reducing attacker's casualties more)
 
3196
     * (Atm, this method does not work well...)
 
3197
     */
 
3198
    public static Unit CalculateUnitThatWillHelpWinAttackOnArmyTheMostPerPU(Territory testTer, GameData data, PlayerID player, Collection<Unit> unitsAlreadyAttacking, Collection<Unit> unitsToChooseFrom, Collection<Unit> unitsDefending, Match<Unit> match, int calcRunsPerUnit)
 
3199
    {
 
3200
        float bestTakeoverScore = Integer.MIN_VALUE;
 
3201
        Unit bestUnit = null;
 
3202
        List<Unit> fakeDefenseUnits = DUtils.CreateDefendUnitsTillTakeoverChanceIsLessThanX(unitsAlreadyAttacking, unitsDefending, data, testTer, .85F); //Increase the number of defenders to give a better unit help calculation
 
3203
 
 
3204
        List<Unit> units = new ArrayList<Unit>(unitsAlreadyAttacking);
 
3205
        AggregateResults oldResults = DUtils.GetBattleResults(units, fakeDefenseUnits, testTer, data, calcRunsPerUnit * 2, true);
 
3206
        float oldAttackerWinPercent = (float) oldResults.getAttackerWinPercent();
 
3207
        float oldAttackersLeft = (float) oldResults.getAverageAttackingUnitsLeft();
 
3208
        float oldDefendersLeft = (float) oldResults.getAverageDefendingUnitsLeft();
 
3209
        for (Unit testUnit : unitsToChooseFrom)
 
3210
        {
 
3211
            UnitType ut = testUnit.getUnitType();
 
3212
            UnitAttachment ua = UnitAttachment.get(ut);
 
3213
            if (ua.isSea() || ua.isAA() || ua.isFactory())
 
3214
                continue;
 
3215
            if(!match.match(testUnit))
 
3216
                continue;
 
3217
            //if(ua.isAir() && Math.random() <= .50F)
 
3218
            //    continue; //50% of the time, ignore air units (so we don't want to buy them)
 
3219
 
 
3220
            units.add(testUnit);
 
3221
            AggregateResults results = DUtils.GetBattleResults(units, fakeDefenseUnits, testTer, data, calcRunsPerUnit, true);
 
3222
            float attackerWinPercent = (float) results.getAttackerWinPercent();
 
3223
            float attackersLeft = (float) results.getAverageAttackingUnitsLeft();
 
3224
            float defendersLeft = (float) results.getAverageDefendingUnitsLeft();
 
3225
            float cost = GetTUVOfUnit(testUnit, GlobalCenter.GetPUResource());
 
3226
            float dif = attackerWinPercent - oldAttackerWinPercent;
 
3227
            float dif2 = (attackersLeft - oldAttackersLeft) + (oldDefendersLeft - defendersLeft);
 
3228
            if (dif != 0 && dif > 0)
 
3229
            {
 
3230
                if (dif / cost > bestTakeoverScore)
 
3231
                {
 
3232
                    bestUnit = testUnit;
 
3233
                    bestTakeoverScore = dif / cost;
 
3234
                }
 
3235
            }
 
3236
            else
 
3237
            {
 
3238
                if (dif2 / cost > bestTakeoverScore)
 
3239
                {
 
3240
                    bestUnit = testUnit;
 
3241
                    bestTakeoverScore = dif2 / cost;
 
3242
                }
 
3243
            }
 
3244
            units.remove(testUnit);
 
3245
        }
 
3246
        return bestUnit;
 
3247
    }
 
3248
    public static List<UnitGroup> CreateUnitGroupsForUnits(Collection<Unit> units, Territory ter, GameData data)
 
3249
    {
 
3250
        List<UnitGroup> result = new ArrayList<UnitGroup>();
 
3251
        for (Unit unit : units)
 
3252
        {
 
3253
            result.add(new UnitGroup(unit, ter, data));
 
3254
        }
 
3255
        return result;
 
3256
    }
 
3257
    /**
 
3258
     * Meant to duplicate the String.format method I used frequently in Microsoft Visual C#.
 
3259
     * (The String.format method in java doesn't seem to replace {0} with the first argument, {1} with the second, etc.)
 
3260
     */
 
3261
    public static String Format(String message, Object ... args)
 
3262
    {
 
3263
        int count = 0;
 
3264
        for(Object obj : args)
 
3265
        {
 
3266
            message = message.replace("{".concat(Integer.toString(count)).concat("}"), "" + obj);
 
3267
            count++;
 
3268
        }
 
3269
        return message;
 
3270
    }
 
3271
    /**
 
3272
     * Adds extra spaces to get logs to lineup correctly. (Adds two spaces to fine, one to finer, none to finest, etc.)
 
3273
     */
 
3274
    private static String addIndentationCompensation(String message, Level level)
 
3275
    {
 
3276
        StringBuilder builder = new StringBuilder();
 
3277
        int compensateLength = 6 - level.toString().length();
 
3278
        if(compensateLength == 0)
 
3279
            return message;
 
3280
        for(int i = 0; i < compensateLength;i++)
 
3281
        {
 
3282
            builder.append(" ");
 
3283
        }
 
3284
        builder.append(message);
 
3285
        return builder.toString();
 
3286
    }
 
3287
    /**
 
3288
     * Some notes on using the Dynamix logger:
 
3289
     *
 
3290
     * First, to make the logs easily readable even when there are hundreds of lines, I want every considerable step down in the call stack to mean more log message indentation.
 
3291
     * For example, the base logs in the Dynamix_AI class have no indentation before them, but the base logs in the DoCombatMove class will have two spaces inserted at the start, and the level below that, four spaces.
 
3292
     * In this way, when you're reading the log, you can skip over unimportant areas with speed because of the indentation.
 
3293
     *
 
3294
     * Second, I generally want the Fine logs to be messages that run less than 10 times each round, including almost all messages in the Dynamix_AI class,
 
3295
     * Finest for messages showing details within a method that, for example, returns a value.
 
3296
     * (So, for example, the NCM_Task method IsTaskWorthwhile() would primarily use finest, as it just returns a boolean, and the logs within it are just for details)
 
3297
     * Finer for just about everything else. (There's also the SERVER, INFO, etc. levels)
 
3298
     *
 
3299
     * Just keep these things in mind while adding new logging code.
 
3300
     */
 
3301
    public static void Log(Level level, String message, Object ... args)
 
3302
    {
 
3303
        //Used to pause AI's temporarily while the user is examining the AI logs
 
3304
        if(GlobalCenter.IsPaused && !SwingUtilities.isEventDispatchThread()) //Never 'sleep' on the UI thread
 
3305
            synchronized(GlobalCenter.IsPaused_Object){while(GlobalCenter.IsPaused)try{GlobalCenter.IsPaused_Object.wait();}catch(InterruptedException ex){}}        
 
3306
 
 
3307
        if(args.length > 0)
 
3308
            message = Format(message, args); //Convert {0}, {1}, etc to the objects supplied for them
 
3309
 
 
3310
        //We always log to the AI logger, though it only shows up if the developer has the logger enabled in logging.properties
 
3311
        Dynamix_AI.GetStaticLogger().log(level, addIndentationCompensation(message, level));
 
3312
        
 
3313
        if (!DSettings.LoadSettings().EnableAILogging)
 
3314
            return; //Skip displaying to settings window if settings window option is turned off
 
3315
        Level logDepth = DSettings.LoadSettings().AILoggingDepth;
 
3316
        if (logDepth.equals(Level.FINE) && (level.equals(Level.FINER) || level.equals(Level.FINEST)))
 
3317
            return; //If the settings window log depth is a higher level than this messages, skip
 
3318
        if (logDepth.equals(Level.FINER) && level.equals(Level.FINEST))
 
3319
            return;
 
3320
 
 
3321
        UI.NotifyAILogMessage(level, message);
 
3322
    }
 
3323
    public static UnitGroup CreateUnitGroupForUnit(Unit unit, Territory ter, GameData data)
 
3324
    {
 
3325
        return CreateUnitGroupForUnits(Collections.singleton(unit), ter, data);
 
3326
    }
 
3327
    /**
 
3328
     * Only use this if you know that all the units have the same movement amount left, otherwise the units with more movement left will not go as far as they could
 
3329
     */
 
3330
    public static UnitGroup CreateUnitGroupForUnits(Collection<Unit> units, Territory ter, GameData data)
 
3331
    {
 
3332
        return new UnitGroup(units, ter, data);
 
3333
    }
 
3334
    /**
 
3335
     * This method is very handy when you want to move a territory's units, you want the units to move to a target as far as possible, and in the largest groups possible.
 
3336
     * (If this method were not used, any units with more movement left in the list would not go as far as they could)
 
3337
     */
 
3338
    public static List<UnitGroup> CreateSpeedSplitUnitGroupsForUnits(Collection<Unit> units, Territory ter, GameData data)
 
3339
    {
 
3340
        List<UnitGroup> result = new ArrayList<UnitGroup>();
 
3341
        HashMap<Integer, List<Unit>> splitUnits = DUtils.SeperateUnitsInListIntoSeperateMovementLists(new ArrayList<Unit>(units));
 
3342
        for (Integer speed : splitUnits.keySet())
 
3343
        {
 
3344
            List<Unit> unitsForSpeed = splitUnits.get(speed);
 
3345
            result.add(new UnitGroup(unitsForSpeed, ter, data));
 
3346
        }
 
3347
        return result;
 
3348
    }
 
3349
    public static List<Unit> GetEndingCapitalUnits(GameData data, PlayerID player)
 
3350
    {
 
3351
        Territory ourCapital = TerritoryAttachment.getCapital(player, data);
 
3352
 
 
3353
        return GetTerUnitsAtEndOfTurn(data, player, ourCapital);
 
3354
    }
 
3355
}