//
// @(#)Position.java 2/2003
//
// Copyright 2002 Zachary DelProposto. All rights reserved.
// Use is subject to license terms.
//
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// Or from http://www.gnu.org/
//
package dip.world;
import dip.world.Province;
import dip.world.Power;
import dip.world.Unit;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashMap;
import java.util.Iterator;
/**
*
* Stores all the mutable (state) information for a given TurnState.
* Immutable data is retained in the Power/Province/etc. objects; only
* mutable data is stored here.
* <p>
* This object can be cloned, and should be cloned, when creating a new
* Position based upon previous Position data. Several clone methods are
* available, each optimized for speed and cloning requirements.
* <p>
* WARNING: this code is not MT (Multithread) safe!
* <p>
* This class is heavily optimized, as adjudicator performance is highly dependent
* upon the performance of this class.
* <p>
* The clone() methods are not strictly implemented; they call a constructor
* to assist in cloning rather than call super.clone(). This is done for
* performance reasons.
*/
public class Position implements java.io.Serializable, Cloneable
{
// size constants; these should be prime
private static final int POWER_SIZE = 17;
// instance variables
protected final Map powerMap = new HashMap(POWER_SIZE);
protected final ProvinceData[] provArray;
protected final dip.world.Map map;
private transient Province[] tmpProvArray = null;
public Position(dip.world.Map map)
{
this.map = map;
this.provArray = new ProvinceData[map.getProvinces().length];
}// Position()
/** The Number of Provinces in this Position */
public final int size()
{
return provArray.length;
}// size()
/** Convenience method: Returns an array of Provinces */
public final Province[] getProvinces()
{
return map.getProvinces();
}// getProvinces()
/** Returns true if this Power has been eliminated. False by default. */
public boolean isEliminated(Power power)
{
PowerData pd = (PowerData) powerMap.get(power);
if(pd != null)
{
return pd.isEliminated();
}
return false;
}// isEliminated()
/** Set whether this Power has been eliminated. */
public void setEliminated(Power power, boolean value)
{
PowerData pd = getPowerData(power);
pd.setEliminated(value);
}// setEliminated()
/**
* Scans the Position; sets/unsets elimination depending upon if a given
* Power has any units (including dislodged units) or supply centers on the map
*/
public void setEliminationStatus(final Power[] powers)
{
HashMap pmap = new HashMap(19);
for(int i=0; i<powers.length; i++)
{
pmap.put(powers[i], null);
}
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
Power power = null;
if(pd != null)
{
Unit unit = pd.getUnit();
if(unit != null) // first check non-dislodged units
{
power = unit.getPower();
if(pmap.get(power) == null)
{
pmap.put(power, new Object());
}
}
// then see if there's a dislodged unit
unit = pd.getDislodgedUnit();
if(unit != null)
{
power = unit.getPower();
if(pmap.get(power) == null)
{
pmap.put(power, new Object());
}
}
// finally, see if we own a supply center
power = pd.getSCOwner();
if(power != null)
{
if(pmap.get(power) == null)
{
pmap.put(power, new Object());
}
}
}
}
for(int i=0; i<powers.length; i++)
{
if(pmap.get(powers[i]) == null)
{
setEliminated(powers[i], true);
}
else
{
setEliminated(powers[i], false);
}
}
}// setEliminationStatus()
/** Set the owner of the supply center. */
public void setSupplyCenterOwner(Province province, Power power)
{
ProvinceData pd = getProvinceData(province);
pd.setSCOwner(power);
}// setSupplyCenterOwner()
/** Set the owner of a home supply center. */
public void setSupplyCenterHomePower(Province province, Power power)
{
ProvinceData pd = getProvinceData(province);
pd.setSCHomePower(power);
}// setSupplyCenterHomePower()
/** Determine if this Province contains a supply center */
public boolean hasSupplyCenterOwner(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.isSCOwned();
}
return false;
}// hasSupplyCenterOwner()
/** Determine if this Province contains a Home supply center */
public boolean isSupplyCenterAHome(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.isSCAHome();
}
return false;
}// isSupplyCenterAHome()
/** Get the home power of the supply center; null if no supply center or home power */
public Power getSupplyCenterHomePower(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.getSCHomePower();
}
return null;
}// getSupplyCenterHomePower()
/** Get the owner of the supply center; null if no owner or no supply center. */
public Power getSupplyCenterOwner(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.getSCOwner();
}
return null;
}// getSupplyCenterOwner()
// non-dislodged unit
/** Set the unit contained in this province; null to eliminate an existing unit. */
public void setUnit(Province province, Unit unit)
{
ProvinceData pd = getProvinceData(province);
pd.setUnit(unit);
}// setUnit()
/** Determines if there is a unit present in this province. */
public boolean hasUnit(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.hasUnit();
}
return false;
}// hasUnit()
/** Get the unit contained in this Province. Returns null if no unit exists. */
public Unit getUnit(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.getUnit();
}
return null;
}// getUnit()
/** Test if the given type of unit is contained in this Province. */
public boolean hasUnit(Province province, Unit.Type unitType)
{
Unit unit = getUnit(province);
if(unit != null)
{
return(unit.getType().equals(unitType));
}
return false;
}// hasUnit()
/** Test if the given type of unit is contained in this Province. */
public boolean hasDislodgedUnit(Province province, Unit.Type unitType)
{
Unit unit = getDislodgedUnit(province);
if(unit != null)
{
return(unit.getType().equals(unitType));
}
return false;
}// hasDislodgedUnit()
// dislodged unit
/** Set the dislodged unit contained in this province; null to eliminate an existing unit. */
public void setDislodgedUnit(Province province, Unit unit)
{
ProvinceData pd = getProvinceData(province);
pd.setDislodgedUnit(unit);
}// setDislodgedUnit()
/** Get the dislodged unit in this Province. Returns null if no dislodged unit exists. */
public Unit getDislodgedUnit(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.getDislodgedUnit();
}
return null;
}// getDislodgedUnit()
// last occupier
/**
* Sets the Power that last occupied a given space. Note that this
* is not intended to be used for Supply Center ownership (which only
* changes in the Fall season); use setSupplyCenterOwner() instead.
*/
public void setLastOccupier(Province province, Power power)
{
ProvinceData pd = getProvinceData(province);
pd.setLastOccupier(power);
}// setLastOccupier()
/**
* Returns the Power that last occupied a given space. Note that this
* is not intended to be used for Supply Center ownership (which only
* changes in the Fall season); use getSupplyCenterOwner() instead.
*/
public Power getLastOccupier(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.getLastOccupier();
}
return null;
}// getLastOccupier()
/** Determines if there is a dislodged unit present in this province. */
public boolean hasDislodgedUnit(Province province)
{
ProvinceData pd = provArray[province.getIndex()];
if(pd != null)
{
return pd.hasDislodgedUnit();
}
return false;
}// hasDislodgedUnit()
/** Returns an array of provinces with non-dislodged units */
public Province[] getUnitProvinces()
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && pd.hasUnit())
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getUnitProvinces()
/** Returns an array of provinces with dislodged units */
public Province[] getDislodgedUnitProvinces()
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && pd.hasDislodgedUnit())
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getDislodgedUnitProvinces()
/** Returns the number of provinces with non-dislodged units */
public int getUnitCount()
{
int count = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && pd.hasUnit())
{
count++;
}
}
return count;
}// getUnitCount()
/** Returns the number of provinces with dislodged units */
public int getDislodgedUnitCount()
{
int count = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && pd.hasDislodgedUnit())
{
count++;
}
}
return count;
}// getDislodgedUnitCount()
/** Returns an array of provinces with home supply centers */
public Province[] getHomeSupplyCenters()
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && pd.isSCAHome())
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getHomeSupplyCenters()
/** Returns an Array of the Home Supply Centers for a given power (whether or not they are owned by that power) */
public Province[] getHomeSupplyCenters(Power power)
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && power.equals(pd.getSCHomePower()))
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getHomeSupplyCenters()
/**
* Determines if a Power has at least one owned Home Supply Center.
* <p>
* An owned home supply center need not have a unit present.
*/
public boolean hasAnOwnedHomeSC(Power power)
{
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && pd.getSCHomePower().equals(power) && pd.getSCOwner().equals(power))
{
return true;
}
}
return false;
}// hasAnOwnedHomeSC()
/** Returns an Array of the owned Supply Centers for a given Power (whether or not they are home supply centers) */
public Province[] getOwnedSupplyCenters(Power power)
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && power.equals(pd.getSCOwner()))
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getOwnedSupplyCenters()
/** Returns an array of provinces with owned supply centers */
public Province[] getOwnedSupplyCenters()
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null && pd.isSCOwned())
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getOwnedSupplyCenters()
/**
* Deep clone of the contents of this Position.
*/
public Object clone()
{
Position pos = new Position(map);
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null)
{
pos.provArray[i] = pd.normClone();
}
}
Iterator iter = powerMap.keySet().iterator();
while(iter.hasNext())
{
Power key = (Power) iter.next();
PowerData pd = (PowerData) powerMap.get(key);
pos.powerMap.put(key, pd.normClone());
}
return pos;
}// clone()
/**
* Deep clone of everything *except* dislodged & non-dislodged units;
* (e.g., SC ownership, Power Info, etc.)
*/
public Position cloneExceptUnits()
{
Position pos = new Position(map);
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null)
{
pos.provArray[i] = pd.cloneExceptUnits();
}
}
Iterator iter = powerMap.keySet().iterator();
while(iter.hasNext())
{
Power key = (Power) iter.next();
PowerData pd = (PowerData) powerMap.get(key);
pos.powerMap.put(key, pd.normClone());
}
return pos;
}// cloneExceptUnits()
/** Deep clone of everything <b>except</b> dislodged units. */
public Position cloneExceptDislodged()
{
Position pos = new Position(map);
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null)
{
pos.provArray[i] = pd.cloneExceptDislodged();
}
}
Iterator iter = powerMap.keySet().iterator();
while(iter.hasNext())
{
Power key = (Power) iter.next();
PowerData pd = (PowerData) powerMap.get(key);
pos.powerMap.put(key, pd.normClone());
}
return pos;
}// cloneExceptDislodged()
/**
* Gets all the Provinces with non-dislodged
* Units for a particular power.
*
*/
public Province[] getUnitProvinces(Power power)
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null)
{
Unit unit = pd.getUnit();
if(unit != null && unit.getPower().equals(power))
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getUnitProvinces()
/**
* Gets all the Provinces with dislodged
* Units for a particular power.
*/
public Province[] getDislodgedUnitProvinces(Power power)
{
makeTmpProvArray();
int arrSize = 0;
for(int i=0; i<provArray.length; i++)
{
ProvinceData pd = provArray[i];
if(pd != null)
{
Unit unit = pd.getDislodgedUnit();
if(unit != null && unit.getPower().equals(power))
{
tmpProvArray[arrSize] = map.reverseIndex(i);
arrSize++;
}
}
}
Province[] p = new Province[arrSize];
System.arraycopy(tmpProvArray, 0, p, 0, arrSize);
return p;
}// getDislodgedUnitProvinces()
/**
* Call this method FIRST before any set(); thus if ProvinceData
* does not exist, we will add one to the map.
*
*/
private ProvinceData getProvinceData(Province province)
{
int idx = province.getIndex();
ProvinceData pd = provArray[idx];
if(pd == null)
{
pd = new ProvinceData();
provArray[idx] = pd;
}
return pd;
}// getProvinceData()
/** Same type of functionality as getProvinceData() but for PowerData objects */
private PowerData getPowerData(Power power)
{
PowerData pd = (PowerData) powerMap.get(power);
if(pd == null)
{
pd = new PowerData();
powerMap.put(power, pd);
}
return pd;
}// getPowerData()
/** All mutable Province data is kept here */
private class ProvinceData implements java.io.Serializable
{
// instance variables
private Unit unit = null;
private Unit dislodgedUnit = null;
private Power SCOwner = null;
private Power SCHomePower = null;
private Power lastOccupier = null;
// unit set/get
public boolean hasUnit() { return (unit != null); }
public boolean hasDislodgedUnit() { return (dislodgedUnit != null); }
public Unit getUnit() { return unit; }
public Unit getDislodgedUnit() { return dislodgedUnit; }
public void setUnit(Unit u) { unit = u; }
public void setDislodgedUnit(Unit u) { dislodgedUnit = u; }
// SC set/get
public boolean isSCAHome() { return (SCHomePower != null); }
public boolean isSCOwned() { return (SCOwner != null); }
public void setSCOwner(Power p) { SCOwner = p; }
public void setSCHomePower(Power p) { SCHomePower = p; }
public Power getSCOwner() { return SCOwner; }
public Power getSCHomePower() { return SCHomePower; }
// occupier set/get
public Power getLastOccupier() { return lastOccupier; }
public void setLastOccupier(Power p) { lastOccupier = p; }
// normal clone
public ProvinceData normClone()
{
ProvinceData pd = new ProvinceData();
// deep copy unit information
if(unit != null)
{
pd.unit = (Unit) unit.clone();
}
if(dislodgedUnit != null)
{
pd.dislodgedUnit = (Unit) dislodgedUnit.clone();
}
// shallow copy Powers [Power is immutable]
pd.SCOwner = this.SCOwner;
pd.SCHomePower = this.SCHomePower;
pd.lastOccupier = this.lastOccupier;
return pd;
}// normClone()
/** Returns null if no non-ownership information exists */
public ProvinceData cloneExceptUnits()
{
// don't create an object if there is no ownership info.
// this also compacts the Position map!
if(SCOwner == null && SCHomePower == null && lastOccupier == null)
{
return null;
}
// create a ProvinceData object
ProvinceData pd = new ProvinceData();
// shallow copy Power [Power is immutable]
pd.SCOwner = this.SCOwner;
pd.SCHomePower = this.SCHomePower;
pd.lastOccupier = this.lastOccupier;
return pd;
}// cloneExceptUnits()
/** Returns null if no ownership info/unit exists */
public ProvinceData cloneExceptDislodged()
{
// don't create an object if there is no ownership info.
// this also compacts the Position map!
if(SCOwner == null && SCHomePower == null && unit == null && lastOccupier == null)
{
return null;
}
// create a ProvinceData object
ProvinceData pd = new ProvinceData();
// shallow copy Power [Power is immutable]
pd.SCOwner = this.SCOwner;
pd.SCHomePower = this.SCHomePower;
pd.lastOccupier = this.lastOccupier;
// deep copy unit
if(unit != null)
{
pd.unit = (Unit) unit.clone();
}
return pd;
}// cloneExceptUnits()
}// inner class ProvinceData
/** All mutable Power data is kept here */
private class PowerData implements java.io.Serializable
{
/**
*
*/
private static final long serialVersionUID = 5042765689873295374L;
// instance variables
private boolean isEliminated = false;
public PowerData()
{
}// PowerData()
public boolean isEliminated() { return isEliminated; }
public void setEliminated(boolean value) { isEliminated = value; }
public PowerData normClone()
{
PowerData pd = new PowerData();
pd.isEliminated = this.isEliminated;
return pd;
}// normClone()
}// inner class PowerData
/**
* Copying references into a large temporary array, then creating an array
* and copying the temp array into the correctly-sized array via
* System.arraycopy() is about twice as fast as using an ArrayList, and
* almost twice as fast as double-iterating (one to gauge the array size,
* and one to copy data).
* <p>
* This method creates a sufficiently large array to hold temporary data.
*/
private final void makeTmpProvArray()
{
if(tmpProvArray == null)
{
tmpProvArray = new Province[provArray.length];
}
}// makeTmpProvArray()
}// class Position