Skip to content

Commit 870de56

Browse files
authored
Fix issue 12822 GameMap#getNeighbors IllegalArgumentException (#12831)
* Fix issue 12822 GameMap#getNeighbors IllegalArgumentException Root cause: myCapitol was null when calling method getNeighboringLandTerritories ProTechAi.java - new method getMyStrength making sure method getNeighboringLandTerritories is not called with myCapitol == null - fix use of randomness now based on common ThreadLocalRandom * Fix randomness in LanchesterDebugAction LanchesterDebugAction.java - fix use of randomness now based on common ThreadLocalRandom variable for each loop run (also passed to method getRandomUnitType)
1 parent 1e72097 commit 870de56

File tree

2 files changed

+47
-21
lines changed

2 files changed

+47
-21
lines changed

game-app/ai/src/main/java/org/triplea/ai/flowfield/odds/LanchesterDebugAction.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Optional;
27-
import java.util.Random;
27+
import java.util.concurrent.ThreadLocalRandom;
2828
import java.util.function.Consumer;
2929
import java.util.function.Predicate;
3030
import java.util.stream.Collectors;
@@ -105,15 +105,16 @@ public void accept(final AiPlayerDebugAction aiPlayerDebugAction) {
105105
unitAttachment ->
106106
unitAttachment.getDefense(defender) > 0
107107
&& Matches.unitTypeIsLand().test((UnitType) unitAttachment.getAttachedTo()));
108+
final ThreadLocalRandom localRandom = ThreadLocalRandom.current();
108109
for (int i = 0; i < 4; i++) {
109-
getRandomUnitType(offenseUnitTypes)
110+
getRandomUnitType(localRandom, offenseUnitTypes)
110111
.ifPresent(
111112
unitType ->
112-
attackingUnits.addAll(unitType.create(new Random().nextInt(10), offender)));
113-
getRandomUnitType(defenseUnitTypes)
113+
attackingUnits.addAll(unitType.create(localRandom.nextInt(10), offender)));
114+
getRandomUnitType(localRandom, defenseUnitTypes)
114115
.ifPresent(
115116
unitType ->
116-
defendingUnits.addAll(unitType.create(new Random().nextInt(10), defender)));
117+
defendingUnits.addAll(unitType.create(localRandom.nextInt(10), defender)));
117118
}
118119

119120
System.out.println("Attack Units: " + MyFormatter.unitsToText(attackingUnits));
@@ -192,7 +193,8 @@ private Collection<UnitType> getUnitTypes(
192193
.collect(Collectors.toSet());
193194
}
194195

195-
private Optional<UnitType> getRandomUnitType(final Collection<UnitType> unitTypes) {
196-
return unitTypes.stream().skip((int) (unitTypes.size() * Math.random())).findFirst();
196+
private Optional<UnitType> getRandomUnitType(
197+
final ThreadLocalRandom localRandom, final Collection<UnitType> unitTypes) {
198+
return unitTypes.stream().skip((localRandom.nextInt(unitTypes.size()))).findFirst();
197199
}
198200
}

game-app/game-core/src/main/java/games/strategy/triplea/ai/pro/ProTechAi.java

