package com.editev.chess; import com.editev.util.Filter; import com.editev.util.Function; import com.editev.util.Enum; import com.editev.util.ExceptionWrapper; import com.editev.util.Lists; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.NoSuchElementException; /** A GameMoves is an EnumeratedGame with a list of legal Moves for that Game. * * @see See the source here. */ public class GameMoves extends Game { /** Estimate of max legal moves per position: max ever actually recorded in a random game is 71. */ public static final short MAX_MOVES = 80; /** Store all the legal next moves from this board position. */ final Moves allMoves = new Moves( MAX_MOVES ); /** Have we enumerated the next moves for this position yet? */ protected boolean movesEnumerated = false; /** Have we enumerated ALL the next moves for this position yet? */ protected boolean movesCompleted = false; /** Count all the the possible legal moves -- don't call this if you want to avoid enumerating * all possible legal moves for efficiency reasons, go right to getMove(). * * @return count of the number of legal next Moves for this Game. */ public short getMoveCount() { computeMoves(); return (short) allMoves.getLength(); } /** @return the legal move at this location -- only computes legal moves up to this point for efficiency. * * @param move the index of the legal move we're locating. * * @throw IndexOutOfBoundsException if there is no legal move with this index. */ public Move getMove( short move ) { computeMoves( move ); // get all moves to this point. if (move >= allMoves.getLength() || move < 0) { // no such move. throw new IndexOutOfBoundsException( "accessed move indexed "+move+ " out of "+allMoves.getLength() ); } return allMoves.getAt( move ); // get the move from the List. } /** An enumeration of all the legal moves, might be null or partially consumed. */ private Enumeration moveEnumeration; /** Retrieves or creates an enumeration of all the legal moves. * * @return an Enumeration of all the legal moves. */ protected Enumeration getMoveEnumeration() { if (!movesEnumerated) { moveEnumeration = enumerateAllLegalMoves(); movesEnumerated = true; // we got the move enumeration. movesCompleted = false; // we haven't run out of moves (redundant). allMoves.clear(); } return moveEnumeration; // return the enumeration if any. } /** Computes all next legal moves up to a certain move index. * * @param moveNeeded the index of the last legal move we need (or Short.MAX_SHORT for all!) */ protected void computeMoves( short moveNeeded ) { if (movesCompleted || (movesEnumerated && (moveNeeded < allMoves.getLength())) ) return; // already reached that point. Enumeration moves = getMoveEnumeration(); // get all the (remaining) moves. Move move; while ( moves.hasMoreElements() // while there are more elements to retrieve && moveNeeded >= allMoves.getLength() // and we haven't found the desired move ) { allMoves.append( (Move) moves.nextElement() ); } if (!moves.hasMoreElements()) { movesCompleted = true; // no more moves in the enumeration. moveEnumeration = null; // send enumeration to GC } } /** Compute all the legal moves. */ protected void computeMoves( ) { computeMoves( Short.MAX_VALUE ); } /** Apply a specific move to this Game. * Apply the move to the parent class -- but then invalidate the stored list of legal moves. * * @return the index of the captured piece, if any (special case for ep). * @see com.editev.chess.Game. */ public byte applyMove( Move move ) { byte captured = super.applyMove( move ); // apply the move. movesCompleted = false; // now invalidate the cached enumeration of moves. movesEnumerated = false; allMoves.clear(); moveEnumeration = null; // redundant. return captured; } public Object clone() { return cloneGameMoves(); } public GameMoves cloneGameMoves() { GameMoves game = (GameMoves) super.clone(); return game; } }