// // @(#)Adjustment.java 1.00 4/1/2002 // // 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.process; import dip.world.Power; import dip.world.Province; import dip.world.TurnState; import dip.world.Position; import dip.world.Unit; import dip.world.RuleOptions; import java.util.List; import java.util.Iterator; import java.util.HashMap; /** * * Calculates Adjustments (how many units a power may build or must remove). * * * */ public class Adjustment { private Adjustment() { }// Adjustment() /** * Determines AdjustmentInfo for a given power * <p> * Note that this will work during any phase. */ public static AdjustmentInfo getAdjustmentInfo(TurnState turnState, RuleOptions ruleOpts, Power power) { if(power == null || turnState == null) { throw new IllegalArgumentException(); } AdjustmentInfo ai = new AdjustmentInfo(ruleOpts); Position position = turnState.getPosition(); final Province[] provinces = position.getProvinces(); for(int i=0; i<provinces.length; i++) { Province province = provinces[i]; // tally units Unit unit = position.getUnit(province); if( unit != null && unit.getPower().equals(power) ) { ai.numUnits++; } unit = position.getDislodgedUnit(province); if( unit != null && unit.getPower().equals(power) ) { ai.numDislodgedUnits++; } // tally supply centers if(power.equals(position.getSupplyCenterOwner(province))) { ai.numSC++; if(power.equals(position.getSupplyCenterHomePower(province))) { ai.numHSC++; } } } return ai; }// getAdjustmentInfo() /** * Determines AdjustmentInfo for all Powers; * <p> * Note that this will work during any phase. * <p> * Results are returned as an AdjustmentInfoMap */ public static AdjustmentInfoMap getAdjustmentInfo(TurnState turnState, RuleOptions ruleOpts, Power[] powers) { if(powers == null || turnState == null) { throw new IllegalArgumentException(); } // setup AdjustmentInfoMap AdjustmentInfoMap adjMap = new AdjustmentInfoMap(); for(int i=0; i<powers.length; i++) { adjMap.put(powers[i], new AdjustmentInfo(ruleOpts)); } // Iterate for all Powers Position position = turnState.getPosition(); final Province[] provinces = position.getProvinces(); for(int i=0; i<provinces.length; i++) { Province province = provinces[i]; boolean hasUnit = false; // tally units Unit unit = position.getUnit(province); if(unit != null) { adjMap.get(unit.getPower()).numUnits++; hasUnit = true; } unit = position.getDislodgedUnit(province); if(unit != null) { adjMap.get(unit.getPower()).numDislodgedUnits++; } // tally supply centers Power power = position.getSupplyCenterOwner(province); if(power != null) { adjMap.get(power).numSC++; if(hasUnit) { adjMap.get(power).numOccSC++; } power = position.getSupplyCenterHomePower(province); if(power != null) { adjMap.get(power).numHSC++; if(hasUnit) { adjMap.get(power).numOccHSC++; } } } } return adjMap; }// getAdjustmentInfo() /** Class containing various information about Adjustments, for a given Power */ public static class AdjustmentInfo { // these private fields may still be set by enclosing class // private int numUnits = 0; // # of units total private int numSC = 0; // # of owned supply centers private int numHSC = 0; // # of owned home supply centers private int numOccHSC = 0; // # of occupied owned home supply centers private int numOccSC = 0; // # of occupied non-home supply centers private int numDislodgedUnits = 0; // # of dislodged units private int adj = 0; // the adjustment amount, as determined by calculate() private boolean isCalc = false; // if we have been calculated private RuleOptions ruleOpts; // for calculating /** Construct an AdjustmentInfo object */ protected AdjustmentInfo(RuleOptions ruleOpts) { if(ruleOpts == null) { throw new IllegalArgumentException(); } this.ruleOpts = ruleOpts; }// AdjustmentInfo() /** # of units to adjust (+/0/-) from current unit count. * * If units cannot be built, because supply center is occupied, * that is taken into account */ public int getAdjustmentAmount() { checkCalc(); return adj; }// getAdjustmentAmount() /** * Force calculation of adjustment amount, and adjust supply-center * information according to Build Options * <p> * IT IS VITAL for this method to be called after values are set, and * before any values are read back. * <p> * There should be no effect if this method is called multiple times, * as long as RuleOptions do NOT change in between callings. */ private void calculate() { RuleOptions.OptionValue buildOpt = ruleOpts.getOptionValue(RuleOptions.OPTION_BUILDS); assert(numOccSC <= numSC); if(buildOpt == RuleOptions.VALUE_BUILDS_HOME_ONLY) { // Adjustment = number of SC gained. But, if we have gained more adjustments // than we have home supply centers to build on, those builds are discarded. // Or, if some are occupied, those builds are discarded. // e.g.: // 3 builds, 3 empty owned home supply centers: adjustments: +3 // 3 builds, 2 empty owned home supply centers: adjustments: +2 adj = numSC - numUnits; adj = (adj > (numHSC - numOccHSC)) ? (numHSC - numOccHSC) : adj; } else if(buildOpt == RuleOptions.VALUE_BUILDS_ANY_OWNED) { // We can build in any owned supply center. Effectively, then, // ALL owned supply centers are home supply centers. numHSC = numSC; adj = (numSC - numUnits); adj = (adj > (numSC - numOccSC)) ? (numSC - numOccSC) : adj; } else if(buildOpt == RuleOptions.VALUE_BUILDS_ANY_IF_HOME_OWNED) { // We can build in any supply center, if at least ONE home supply // center is owned. adj = numSC - numUnits; adj = (adj > 0 && numHSC < 1) ? 0 : adj; adj = (adj > (numSC - numOccSC)) ? (numSC - numOccSC) : adj; } else { // should not occur throw new IllegalStateException(); } isCalc = true; }// calculate() /** # of units for this power */ public int getUnitCount() { checkCalc(); return numUnits; } /** # of dislodged units for this power */ public int getDislodgedUnitCount() { checkCalc(); return numDislodgedUnits; } /** # of supply centers for this power (includes home supply centers) */ public int getSupplyCenterCount() { checkCalc(); return numSC; } /** # of home supply centers */ public int getHomeSupplyCenterCount() { checkCalc(); return numHSC; } /** mostly for debugging */ public String toString() { StringBuffer sb = new StringBuffer(128); sb.append("[AdjustmentInfo: units="); sb.append(numUnits); sb.append("; supplycenters="); sb.append(numSC); sb.append("; home supplycenters="); sb.append(numHSC); sb.append("; adjustment="); sb.append(getAdjustmentAmount()); sb.append(']'); return sb.toString(); }// toString() /** Checks if we have been calculated. */ private void checkCalc() { if(!isCalc) { calculate(); } }// checkCalc() }// nested class AdjustmentInfo /** * Aggregation of HashMap that contains only AdjustmentInfo objects, * mapped by Power. * */ public static class AdjustmentInfoMap { private HashMap map; /** Create an AdjustmentInfoMap */ public AdjustmentInfoMap() { map = new HashMap(13); }// AdjustmentInfoMap() /** Create an AdjustmentInfoMap */ public AdjustmentInfoMap(int size) { map = new HashMap(size); }// AdjustmentInfoMap() /** Set AdjustmentInfo for a power. */ private void put(Power power, AdjustmentInfo ai) { map.put(power, ai); }// put() /** Gets AdjustmentInfo for a power. */ public AdjustmentInfo get(Power power) { return (AdjustmentInfo) map.get(power); }// get() /** Clears all information from this object. */ public void clear() { map.clear(); }// clear() }// nested class AdjustmentInfoMap }// class Adjustment