//
// @(#)Retreat.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.order.result;
//
package dip.order;
import dip.order.result.OrderResult.ResultType;
import dip.world.*;
import dip.process.Adjudicator;
import dip.process.OrderState;
import dip.process.Tristate;
import dip.process.RetreatChecker;
import dip.misc.Log;
import dip.misc.Utils;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Implementation of the Retreat order.
* <p>
* Note that a Retreat order is a Move order. Retreat orders are issued
* instead of Move orders during the Retreat phase, by OrderParser.
* <p>
* Convoy options are not valid in Retreat orders.
*/
public class Retreat extends Move
{
/**
*
*/
private static final long serialVersionUID = -3349911124867265066L;
// il8n constants
private static final String RETREAT_SRC_EQ_DEST = "RETREAT_SRC_EQ_DEST";
private static final String RETREAT_CANNOT = "RETREAT_CANNOT";
private static final String RETREAT_FAIL_DPB = "RETREAT_FAIL_DPB";
private static final String RETREAT_FAIL_MULTIPLE = "RETREAT_FAIL_MULTIPLE";
private Tristate evalResult = Tristate.UNCERTAIN;
private static final String orderNameFull = "Retreat"; // brief order name is still 'M'
/** Creates a Retreat order */
protected Retreat(Power power, Location src, Unit.Type srcUnitType, Location dest)
{
super(power, src, srcUnitType, dest, false);
}// Move()
/** Creates a Retreat order */
protected Retreat()
{
super();
}// Retreat()
/** Retreats are never convoyed; this will always return false. */
public boolean isByConvoy()
{
return false;
}// isByConvoy()
public String getFullName()
{
return orderNameFull;
}// getFullName()
public boolean equals(Object obj)
{
if(obj instanceof Retreat)
{
Retreat retreat = (Retreat) obj;
if( super.equals(retreat)
&& this.dest.equals(retreat.dest) )
{
return true;
}
}
return false;
}// equals()
public void validate(TurnState state, ValidationOptions valOpts, RuleOptions ruleOpts)
throws OrderException
{
// step 0
checkSeasonRetreat(state, getFullName());
checkPower(power, state, true);
// 1
Position position = state.getPosition();
Unit unit = position.getDislodgedUnit( src.getProvince() );
super.validate(valOpts, unit);
if(valOpts.getOption(ValidationOptions.KEY_GLOBAL_PARSING).equals(ValidationOptions.VALUE_GLOBAL_PARSING_STRICT))
{
// 2
if(src.isProvinceEqual(dest))
{
throw new OrderException(Utils.getLocalString(RETREAT_SRC_EQ_DEST));
}
// validate Borders
Border border = src.getProvince().getTransit(src, srcUnitType, state.getPhase(), this.getClass());
if(border != null)
{
throw new OrderException( Utils.getLocalString(ORD_VAL_BORDER, src.getProvince(), border.getDescription()) );
}
// 3: validate destination [must be a legal move] This is important, because
// the coast of the destination needs to be known (if unit is a fleet).
// otherwise, RetreatChecker.isValid() won't work.
dest = dest.getValidatedWithMove(srcUnitType, src);
// check that we can transit into destination (check borders)
border = dest.getProvince().getTransit(dest, srcUnitType, state.getPhase(), this.getClass());
if(border != null)
{
throw new OrderException( Utils.getLocalString(ORD_VAL_BORDER, src.getProvince(), border.getDescription()) );
}
// 4
RetreatChecker rc = new RetreatChecker(state);
if( !rc.isValid(src, dest) )
{
throw new OrderException(Utils.getLocalString(RETREAT_CANNOT, dest));
}
}
}// validate()
/** No verification is performed.*/
public void verify(Adjudicator adjudicator)
{
OrderState thisOS = adjudicator.findOrderStateBySrc(getSource());
thisOS.setVerified(true);
}// verify()
/**
* Retreat orders are only dependent on other
* retreat orders that are moving to the same destination.
*/
public void determineDependencies(Adjudicator adjudicator)
{
// add moves to destination space, and supports of this space
OrderState thisOS = adjudicator.findOrderStateBySrc(getSource());
ArrayList depMTDest = null;
OrderState[] orderStates = adjudicator.getOrderStates();
for(int osIdx=0; osIdx<orderStates.length; osIdx++)
{
OrderState dependentOS = orderStates[osIdx];
Order order = dependentOS.getOrder();
if( order instanceof Retreat
&& order != this )
{
Retreat retreat = (Retreat) order;
if( retreat.getDest().isProvinceEqual(this.getDest()) )
{
if(depMTDest == null) { depMTDest = new ArrayList(4); }
depMTDest.add(dependentOS);
}
}
}
// set dependent moves to destination
if(depMTDest != null)
{
thisOS.setDependentMovesToDestination(depMTDest);
}
}// determineDependencies()
/**
* If a retreat is valid, it will be successfull unless
* there exists one or more retreats to the same destination.
*/
public void evaluate(Adjudicator adjudicator)
{
OrderState thisOS = adjudicator.findOrderStateBySrc(getSource());
// while Retreat orders cannot be supported, we DO need to determine
// if we are retrating across a difficult passable border.
// We only need to set 'max' support. For the typical case (base move
// modifier == -1), this will return "1" unless over a DPB (base move
// modifier -1) which will return "0".
//
thisOS.setRetreatStrength( thisOS.getSupport(false) );
Log.println("--- evaluate() dip.order.Retreat ---");
Log.println(" order: ", this);
Log.println(" retreat strength: [dpb check]: ", thisOS.getRetreatStrength());
Log.println(" # moves to dest: ", thisOS.getDependentMovesToDestination().length);
// if other retreats to destination, we will succeed; otherwise, we will
// probably fail.
if(thisOS.getEvalState() == Tristate.UNCERTAIN)
{
OrderState[] depMovesToDest = thisOS.getDependentMovesToDestination();
if(depMovesToDest.length == 0)
{
// typical case
Log.println(" SUCCESS!");
thisOS.setEvalState(Tristate.SUCCESS);
}
else
{
// Modified for Difficult Passsable Border (see DATC 16-dec-03 10.K)
// But must differentiate DPB from multiple retreats to same location.
//
// Furthermore, we need to account for the possibility of 3 retreats
// to 1 dest, where 2 retreats are over a normal border and 1 is over
// a DPB; that would cause all retreats to fail.
//
// Thus, if we are <= strength of others, we fail. Otherwise we are >, and
// successful.
//
// This logic *MUST* be run in an evaluation loop.
//
Tristate evalResult = Tristate.UNCERTAIN;
boolean isStrongerThanAllOthers = false;
for(int i=0; i<depMovesToDest.length; i++)
{
OrderState depMoveOS = depMovesToDest[i];
Move depMove = (Move) depMoveOS.getOrder();
if(depMoveOS.isRetreatStrengthSet())
{
if(thisOS.getRetreatStrength() < depMoveOS.getRetreatStrength())
{
// only can be less when considering DPBs
Log.println(" FAILURE! (<)", depMoveOS.getOrder());
evalResult = Tristate.FAILURE;
adjudicator.addResult(thisOS, ResultType.FAILURE, Utils.getLocalString(RETREAT_FAIL_DPB));
isStrongerThanAllOthers = false;
break;
}
else if(thisOS.getRetreatStrength() == depMoveOS.getRetreatStrength())
{
// the usual case
Log.println(" FAILURE! (==)", depMoveOS.getOrder());
evalResult = Tristate.FAILURE;
adjudicator.addResult(thisOS, ResultType.FAILURE, Utils.getLocalString(RETREAT_FAIL_MULTIPLE));
isStrongerThanAllOthers = false;
break;
}
else // >
{
// we *may* be stronger than all others
Log.println(" We may be stronger than all others...");
isStrongerThanAllOthers = true;
}
}
else
{
// remain uncertain. Dependent orderstate not yet evaluated.
Log.println(" Uncertain: ", depMoveOS.getOrder(), " not yet evaluated.");
isStrongerThanAllOthers = false; // we don't know yet.
evalResult = Tristate.UNCERTAIN;
}
}// for()
// if we are stronger than all others, we will succeed.
if(isStrongerThanAllOthers)
{
Log.println(" SUCCESS! [stronger than all others]");
evalResult = Tristate.UNCERTAIN;
}
thisOS.setEvalState(evalResult);
}
}
Log.println(" final evalState() = ", thisOS.getEvalState());
}// evaluate()
}// class Retreat