package games.strategy.triplea.delegate;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import games.strategy.engine.data.CompositeChange;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.UnitType;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.message.IRemote;
import games.strategy.engine.random.IRandomStats.DiceType;
import games.strategy.triplea.MapSupport;
import games.strategy.triplea.formatter.MyFormatter;
import games.strategy.util.CompositeMatchAnd;
import games.strategy.util.IntegerMap;
import games.strategy.util.Match;
import games.strategy.util.ThreadUtil;
import games.strategy.util.Tuple;
/**
* This delegate sets up the game according to Risk rules, with a few allowed customizations.
* Either divide all neutral territories between players randomly, or let them pick one by one.
* After that, any remaining units get placed one by one.
* (Note that m_player may not be used here, because this delegate is not run by any player [it is null])
*/
@MapSupport
public class RandomStartDelegate extends BaseTripleADelegate {
private static final int UNITS_PER_PICK = 1;
protected PlayerID m_currentPickingPlayer = null;
@Override
public void start() {
super.start();
setupBoard();
}
/**
* Called before the delegate will stop running.
*/
@Override
public void end() {
super.end();
m_currentPickingPlayer = null;
}
@Override
public boolean delegateCurrentlyRequiresUserInput() {
return !(Match.noneMatch(getData().getMap().getTerritories(), getTerritoryPickableMatch())
&& Match.noneMatch(getData().getPlayerList().getPlayers(), getPlayerCanPickMatch()));
}
@Override
public Serializable saveState() {
final RandomStartExtendedDelegateState state = new RandomStartExtendedDelegateState();
state.superState = super.saveState();
state.m_currentPickingPlayer = this.m_currentPickingPlayer;
return state;
}
@Override
public void loadState(final Serializable state) {
final RandomStartExtendedDelegateState s = (RandomStartExtendedDelegateState) state;
super.loadState(s.superState);
this.m_currentPickingPlayer = s.m_currentPickingPlayer;
}
protected void setupBoard() {
final GameData data = getData();
final boolean randomTerritories = games.strategy.triplea.Properties.getTerritoriesAreAssignedRandomly(data);
final Match<Territory> pickableTerritoryMatch = getTerritoryPickableMatch();
final Match<PlayerID> playerCanPickMatch = getPlayerCanPickMatch();
final List<Territory> allPickableTerritories =
Match.getMatches(data.getMap().getTerritories(), pickableTerritoryMatch);
final List<PlayerID> playersCanPick = new ArrayList<>();
playersCanPick.addAll(Match.getMatches(data.getPlayerList().getPlayers(), playerCanPickMatch));
// we need a main event
if (!playersCanPick.isEmpty()) {
m_bridge.getHistoryWriter().startEvent("Assigning Territories");
}
// for random:
final int[] hitRandom = (!randomTerritories ? new int[0]
: m_bridge.getRandom(allPickableTerritories.size(), allPickableTerritories.size(), null, DiceType.ENGINE,
"Picking random territories"));
int i = 0;
int pos = 0;
// divvy up territories
while (!allPickableTerritories.isEmpty() && !playersCanPick.isEmpty()) {
if (m_currentPickingPlayer == null || !playersCanPick.contains(m_currentPickingPlayer)) {
m_currentPickingPlayer = playersCanPick.get(0);
}
if (!ThreadUtil.sleep(250)) {
return;
}
Territory picked;
if (randomTerritories) {
pos += hitRandom[i];
i++;
final IntegerMap<UnitType> costs = BattleCalculator.getCostsForTUV(m_currentPickingPlayer, data);
final List<Unit> units = new ArrayList<>(m_currentPickingPlayer.getUnits().getUnits());
Collections.sort(units, new UnitCostComparator(costs));
final Set<Unit> unitsToPlace = new HashSet<>();
unitsToPlace.add(units.get(0));
picked = allPickableTerritories.get(pos % allPickableTerritories.size());
final CompositeChange change = new CompositeChange();
change.add(ChangeFactory.changeOwner(picked, m_currentPickingPlayer));
final Collection<Unit> factoryAndInfrastructure = Match.getMatches(unitsToPlace, Matches.UnitIsInfrastructure);
if (!factoryAndInfrastructure.isEmpty()) {
change.add(OriginalOwnerTracker.addOriginalOwnerChange(factoryAndInfrastructure, m_currentPickingPlayer));
}
change.add(ChangeFactory.removeUnits(m_currentPickingPlayer, unitsToPlace));
change.add(ChangeFactory.addUnits(picked, unitsToPlace));
m_bridge.getHistoryWriter().addChildToEvent(m_currentPickingPlayer.getName() + " receives territory "
+ picked.getName() + " with units " + MyFormatter.unitsToTextNoOwner(unitsToPlace), picked);
m_bridge.addChange(change);
} else {
Tuple<Territory, Set<Unit>> pick;
Set<Unit> unitsToPlace;
while (true) {
pick = getRemotePlayer(m_currentPickingPlayer).pickTerritoryAndUnits(
new ArrayList<>(allPickableTerritories),
new ArrayList<>(m_currentPickingPlayer.getUnits().getUnits()), UNITS_PER_PICK);
picked = pick.getFirst();
unitsToPlace = pick.getSecond();
if (!allPickableTerritories.contains(picked)
|| !m_currentPickingPlayer.getUnits().getUnits().containsAll(unitsToPlace)
|| unitsToPlace.size() > UNITS_PER_PICK || (unitsToPlace.size() < UNITS_PER_PICK
&& unitsToPlace.size() < m_currentPickingPlayer.getUnits().getUnits().size())) {
getRemotePlayer(m_currentPickingPlayer).reportMessage("Chosen territory or units invalid!",
"Chosen territory or units invalid!");
} else {
break;
}
}
final CompositeChange change = new CompositeChange();
change.add(ChangeFactory.changeOwner(picked, m_currentPickingPlayer));
final Collection<Unit> factoryAndInfrastructure = Match.getMatches(unitsToPlace, Matches.UnitIsInfrastructure);
if (!factoryAndInfrastructure.isEmpty()) {
change.add(OriginalOwnerTracker.addOriginalOwnerChange(factoryAndInfrastructure, m_currentPickingPlayer));
}
change.add(ChangeFactory.removeUnits(m_currentPickingPlayer, unitsToPlace));
change.add(ChangeFactory.addUnits(picked, unitsToPlace));
m_bridge.getHistoryWriter().addChildToEvent(m_currentPickingPlayer.getName() + " picks territory "
+ picked.getName() + " and places in it " + MyFormatter.unitsToTextNoOwner(unitsToPlace), unitsToPlace);
m_bridge.addChange(change);
}
allPickableTerritories.remove(picked);
final PlayerID lastPlayer = m_currentPickingPlayer;
m_currentPickingPlayer = getNextPlayer(playersCanPick, m_currentPickingPlayer);
if (!playerCanPickMatch.match(lastPlayer)) {
playersCanPick.remove(lastPlayer);
}
if (playersCanPick.isEmpty()) {
m_currentPickingPlayer = null;
}
}
// place any remaining units
while (!playersCanPick.isEmpty()) {
if (m_currentPickingPlayer == null || !playersCanPick.contains(m_currentPickingPlayer)) {
m_currentPickingPlayer = playersCanPick.get(0);
}
final List<Territory> territoriesToPickFrom = data.getMap().getTerritoriesOwnedBy(m_currentPickingPlayer);
Tuple<Territory, Set<Unit>> pick;
Territory picked;
Set<Unit> unitsToPlace;
while (true) {
pick = getRemotePlayer(m_currentPickingPlayer).pickTerritoryAndUnits(
new ArrayList<>(territoriesToPickFrom),
new ArrayList<>(m_currentPickingPlayer.getUnits().getUnits()), UNITS_PER_PICK);
picked = pick.getFirst();
unitsToPlace = pick.getSecond();
if (!territoriesToPickFrom.contains(picked)
|| !m_currentPickingPlayer.getUnits().getUnits().containsAll(unitsToPlace)
|| unitsToPlace.size() > UNITS_PER_PICK || (unitsToPlace.size() < UNITS_PER_PICK
&& unitsToPlace.size() < m_currentPickingPlayer.getUnits().getUnits().size())) {
getRemotePlayer(m_currentPickingPlayer).reportMessage("Chosen territory or units invalid!",
"Chosen territory or units invalid!");
} else {
break;
}
}
final CompositeChange change = new CompositeChange();
final Collection<Unit> factoryAndInfrastructure = Match.getMatches(unitsToPlace, Matches.UnitIsInfrastructure);
if (!factoryAndInfrastructure.isEmpty()) {
change.add(OriginalOwnerTracker.addOriginalOwnerChange(factoryAndInfrastructure, m_currentPickingPlayer));
}
change.add(ChangeFactory.removeUnits(m_currentPickingPlayer, unitsToPlace));
change.add(ChangeFactory.addUnits(picked, unitsToPlace));
m_bridge.getHistoryWriter().addChildToEvent(m_currentPickingPlayer.getName() + " places "
+ MyFormatter.unitsToTextNoOwner(unitsToPlace) + " in territory " + picked.getName(), unitsToPlace);
m_bridge.addChange(change);
final PlayerID lastPlayer = m_currentPickingPlayer;
m_currentPickingPlayer = getNextPlayer(playersCanPick, m_currentPickingPlayer);
if (!playerCanPickMatch.match(lastPlayer)) {
playersCanPick.remove(lastPlayer);
}
if (playersCanPick.isEmpty()) {
m_currentPickingPlayer = null;
}
}
}
protected PlayerID getNextPlayer(final List<PlayerID> playersCanPick, final PlayerID currentPlayer) {
int index = playersCanPick.indexOf(currentPlayer);
if (index == -1) {
return null;
}
index++;
if (index >= playersCanPick.size()) {
index = 0;
}
return playersCanPick.get(index);
}
public Match<Territory> getTerritoryPickableMatch() {
return new CompositeMatchAnd<>(Matches.TerritoryIsLand, Matches.TerritoryIsNotImpassable,
Matches.isTerritoryOwnedBy(PlayerID.NULL_PLAYERID), Matches.TerritoryIsEmpty);
}
public Match<PlayerID> getPlayerCanPickMatch() {
return new Match<PlayerID>() {
@Override
public boolean match(final PlayerID player) {
if (player == null || player.equals(PlayerID.NULL_PLAYERID)) {
return false;
}
if (player.getUnits().isEmpty()) {
return false;
}
return !player.getIsDisabled();
}
};
}
@Override
public Class<? extends IRemote> getRemoteType() {
return null;
}
}
class RandomStartExtendedDelegateState implements Serializable {
private static final long serialVersionUID = 607794506772555083L;
Serializable superState;
// add other variables here:
public PlayerID m_currentPickingPlayer;
}
class UnitCostComparator implements Comparator<Unit> {
private final IntegerMap<UnitType> m_costs;
public UnitCostComparator(final IntegerMap<UnitType> costs) {
m_costs = costs;
}
public UnitCostComparator(final PlayerID player, final GameData data) {
m_costs = BattleCalculator.getCostsForTUV(player, data);
}
@Override
public int compare(final Unit u1, final Unit u2) {
return m_costs.getInt(u1.getType()) - m_costs.getInt(u2.getType());
}
}