+38-14
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Objects;
2929
import java.util.Queue;
3030
import java.util.Set;
31+
import java.util.concurrent.ThreadLocalRandom;
3132
import java.util.function.Predicate;
3233
import javax.annotation.Nullable;
3334
import org.triplea.java.PredicateBuilder;
@@ -45,37 +46,60 @@ static void tech(final ITechDelegate techDelegate, final GameData data, final Ga
4546
final Territory myCapitol =
4647
TerritoryAttachment.getFirstOwnedCapitalOrFirstUnownedCapital(player, data.getMap());
4748
final float enemyStrength = getStrengthOfPotentialAttackers(myCapitol, data, player);
48-
float myStrength =
49-
(myCapitol == null) ? 0.0F : strength(myCapitol.getUnits(), false, false, false);
50-
final List<Territory> areaStrength = getNeighboringLandTerritories(data, player, myCapitol);
51-
for (final Territory areaTerr : areaStrength) {
52-
myStrength += strength(areaTerr.getUnits(), false, false, false) * 0.75F;
53-
}
49+
float myStrength = getMyStrength(data, player, myCapitol);
5450
final boolean capDanger = myStrength < (enemyStrength * 1.25F + 3.0F);
5551
final Resource pus = data.getResourceList().getResource(Constants.PUS);
5652
final int pusRemaining = player.getResources().getQuantity(pus);
5753
final Resource techTokens = data.getResourceList().getResource(Constants.TECH_TOKENS);
5854
final int techTokensQuantity = player.getResources().getQuantity(techTokens);
55+
final ThreadLocalRandom localRandom = ThreadLocalRandom.current();
5956
int tokensToBuy = 0;
60-
if (!capDanger && techTokensQuantity < 3 && pusRemaining > Math.random() * 160) {
57+
if (!capDanger && techTokensQuantity < 3 && pusRemaining > localRandom.nextInt(160)) {
6158
tokensToBuy = 1;
6259
}
6360
if (techTokensQuantity > 0 || tokensToBuy > 0) {
6461
final List<TechnologyFrontier> cats = TechAdvance.getPlayerTechCategories(player);
6562
// retaining 65% chance of choosing land advances using basic ww2v3 model.
6663
if (data.getTechnologyFrontier().isEmpty()) {
67-
if (Math.random() > 0.35) {
64+
if (localRandom.nextFloat() > 0.35) {
6865
techDelegate.rollTech(techTokensQuantity + tokensToBuy, cats.get(1), tokensToBuy, null);
6966
} else {
7067
techDelegate.rollTech(techTokensQuantity + tokensToBuy, cats.get(0), tokensToBuy, null);
7168
}
7269
} else {
73-
final int rand = (int) (Math.random() * cats.size());
74-
techDelegate.rollTech(techTokensQuantity + tokensToBuy, cats.get(rand), tokensToBuy, null);
70+
techDelegate.rollTech(
71+
techTokensQuantity + tokensToBuy,
72+
cats.get(localRandom.nextInt(cats.size())),
73+
tokensToBuy,
74+
null);
7575
}
7676
}
7777
}
7878

79+
/**
80+
* Get strength value for territory {@code myCapitol} calculated from units in the territory and
81+
* neighboring land territories (latter adjusted with a factor)
82+
*
83+
* @param data {@code GameData}
84+
* @param player current {@code GamePlayer}
85+
* @param myCapitol current {@code Territory}
86+
* @return strength value for territory {@code myCapitol}
87+
*/
88+
private static float getMyStrength(
89+
final GameData data, final GamePlayer player, final Territory myCapitol) {
90+
if (myCapitol == null) {
91+
return 0.0F;
92+
}
93+
final float capitolStrength = strength(myCapitol.getUnits(), false, false, false);
94+
final List<Territory> neighboringLandTerritories =
95+
getNeighboringLandTerritories(data, player, myCapitol);
96+
final List<Unit> unitsOfNeighboringLandTerritories = new ArrayList<>();
97+
neighboringLandTerritories.forEach(t -> unitsOfNeighboringLandTerritories.addAll(t.getUnits()));
98+
final float neighborStrength =
99+
strength(unitsOfNeighboringLandTerritories, false, false, false) * 0.75F;
100+
return capitolStrength + neighborStrength;
101+
}
102+
79103
/**
80104
* Returns the strength of all attackers to a territory. Differentiates between sea and land
81105
* attack Determines all transports within range of territory Determines all air units within
@@ -282,7 +306,7 @@ private static float getStrengthOfPotentialAttackers(
282306
}
283307
for (final GamePlayer enemyPlayerCandidate : enemyPlayers) {
284308
if (!Objects.equals(enemyPlayer, enemyPlayerCandidate)) {
285-
// give 40% of other players...this is will affect a lot of decisions by AI
309+
// give 40% of other players...this will affect a lot of decisions by AI
286310
maxStrength += enemyPlayerAttackMap.get(enemyPlayerCandidate) * 0.40F;
287311
}
288312
}
@@ -540,8 +564,8 @@ private static Route getMaxSeaRoute(
540564
final Collection<Unit> units,
541565
final GamePlayer player,
542566
final int maxDistance) {
543-
// note this does not care if subs are submerged or not
544-
// should it? does submerging affect movement of enemies?
567+
// note this does not care if subs are submerged or not, should it?
568+
// does submerging affect movement of enemies?
545569
if (start == null || destination == null || !start.isWater() || !destination.isWater()) {
546570
return null;
547571
}
@@ -604,7 +628,7 @@ private static List<Territory> getExactNeighbors(
604628
* Finds list of territories at exactly distance from the start.
605629
*
606630
* @param endCondition condition that all end points must satisfy
607-
* @param routeCondition condition that all traversed internal territories must satisfied
631+
* @param routeCondition condition that all traversed internal territories must satisfy
608632
*/
609633
private static List<Territory> findFrontier(
610634
final Territory start,

0 commit comments

Comments
 (0)