package games.strategy.triplea.ai.proAI.simulate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import games.strategy.engine.data.Change;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.RelationshipTracker;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.delegate.IDelegateBridge;
import games.strategy.triplea.ai.proAI.ProData;
import games.strategy.triplea.ai.proAI.data.ProBattleResult;
import games.strategy.triplea.ai.proAI.data.ProTerritory;
import games.strategy.triplea.ai.proAI.logging.ProLogger;
import games.strategy.triplea.ai.proAI.util.ProMatches;
import games.strategy.triplea.ai.proAI.util.ProOddsCalculator;
import games.strategy.triplea.attachments.TerritoryAttachment;
import games.strategy.triplea.delegate.BattleDelegate;
import games.strategy.triplea.delegate.BattleTracker;
import games.strategy.triplea.delegate.DelegateFinder;
import games.strategy.triplea.delegate.IBattle;
import games.strategy.triplea.delegate.IBattle.BattleType;
import games.strategy.triplea.delegate.Matches;
import games.strategy.triplea.delegate.OriginalOwnerTracker;
import games.strategy.triplea.delegate.TransportTracker;
import games.strategy.util.Match;
/**
* Pro AI simulate turn utilities.
*/
public class ProSimulateTurnUtils {
public static void simulateBattles(final GameData data, final PlayerID player, final IDelegateBridge delegateBridge,
final ProOddsCalculator calc) {
ProLogger.info("Starting battle simulation phase");
final BattleDelegate battleDelegate = DelegateFinder.battleDelegate(data);
final Map<BattleType, Collection<Territory>> battleTerritories = battleDelegate.getBattles().getBattles();
for (final Entry<BattleType, Collection<Territory>> entry : battleTerritories.entrySet()) {
for (final Territory t : entry.getValue()) {
final IBattle battle =
battleDelegate.getBattleTracker().getPendingBattle(t, entry.getKey().isBombingRun(), entry.getKey());
final List<Unit> attackers = (List<Unit>) battle.getAttackingUnits();
attackers.retainAll(t.getUnits().getUnits());
final List<Unit> defenders = (List<Unit>) battle.getDefendingUnits();
defenders.retainAll(t.getUnits().getUnits());
final Set<Unit> bombardingUnits = new HashSet<>(battle.getBombardingUnits());
ProLogger.debug("---" + t);
ProLogger.debug("attackers=" + attackers);
ProLogger.debug("defenders=" + defenders);
ProLogger.debug("bombardingUnits=" + bombardingUnits);
final ProBattleResult result = calc.callBattleCalculator(player, t, attackers, defenders, bombardingUnits);
final List<Unit> remainingUnits = result.getAverageAttackersRemaining();
ProLogger.debug("remainingUnits=" + remainingUnits);
// Make updates to data
final List<Unit> attackersToRemove = new ArrayList<>(attackers);
attackersToRemove.removeAll(remainingUnits);
final List<Unit> defendersToRemove = Match.getMatches(defenders, Matches.UnitIsInfrastructure.invert());
final List<Unit> infrastructureToChangeOwner = Match.getMatches(defenders, Matches.UnitIsInfrastructure);
ProLogger.debug("attackersToRemove=" + attackersToRemove);
ProLogger.debug("defendersToRemove=" + defendersToRemove);
ProLogger.debug("infrastructureToChangeOwner=" + infrastructureToChangeOwner);
final Change attackerskilledChange = ChangeFactory.removeUnits(t, attackersToRemove);
delegateBridge.addChange(attackerskilledChange);
final Change defenderskilledChange = ChangeFactory.removeUnits(t, defendersToRemove);
delegateBridge.addChange(defenderskilledChange);
BattleTracker.captureOrDestroyUnits(t, player, player, delegateBridge, null);
if (!checkIfCapturedTerritoryIsAlliedCapital(t, data, player, delegateBridge)) {
delegateBridge.addChange(ChangeFactory.changeOwner(t, player));
}
battleDelegate.getBattleTracker().getConquered().add(t);
battleDelegate.getBattleTracker().removeBattle(battle);
final Territory updatedTerritory = data.getMap().getTerritory(t.getName());
ProLogger.debug(
"after changes owner=" + updatedTerritory.getOwner() + ", units=" + updatedTerritory.getUnits().getUnits());
}
}
}
public static Map<Territory, ProTerritory> transferMoveMap(final Map<Territory, ProTerritory> moveMap,
final GameData toData, final PlayerID player) {
ProLogger.info("Transferring move map");
final Map<Unit, Territory> unitTerritoryMap = ProData.unitTerritoryMap;
final Map<Territory, ProTerritory> result = new HashMap<>();
final List<Unit> usedUnits = new ArrayList<>();
for (final Territory fromTerritory : moveMap.keySet()) {
final Territory toTerritory = toData.getMap().getTerritory(fromTerritory.getName());
final ProTerritory patd = new ProTerritory(toTerritory);
result.put(toTerritory, patd);
final Map<Unit, List<Unit>> amphibAttackMap = moveMap.get(fromTerritory).getAmphibAttackMap();
final Map<Unit, Boolean> isTransportingMap = moveMap.get(fromTerritory).getIsTransportingMap();
final Map<Unit, Territory> transportTerritoryMap = moveMap.get(fromTerritory).getTransportTerritoryMap();
final Map<Unit, Territory> bombardMap = moveMap.get(fromTerritory).getBombardTerritoryMap();
ProLogger.debug("Transferring " + fromTerritory + " to " + toTerritory);
final List<Unit> amphibUnits = new ArrayList<>();
for (final Unit transport : amphibAttackMap.keySet()) {
Unit toTransport = null;
final List<Unit> toUnits = new ArrayList<>();
if (isTransportingMap.get(transport)) {
toTransport = transferLoadedTransport(transport, amphibAttackMap.get(transport), unitTerritoryMap, usedUnits,
toData, player);
toUnits.addAll(TransportTracker.transporting(toTransport));
} else {
toTransport = transferUnit(transport, unitTerritoryMap, usedUnits, toData, player);
for (final Unit u : amphibAttackMap.get(transport)) {
final Unit toUnit = transferUnit(u, unitTerritoryMap, usedUnits, toData, player);
toUnits.add(toUnit);
}
}
patd.addUnits(toUnits);
patd.putAmphibAttackMap(toTransport, toUnits);
amphibUnits.addAll(amphibAttackMap.get(transport));
if (transportTerritoryMap.get(transport) != null) {
patd.getTransportTerritoryMap().put(toTransport,
toData.getMap().getTerritory(transportTerritoryMap.get(transport).getName()));
}
ProLogger.trace("---Transferring transport=" + transport + " with units=" + amphibAttackMap.get(transport)
+ " unloadTerritory=" + transportTerritoryMap.get(transport) + " to transport=" + toTransport
+ " with units=" + toUnits + " unloadTerritory=" + patd.getTransportTerritoryMap().get(toTransport));
}
for (final Unit u : moveMap.get(fromTerritory).getUnits()) {
if (!amphibUnits.contains(u)) {
final Unit toUnit = transferUnit(u, unitTerritoryMap, usedUnits, toData, player);
patd.addUnit(toUnit);
ProLogger.trace("---Transferring unit " + u + " to " + toUnit);
}
}
for (final Unit u : moveMap.get(fromTerritory).getBombers()) {
final Unit toUnit = transferUnit(u, unitTerritoryMap, usedUnits, toData, player);
patd.getBombers().add(toUnit);
ProLogger.trace("---Transferring bomber " + u + " to " + toUnit);
}
for (final Unit u : bombardMap.keySet()) {
final Unit toUnit = transferUnit(u, unitTerritoryMap, usedUnits, toData, player);
patd.getBombardTerritoryMap().put(toUnit, toData.getMap().getTerritory(bombardMap.get(u).getName()));
ProLogger.trace("---Transferring bombard=" + u + ", bombardFromTerritory=" + bombardMap.get(u) + " to bomard="
+ toUnit + ", bombardFromTerritory=" + patd.getBombardTerritoryMap().get(toUnit));
}
}
return result;
}
private static boolean checkIfCapturedTerritoryIsAlliedCapital(final Territory t, final GameData data,
final PlayerID player, final IDelegateBridge delegateBridge) {
final PlayerID terrOrigOwner = OriginalOwnerTracker.getOriginalOwner(t);
final RelationshipTracker relationshipTracker = data.getRelationshipTracker();
final TerritoryAttachment ta = TerritoryAttachment.get(t);
if (ta != null && ta.getCapital() != null && terrOrigOwner != null
&& TerritoryAttachment.getAllCapitals(terrOrigOwner, data).contains(t)
&& relationshipTracker.isAllied(terrOrigOwner, player)) {
// Give capital and any allied territories back to original owner
final Collection<Territory> originallyOwned = OriginalOwnerTracker.getOriginallyOwned(data, terrOrigOwner);
final List<Territory> friendlyTerritories =
Match.getMatches(originallyOwned, Matches.isTerritoryAllied(terrOrigOwner, data));
friendlyTerritories.add(t);
for (final Territory item : friendlyTerritories) {
if (item.getOwner() == terrOrigOwner) {
continue;
}
final Change takeOverFriendlyTerritories = ChangeFactory.changeOwner(item, terrOrigOwner);
delegateBridge.addChange(takeOverFriendlyTerritories);
final Collection<Unit> units = Match.getMatches(item.getUnits().getUnits(), Matches.UnitIsInfrastructure);
if (!units.isEmpty()) {
final Change takeOverNonComUnits = ChangeFactory.changeOwner(units, terrOrigOwner, t);
delegateBridge.addChange(takeOverNonComUnits);
}
}
return true;
}
return false;
}
private static Unit transferUnit(final Unit u, final Map<Unit, Territory> unitTerritoryMap,
final List<Unit> usedUnits, final GameData toData, final PlayerID player) {
final Territory unitTerritory = unitTerritoryMap.get(u);
final List<Unit> toUnits = toData.getMap().getTerritory(unitTerritory.getName()).getUnits()
.getMatches(ProMatches.unitIsOwnedAndMatchesTypeAndNotTransporting(player, u.getType()));
for (final Unit toUnit : toUnits) {
if (!usedUnits.contains(toUnit)) {
usedUnits.add(toUnit);
return toUnit;
}
}
return null;
}
private static Unit transferLoadedTransport(final Unit transport, final List<Unit> transportingUnits,
final Map<Unit, Territory> unitTerritoryMap, final List<Unit> usedUnits, final GameData toData,
final PlayerID player) {
final Territory unitTerritory = unitTerritoryMap.get(transport);
final List<Unit> toTransports = toData.getMap().getTerritory(unitTerritory.getName()).getUnits()
.getMatches(ProMatches.unitIsOwnedAndMatchesTypeAndIsTransporting(player, transport.getType()));
for (final Unit toTransport : toTransports) {
if (!usedUnits.contains(toTransport)) {
final List<Unit> toTransportingUnits = (List<Unit>) TransportTracker.transporting(toTransport);
if (transportingUnits.size() == toTransportingUnits.size()) {
boolean canTransfer = true;
for (int i = 0; i < transportingUnits.size(); i++) {
if (!transportingUnits.get(i).getType().equals(toTransportingUnits.get(i).getType())) {
canTransfer = false;
break;
}
}
if (canTransfer) {
usedUnits.add(toTransport);
usedUnits.addAll(toTransportingUnits);
return toTransport;
}
}
}
}
return null;
}
}