PageRenderTime 25ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/dip/process/RetreatChecker.java

https://github.com/DetriusXii/jdip
Java | 350 lines | 157 code | 40 blank | 153 comment | 46 complexity | 2497b8e45ae6a5c7caf1c57e57f6e77c MD5 | raw file
  1. //
  2. // @(#)RetreatChecker.java 1.00 4/1/2002
  3. //
  4. // Copyright 2002 Zachary DelProposto. All rights reserved.
  5. // Use is subject to license terms.
  6. //
  7. //
  8. // This program is free software; you can redistribute it and/or modify
  9. // it under the terms of the GNU General Public License as published by
  10. // the Free Software Foundation; either version 2 of the License, or
  11. // (at your option) any later version.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU General Public License
  19. // along with this program; if not, write to the Free Software
  20. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. // Or from http://www.gnu.org/
  22. //
  23. package dip.process;
  24. import java.util.ArrayList;
  25. import java.util.HashMap;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import dip.order.Move;
  29. import dip.order.Orderable;
  30. import dip.order.result.OrderResult;
  31. import dip.order.result.Result;
  32. import dip.world.Location;
  33. import dip.world.Position;
  34. import dip.world.Province;
  35. import dip.world.TurnState;
  36. import dip.world.World;
  37. /**
  38. * RetreatChecker analyzes the current TurnState and the results of the previous
  39. * TurnState to determine which (if any) retreat locations are acceptable for a
  40. * retreating unit. Thus it is only dependent upon the adjudication results, and
  41. * the current Position.
  42. * <p>
  43. * Basic Retreat Algorithm:<br>
  44. * <ul>
  45. * <li> locations must be adjacent to dislodged unit
  46. * <li> locations must be unoccupied
  47. * <li> unoccupied location must not be that of the dislodging attacks origin,
  48. * unless that attack occured was by a (successful) convoyed Move
  49. * <li> unoccupied locations must not be involved in a standoff (2 or more
  50. * unsuccesful Moves)
  51. * </ul>
  52. *
  53. * Should be threadsafe.
  54. */
  55. public class RetreatChecker {
  56. // instance variables
  57. private transient final Position position;
  58. private transient final List<RCMoveResult> filteredMoveResults;
  59. /**
  60. * Create a RetreatChecker.
  61. * <p>
  62. * There must be at least one prior TurnState in the World object for
  63. * this to work, however, if we a unit is Dislodged and it is the very
  64. * first TurnState (this can happen if the game is edited), it is allowed.
  65. */
  66. public RetreatChecker(TurnState current) {
  67. List<Result> results;
  68. TurnState last = current.getWorld().getPreviousTurnState(current);
  69. if (last == null) {
  70. // if we are the very first TurnState, last==null is permissable,
  71. // but we must take special action to make it work
  72. World w = current.getWorld();
  73. if (w.getInitialTurnState() == current) {
  74. //Log.println(" no previous turnstate, and we are first; creating results");
  75. results = new ArrayList<Result>();
  76. } else {
  77. throw new IllegalStateException("No Previous Turn State!!");
  78. }
  79. } else {
  80. results = last.getResultList();
  81. //Log.println(" last turnstate: ",last.getPhase());
  82. }
  83. this.position = current.getPosition();
  84. this.filteredMoveResults = makeFMRList(results);
  85. }// RetreatChecker()
  86. /**
  87. * Create a RetreatChecker.
  88. * <p>
  89. * Useful for when the previous TurnState has not yet been inserted
  90. * into the World object.
  91. */
  92. public RetreatChecker(TurnState current, List<Result> previousTurnStateResults) {
  93. if (current == null || previousTurnStateResults == null) {
  94. throw new IllegalStateException("null arguments!");
  95. }
  96. this.position = current.getPosition();
  97. this.filteredMoveResults = makeFMRList(previousTurnStateResults);
  98. }// RetreatChecker()
  99. /**
  100. * Determines if the unit located in <code>from</code> can retreat to
  101. * the Location <code>to</code>
  102. *
  103. */
  104. public boolean isValid(Location from, Location to) {
  105. Location[] validLocs = getValidLocations(from);
  106. // debugging
  107. /*
  108. if(Log.isLogging())
  109. {
  110. Log.print(" valid retreat locations from ");
  111. Log.print(from);
  112. Log.print(": ");
  113. for(int i=0; i<validLocs.length; i++)
  114. {
  115. Log.print(validLocs[i]);
  116. Log.print(" ");
  117. }
  118. Log.print("\n");
  119. }
  120. */
  121. for (int i = 0; i < validLocs.length; i++) {
  122. if (validLocs[i].equals(to)) {
  123. return true;
  124. }
  125. }
  126. return false;
  127. }// isValid()
  128. /**
  129. * Gets all valid locations to which this unit may retreat.
  130. * <p>
  131. * Returns a zero-length array if there are no acceptable retreat locations.
  132. */
  133. public Location[] getValidLocations(Location from) {
  134. final List<Location> retreatLocations = new ArrayList<Location>(8);
  135. final List<Location> adjacent = from.getProvince().getAdjacentLocations(from.getCoast());
  136. for (final Location adjacentLocation : adjacent) {
  137. if (!position.hasUnit(adjacentLocation.getProvince())
  138. && !isDislodgersSpace(from, adjacentLocation)
  139. && !isContestedSpace(adjacentLocation)) {
  140. retreatLocations.add(adjacentLocation);
  141. }
  142. }
  143. return retreatLocations.toArray(new Location[retreatLocations.size()]);
  144. }// getValidLocations()
  145. /** Returns 'true' if at least one valid retreat exists for the dislodged unit in 'from' */
  146. public boolean hasRetreats(Location from) {
  147. final List<Location> adjacent = from.getProvince().getAdjacentLocations(from.getCoast());
  148. for (final Location adjacentLocation: adjacent) {
  149. if (!position.hasUnit(adjacentLocation.getProvince())
  150. && !isDislodgersSpace(from, adjacentLocation)
  151. && !isContestedSpace(adjacentLocation)) {
  152. return true;
  153. }
  154. }
  155. return false;
  156. }// hasNoRetreats()
  157. /**
  158. * Returns <code>true</code> if the space is unoccupied,
  159. * and their exists a <b>successful</b> move order from
  160. * that space which dislodged the unit.
  161. * <b>
  162. * @param dislodgedLoc The location with the dislodged unit
  163. * @param loc The unoccupied location which we are checking
  164. * @return <code>true</code> if Move from <code>loc</code>
  165. * dislodged <code>dislodgedLoc</code>
  166. */
  167. private boolean isDislodgersSpace(Location dislodgedLoc, Location loc) {
  168. Iterator<RCMoveResult> iter = filteredMoveResults.iterator();
  169. while (iter.hasNext()) {
  170. RCMoveResult rcmr = iter.next();
  171. // note: dislodgedLoc is the potential move destination
  172. if (rcmr.isDislodger(loc, dislodgedLoc)) {
  173. return true;
  174. }
  175. }
  176. return false;
  177. }// isDislodgersSpace()
  178. /**
  179. * Returns true if a standoff has occured;
  180. * A standoff exists if:
  181. * <ol>
  182. * <li>no unit in space (essential!)</li>
  183. * <li>2 or more <b>legal ("valid")</b> failed move orders exist
  184. * with dest of space</li>
  185. * </ol>
  186. */
  187. private boolean isContestedSpace(Location loc) {
  188. if (position.hasUnit(loc.getProvince())) {
  189. return false;
  190. }
  191. int moveCount = 0;
  192. Iterator<RCMoveResult> iter = filteredMoveResults.iterator();
  193. while (iter.hasNext()) {
  194. RCMoveResult rcmr = iter.next();
  195. if (rcmr.isPossibleStandoff(loc)) {
  196. moveCount++;
  197. }
  198. }
  199. return (moveCount >= 2);
  200. }// isContestedSpace()
  201. /**
  202. * Generate a List of (only) Move orders; when checking multiple
  203. * retreats this is a performance gain.
  204. * <p>
  205. * The filtered Move results consist of going through all OrderResults
  206. * looking for those involving Move orders. For each Move order, we
  207. * generate one RCMoveResult object, which holds the pertinent information
  208. * about that Move order.
  209. */
  210. private List<RCMoveResult> makeFMRList(final List<Result> turnStateResults) {
  211. final List<RCMoveResult> mrList = new ArrayList<RCMoveResult>(64);
  212. final java.util.Map<Province, RCMoveResult> map =
  213. new HashMap<Province, RCMoveResult>(119); // key: move source province; value: RCMoveResult
  214. for(final Result result: turnStateResults) {
  215. if (result instanceof OrderResult) {
  216. final OrderResult or = (OrderResult) result;
  217. final Orderable order = or.getOrder();
  218. if (order instanceof Move) {
  219. // see if we have an entry for this Move; if so,
  220. // set options; if not, create an entry.
  221. // This avoids duplicate entries per Move.
  222. //
  223. RCMoveResult rcmr = map.get(order.getSource().getProvince());
  224. if (rcmr == null) {
  225. rcmr = new RCMoveResult(or);
  226. map.put(order.getSource().getProvince(), rcmr);
  227. mrList.add(rcmr);
  228. } else {
  229. rcmr.setOptions(or);
  230. }
  231. }
  232. }
  233. }
  234. map.clear(); // no longer needed
  235. return mrList;
  236. }// makeFMRList()
  237. /**
  238. * RCMoveResult holds information about a Move order, as generated
  239. * from an OrderResult.
  240. *
  241. */
  242. private class RCMoveResult extends OrderResult {
  243. /**
  244. *
  245. */
  246. private static final long serialVersionUID = 1L;
  247. private final Move move;
  248. private boolean isSuccess = false;
  249. private boolean isByConvoy = false;
  250. private boolean isValid = true;
  251. /**
  252. * Create an RCMoveResult. Assumes that the passed
  253. * OrderResult refers to a Move order. Sets any options
  254. * for the OrderResult.
  255. */
  256. public RCMoveResult(OrderResult or) {
  257. move = (Move) or.getOrder();
  258. setOptions(or);
  259. }// RCMoveResult()
  260. /**
  261. * Given an OrderResult, checks to see if the success
  262. * or convoy flags can be set. If the passed OrderResult
  263. * does NOT refer to the same Move (via referential
  264. * equality), an exception is thrown.
  265. */
  266. public final void setOptions(OrderResult or) {
  267. if (or.getOrder() != move) {
  268. throw new IllegalArgumentException();
  269. }
  270. if (or.getResultType() == OrderResult.ResultType.CONVOY_PATH_TAKEN) {
  271. isByConvoy = true;
  272. } else if (or.getResultType() == OrderResult.ResultType.SUCCESS) {
  273. isSuccess = true;
  274. } else if (or.getResultType() == OrderResult.ResultType.VALIDATION_FAILURE) {
  275. isValid = false;
  276. }
  277. }// setOptions()
  278. /** Successful? */
  279. public boolean isSuccess() {
  280. return isSuccess;
  281. }
  282. /**
  283. * Is this move a potentially vying for a potential standoff?
  284. * <p>
  285. * This will return true iff:
  286. * (1) Destination *province* of Move matchest given location
  287. * (2) Move is NOT successful
  288. * (3) Move is NOT invalid (i.e., no VALIDATION_FAILURE result)
  289. */
  290. public boolean isPossibleStandoff(Location loc) {
  291. return (isValid
  292. && !isSuccess
  293. && move.getDest().isProvinceEqual(loc));
  294. }// isPossibleStandoff()
  295. /**
  296. * Is this move a potentially dislodging move?
  297. * This will return true iff:
  298. * (1) src and dest match;
  299. * (2) successful
  300. * (3) NOT convoyed (DATC 16-dec-03 4.A.5)
  301. */
  302. public boolean isDislodger(Location src, Location dest) {
  303. return (isSuccess
  304. && !isByConvoy
  305. && move.getSource().isProvinceEqual(src)
  306. && move.getDest().isProvinceEqual(dest));
  307. }// isDislodger()
  308. }// inner class RCMoveResult
  309. }// class RetreatChecker