Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve logic for choosing default players in battle calc. #11924

Merged
merged 3 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import lombok.experimental.UtilityClass;
import org.triplea.java.collections.CollectionUtils;
import org.triplea.java.collections.IntegerMap;
Expand Down Expand Up @@ -241,4 +242,21 @@ private static CompositeChange translateHitPointsAndDamageToOtherUnit(
}
return unitChange;
}

public static @Nullable GamePlayer findPlayerWithMostUnits(final Iterable<Unit> units) {
final IntegerMap<GamePlayer> playerUnitCount = new IntegerMap<>();
for (final Unit unit : units) {
playerUnitCount.add(unit.getOwner(), 1);
}
int max = -1;
GamePlayer player = null;
for (final GamePlayer current : playerUnitCount.keySet()) {
final int count = playerUnitCount.getInt(current);
if (count > max) {
max = count;
player = current;
}
}
return player;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -315,23 +315,6 @@ static GamePlayer findDefender(
return defender;
}

static GamePlayer findPlayerWithMostUnits(final Collection<Unit> units) {
final IntegerMap<GamePlayer> playerUnitCount = new IntegerMap<>();
for (final Unit unit : units) {
playerUnitCount.add(unit.getOwner(), 1);
}
int max = -1;
GamePlayer player = null;
for (final GamePlayer current : playerUnitCount.keySet()) {
final int count = playerUnitCount.getInt(current);
if (count > max) {
max = count;
player = current;
}
}
return player;
}

void markDamaged(final Collection<Unit> damaged, final IDelegateBridge bridge) {
BattleDelegate.markDamaged(damaged, bridge, battleSite);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import games.strategy.engine.player.Player;
import games.strategy.engine.random.IRandomStats.DiceType;
import games.strategy.triplea.Properties;
import games.strategy.triplea.UnitUtils;
import games.strategy.triplea.attachments.PlayerAttachment;
import games.strategy.triplea.attachments.TerritoryAttachment;
import games.strategy.triplea.attachments.UnitAttachment;
Expand Down Expand Up @@ -625,7 +626,7 @@ private static void setupTerritoriesAbandonedToTheEnemy(
for (final Territory territory : battleTerritories) {
final List<Unit> abandonedToUnits =
territory.getUnitCollection().getMatches(Matches.enemyUnit(player));
final GamePlayer abandonedToPlayer = AbstractBattle.findPlayerWithMostUnits(abandonedToUnits);
final GamePlayer abandonedToPlayer = UnitUtils.findPlayerWithMostUnits(abandonedToUnits);

// now make sure to add any units that must move with these units, so that they get included
// as dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import games.strategy.engine.history.change.units.TransformDamagedUnitsHistoryChange;
import games.strategy.engine.player.Player;
import games.strategy.triplea.Properties;
import games.strategy.triplea.UnitUtils;
import games.strategy.triplea.delegate.ExecutionStack;
import games.strategy.triplea.delegate.IExecutable;
import games.strategy.triplea.delegate.Matches;
Expand Down Expand Up @@ -1368,7 +1369,7 @@ private void defenderWins(final IDelegateBridge bridge) {
battleSite.getUnitCollection().getMatches(Matches.unitIsNotInfrastructure());
if (!allyOfAttackerUnits.isEmpty()) {
final GamePlayer abandonedToPlayer =
AbstractBattle.findPlayerWithMostUnits(allyOfAttackerUnits);
UnitUtils.findPlayerWithMostUnits(allyOfAttackerUnits);
bridge
.getHistoryWriter()
.addChildToEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import games.strategy.engine.data.RelationshipTracker;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.triplea.UnitUtils;
import games.strategy.triplea.delegate.Matches;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand All @@ -33,10 +35,10 @@ public static class AttackerAndDefender {
/** NONE = No attacker, no defender and no units. */
public static final AttackerAndDefender NONE = AttackerAndDefender.builder().build();

@Nullable private final GamePlayer attacker;
@Nullable private final GamePlayer defender;
@Builder.Default private final List<Unit> attackingUnits = List.of();
@Builder.Default private final List<Unit> defendingUnits = List.of();
@Nullable GamePlayer attacker;
@Nullable GamePlayer defender;
@Builder.Default List<Unit> attackingUnits = List.of();
@Builder.Default List<Unit> defendingUnits = List.of();

public Optional<GamePlayer> getAttacker() {
return Optional.ofNullable(attacker);
Expand Down Expand Up @@ -82,10 +84,11 @@ public AttackerAndDefender getAttackerAndDefender() {
if (!territoryOwner.isNull()) {
playersWithUnits.add(territoryOwner);
}

final GamePlayer attacker = currentPlayer;
// Attacker fights alone; the defender can also use all the allied units.
final GamePlayer defender =
getOpponentWithPriorityList(attacker, playersWithUnits).orElse(null);
getOpponentWithPriorityList(territory, attacker, playersWithUnits).orElse(null);
final List<Unit> attackingUnits =
territory.getUnitCollection().getMatches(Matches.unitIsOwnedBy(attacker));
final List<Unit> defendingUnits =
Expand All @@ -94,7 +97,7 @@ public AttackerAndDefender getAttackerAndDefender() {
: territory.getUnitCollection().getMatches(Matches.alliedUnit(defender));

return AttackerAndDefender.builder()
.attacker(currentPlayer)
.attacker(attacker)
.defender(defender)
.attackingUnits(attackingUnits)
.defendingUnits(defendingUnits)
Expand Down Expand Up @@ -141,7 +144,8 @@ private AttackerAndDefender getAttackerAndDefenderWithPriorityList(
return AttackerAndDefender.NONE;
}
// Defender
final GamePlayer defender = getOpponentWithPriorityList(attacker, priorityPlayers).orElse(null);
final GamePlayer defender =
getOpponentWithPriorityList(territory, attacker, priorityPlayers).orElse(null);
return AttackerAndDefender.builder().attacker(attacker).defender(defender).build();
}

Expand All @@ -150,7 +154,8 @@ private AttackerAndDefender getAttackerAndDefenderWithPriorityList(
* priority. The order in {@code priorityPlayers} determines the priority for those players
* included in that list. Players not in the list are at the bottom without any order.
*
* <p>The opponent is chosen with the following priorities
* <p>Some additional prioritisation is given based on the territory owner and players with units.
* Otherwise, the opponent is chosen with the following priorities
*
* <ol>
* <li>the first player in {@code priorityPlayers} who is an enemy of {@code p}
Expand All @@ -165,14 +170,34 @@ private AttackerAndDefender getAttackerAndDefenderWithPriorityList(
* @return an opponent. An empty optional is returned if the game has no players
*/
private Optional<GamePlayer> getOpponentWithPriorityList(
final GamePlayer player, final List<GamePlayer> priorityPlayers) {
Territory territory, final GamePlayer player, final List<GamePlayer> priorityPlayers) {
GamePlayer bestDefender = null;
// Handle some special cases that the priority ordering logic doesn't handle. See tests.
if (territory != null) {
if (territory.isWater()) {
bestDefender = getEnemyWithMostUnits(territory);
if (bestDefender == null) {
bestDefender = UnitUtils.findPlayerWithMostUnits(territory.getUnits());
}
} else {
bestDefender = territory.getOwner();
// If we're not at war with the owner and there are enemies, fight them.
if (!bestDefender.isAtWar(currentPlayer)) {
GamePlayer enemyWithMostUnits = getEnemyWithMostUnits(territory);
if (enemyWithMostUnits != null) {
bestDefender = enemyWithMostUnits;
}
}
}
}
final Stream<GamePlayer> enemiesPriority =
priorityPlayers.stream().filter(Matches.isAtWar(player));
final Stream<GamePlayer> neutralsPriority =
priorityPlayers.stream()
.filter(Matches.isAtWar(player).negate())
.filter(Matches.isAllied(player).negate());
return Stream.of(
Optional.ofNullable(bestDefender).stream(),
enemiesPriority,
playersAtWarWith(player),
neutralsPriority,
Expand All @@ -182,6 +207,13 @@ private Optional<GamePlayer> getOpponentWithPriorityList(
.findFirst();
}

private @Nullable GamePlayer getEnemyWithMostUnits(Territory territory) {
return UnitUtils.findPlayerWithMostUnits(
territory.getUnits().stream()
.filter(Matches.unitIsEnemyOf(currentPlayer))
.collect(Collectors.toList()));
}

/**
* Returns a stream of all players which are at war with player {@code p}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static GamePlayer germany(final GameState data) {
*
* @return A italian PlayerId.
*/
static GamePlayer italians(final GameState data) {
public static GamePlayer italians(final GameState data) {
return data.getPlayerList().getPlayerId(Constants.PLAYER_NAME_ITALIANS);
}

Expand Down Expand Up @@ -105,6 +105,10 @@ public static GamePlayer britain(final GameState data) {
return data.getPlayerList().getPlayerId("Britain");
}

public static GamePlayer french(final GameState data) {
return data.getPlayerList().getPlayerId(Constants.PLAYER_NAME_FRENCH);
}

/**
* Get the japanese PlayerId for the given GameData object.
*
Expand Down
Loading