// // @(#)VictoryConditions.java 4/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.world; import dip.world.Phase.PhaseType; import dip.world.Phase.SeasonType; import dip.process.Adjudicator; import dip.process.Adjustment; import dip.order.result.Result; import dip.misc.Utils; import java.util.HashMap; import java.util.List; import java.util.ArrayList; /** * * Establishes the conditions required to determine who wins a game, and contains * methods to evaluate if these condtions are met during adjudication. * <p> */ public class VictoryConditions implements java.io.Serializable { // il8n private static final String VC_MAX_GAME_TIME = "VC_MAX_GAME_TIME"; private static final String VC_DRAW = "VC_DRAW"; private static final String VC_WIN_SINGLE = "VC_WIN_SINGLE"; private static final String VC_MAX_NO_SC_CHANGE = "VC_MAX_NO_SC_CHANGE"; // class variables protected final int numSCForVictory; // SCs required for victory; 0 if ignored protected final int maxYearsNoSCChange; // max years w/o supply-center chaning hands; 0 if ignored protected final int maxGameTimeYears; // max time, in years, a game may last protected final int initialYear; // starting game year // transient variables protected transient List evalResults = null; /** VictoryConditions constructor */ public VictoryConditions(int numSCForVictory, int maxYearsNoSCChange, int maxGameTimeYears, Phase initialPhase) { if(maxGameTimeYears < 0 || numSCForVictory < 0 || maxYearsNoSCChange < 0) { throw new IllegalArgumentException("arg: < 0; use 0 to disable"); } if(initialPhase == null) { throw new IllegalArgumentException("args invalid"); } if(maxGameTimeYears == 0 && numSCForVictory == 0 && maxYearsNoSCChange == 0) { throw new IllegalArgumentException("no conditions set!"); } this.numSCForVictory = numSCForVictory; this.maxYearsNoSCChange = maxYearsNoSCChange; this.maxGameTimeYears = maxGameTimeYears; this.initialYear = initialPhase.getYear(); }// VictoryConditions() /** Returns the number of Supply Centers required for victory. */ public int getSCsRequiredForVictory() { return numSCForVictory; } /** Returns number of Years without any Supply Center being captured for the game to end. */ public int getYearsWithoutSCChange() { return maxYearsNoSCChange; } /** Returns number maximum game duration, in years. */ public int getMaxGameDurationYears() { return maxGameTimeYears; } /** * Returns the Result(s) of evaluate(). This will return an empty list if * evaluate() has not been called or returned false. */ public List getEvaluationResults() { return evalResults; }// getEvaluationResults() /** * Evaluates the victory conditions. Returns <code>true</code> if * victory has been achieved (via any condition). * <p> * This method defers to the * <code>evaluate(TurnState, Adjustment.AdjustmentInfoMap)</code> method. * <p> * @param adjMap adjustment map (as returned by Adjustment.getAdjustmentInfo()) * @param adjudicator an Adjudicator object */ public boolean evaluate(Adjudicator adjudicator, Adjustment.AdjustmentInfoMap adjMap) { return evaluate(adjudicator.getTurnState(), adjMap); }// evaluate() /** * Evaluates the victory conditions. Returns <code>true</code> if * victory has been achieved (via any condition). * <p> * @param adjMap adjustment map (as returned by Adjustment.getAdjustmentInfo()) * @param turnState the TurnState */ public boolean evaluate(TurnState turnState, Adjustment.AdjustmentInfoMap adjMap) { Phase phase = turnState.getPhase(); final int currentYear = phase.getYear(); if(evalResults == null) { evalResults = new ArrayList(5); } else { evalResults.clear(); } // create an array of AdjustmentInfo, indexed the same as the array of Powers, // from the passed HashMap final Power[] powers = turnState.getWorld().getMap().getPowers(); final Adjustment.AdjustmentInfo[] adjInfo = new Adjustment.AdjustmentInfo[powers.length]; for(int i=0; i<adjInfo.length; i++) { adjInfo[i] = adjMap.get(powers[i]); } // check to see if we have exceeded the allocated time if(maxGameTimeYears > 0) { if( (currentYear - initialYear + 1) >= maxGameTimeYears ) { evalResults.add(new Result(null, Utils.getLocalString(VC_MAX_GAME_TIME, new Integer(maxGameTimeYears)))); evalResults.add(new Result(null, Utils.getLocalString(VC_DRAW, getRemainingPowers(turnState, powers, adjInfo)) )); return true; } } // check for single-power victory (via controlling numSCForVictory supply centers) if(numSCForVictory > 0) { for(int i=0; i<adjInfo.length; i++) { if(adjInfo[i].getSupplyCenterCount() >= numSCForVictory) { evalResults.add( new Result(null, Utils.getLocalString(VC_WIN_SINGLE, powers[i], new Integer(adjInfo[i].getSupplyCenterCount()), new Integer(numSCForVictory)) )); return true; } } } // check # of years w/o any supply centers captured // (if game exists for less than this #, do nothing). if( maxYearsNoSCChange > 0 && (currentYear - initialYear) >= maxYearsNoSCChange && !turnState.getSCOwnerChanged() ) { // we first check the current turnstate. We do this because it may not yet have been // added to the World object (via setTurnState()). We assume it is a fall movement // or retreat phase as well. boolean overallSCChange = turnState.getSCOwnerChanged(); World world = turnState.getWorld(); for(int year=(currentYear - 1); year>(currentYear - maxYearsNoSCChange); year--) { overallSCChange |= getIfSCChangeOccured(world, year); } if(!overallSCChange) { evalResults.add(new Result(null, Utils.getLocalString(VC_MAX_NO_SC_CHANGE, new Integer(maxYearsNoSCChange)))); evalResults.add(new Result(null, Utils.getLocalString(VC_DRAW, getRemainingPowers(turnState, powers, adjInfo)) )); return true; } } return false; }// evaluate() /** * Given a year, finds the phase in which supply-center-changes could occur. * * the adjudicator marks the NEXT phase, indicating that supply center changes * have occured. Thus, the next phase will be either RETREAT (if a change occured * during movement) or ADJUSTMENT (if a change occured during retreat) * * where supply-center-changes could occur. We actually will check both; */ private boolean getIfSCChangeOccured(World world, int year) { boolean value = false; TurnState tsRetreat = world.getTurnState(new Phase(SeasonType.FALL, year, PhaseType.RETREAT)); TurnState tsAdjustment = world.getTurnState(new Phase(SeasonType.FALL, year, PhaseType.ADJUSTMENT)); if(tsRetreat != null) { value |= tsRetreat.getSCOwnerChanged(); } if(tsAdjustment != null) { value |= tsAdjustment.getSCOwnerChanged(); } return value; }// getLastSCChangePhase() // creates a comma-seperated list of power names, if they are still in play private String getRemainingPowers(TurnState turnState, Power[] powers, Adjustment.AdjustmentInfo[] adjInfo) { StringBuffer sb = new StringBuffer(128); Position pos = turnState.getPosition(); for(int i=0; i<powers.length; i++) { // check for power elimination [note: check might be redundant...] // we need to check adjInfo because we check for victory BEFORE powers may // be eliminated in the adjustment phase. if(adjInfo[i].getSupplyCenterCount() > 0 && !pos.isEliminated(powers[i])) { sb.append(powers[i]); sb.append(", "); } } // delete last comma if(sb.length() > 2) { sb.delete(sb.length() - 2, sb.length()); } return sb.toString(); }// getRemainingPowers() }// class VictoryConditions