// Game of Fanorona // David Eppstein, UC Irvine, 11 Jun 1997 // // Move generation // // This is implemented by a collection of Move Experts, // each of which knows how to construct a bitboard of starting positions // for moves of a certain type, and how to perform those moves given // a bit representing the starting position. import java.util.*; public class MoveGenerator // implements Enumeration (not really) { // The board we're generating moves from and various derived quantities Board board; // board we are moving from long storedFrom; // positions we can move from long storedTo; // positions we can move to long movesV,movesH,movesS,movesB; // positions we can move to ignoring capturability // what kind of move we're making and which pieces can move that way int moveSetIndex; // where we are in the list long set; // set of positions this moveSet is working on // what kind of move is represented by the bits in set int captureType; static final int CAPTURE_FORWARD = 0; static final int CAPTURE_BACKWARD = 1; static final int NO_CAPTURE = 2; static final int PASS = 3; static final int NO_MORE_MOVES = 4; boolean madeCapture; int shift; // which direction pieces are moving in current move set // The main routine to find sets of moves // nextElement then pulls off the moves from these sets one bit at a time. // // This is really not a subroutine but a coroutine: each time we call it we want // to continue at the statement immediately after the point at which we returned // from the previous call. Since Java doesn't have coroutines built-in, we use // moveSetIndex as a program counter, with a big switch statement marking all the // different possible entry points, but (unlike the usual switch) falling through // from case to case rather than having any break statements. // private final void findNextSet() { long from = storedFrom; long to = storedTo; long target = board.opponentPieces; switch (++moveSetIndex) { // CASES 0-7: CAPTURES case 0: // CAPTURE FORWARD VERTICALLY OR BACKWARD -VERTICALLY captureType = CAPTURE_FORWARD; movesV = (from & (to >>> Bits.SHIFT_VERTICAL)) | (to & (from >>> Bits.SHIFT_VERTICAL)); if ((set = (movesV & (target >>> 2*Bits.SHIFT_VERTICAL))) != 0) { shift = Bits.SHIFT_VERTICAL; madeCapture = true; return; } ++moveSetIndex; // fall into... case 1: // CAPTURE FORWARD HORIZONTALLY OR BACKWARD -HORIZONTALLY movesH = (from & (to >>> Bits.SHIFT_HORIZONTAL)) | (to & (from >>> Bits.SHIFT_HORIZONTAL)); if ((set = (movesH & (target >>> 2*Bits.SHIFT_HORIZONTAL))) != 0) { shift = Bits.SHIFT_HORIZONTAL; madeCapture = true; return; } ++moveSetIndex; // fall into... case 2: // CAPTURE FORWARD SLANTLY OR BACKWARD -SLANTLY storedFrom = (from &= Bits.DIAGONAL); movesS = (from & (to >>> Bits.SHIFT_SLANT)) | (to & (from >>> Bits.SHIFT_SLANT)); if ((set = (movesS & (target >>> 2*Bits.SHIFT_SLANT))) != 0) { shift = Bits.SHIFT_SLANT; madeCapture = true; return; } ++moveSetIndex; // fall into... case 3: // CAPTURE FORWARD BACKSLANTLY OR BACKWARD -BACKSLANTLY movesB = (from & (to >>> Bits.SHIFT_BACKSLANT)) | (to & (from >>> Bits.SHIFT_BACKSLANT)); if ((set = (movesB & (target >>> 2*Bits.SHIFT_BACKSLANT))) != 0) { shift = Bits.SHIFT_BACKSLANT; madeCapture = true; return; } ++moveSetIndex; // fall into... case 4: // CAPTURE FORWARD -VERTICALLY OR BACKWARD VERTICALLY captureType = CAPTURE_BACKWARD; if ((set = (movesV & (target << Bits.SHIFT_VERTICAL))) != 0) { shift = Bits.SHIFT_VERTICAL; madeCapture = true; return; } ++moveSetIndex; // fall into... case 5: // CAPTURE FORWARD -HORIZONTALLY OR BACKWARD HORIZONTALLY if ((set = (movesH & (target << Bits.SHIFT_HORIZONTAL))) != 0) { shift = Bits.SHIFT_HORIZONTAL; madeCapture = true; return; } ++moveSetIndex; // fall into... case 6: // CAPTURE FORWARD -SLANTLY OR BACKWARD SLANTLY if ((set = (movesS & (target << Bits.SHIFT_SLANT))) != 0) { shift = Bits.SHIFT_SLANT; madeCapture = true; return; } ++moveSetIndex; // fall into... case 7: // CAPTURE FORWARD -BACKSLANTLY OR BACKWARD BACKSLANTLY if ((set = (movesB & (target << Bits.SHIFT_BACKSLANT))) != 0) { shift = Bits.SHIFT_BACKSLANT; madeCapture = true; return; } ++moveSetIndex; // fall into... // CASES 8-11: SHUFFLES case 8: // VERTICAL SHUFFLE OR PASS if (board.midCapture()) { // illegal to shuffle? captureType = PASS; moveSetIndex = 11; // set up so next call to findset runs into case 12 set = 1; // return a set with one move in it return; } else if (madeCapture) { // capture was forced? moveSetIndex = 11; // move to case 12, end of moves captureType = NO_MORE_MOVES; return; } captureType = NO_CAPTURE; if ((set = movesV) != 0) { shift = Bits.SHIFT_VERTICAL; return; } ++moveSetIndex; // fall into... case 9: // HORIZONTAL SHUFFLE if ((set = movesH) != 0) { shift = Bits.SHIFT_HORIZONTAL; return; } ++moveSetIndex; // fall into... case 10: // SLANT SHUFFLE if ((set = movesS) != 0) { shift = Bits.SHIFT_SLANT; return; } ++moveSetIndex; // fall into... case 11: // BACKSLANT SHUFFLE if ((set = movesB) != 0) { shift = Bits.SHIFT_BACKSLANT; return; } ++moveSetIndex; // fall into... // CASE 12: RAN OUT OF SHUFFLES case 12: moveSetIndex--; // stay in this case and always return zero captureType = NO_MORE_MOVES; return; default: throw new IllegalArgumentException(); } } // is there a capturing move available? // should be called on a newly created MoveGenerator public final boolean hasCapture() { if (set == 0) findNextSet(); return (captureType == CAPTURE_FORWARD || captureType == CAPTURE_BACKWARD); } // initialize and set up first position public MoveGenerator(Board b) { reset(b); } public void reset(Board b) { board = b; moveSetIndex = -1; set = 0; madeCapture = false; // Find positions we can move from and to. // At start of move they are just occupied and empty positions, but in midCapture // we restrict from to the piece just moved and to to places it can legally go. long myPieces = b.myPieces; // Narrow down target positions for midcapture moves to avoid our previous positions if (b.midCapture()) { long move = myPieces ^ b.previousPosition.myPieces; storedFrom = myPieces & move; // Only allow same piece to move if ((move & (move << Bits.SHIFT_VERTICAL)) != 0) move = (move << Bits.SHIFT_VERTICAL) | (move >>> Bits.SHIFT_VERTICAL); else if ((move & (move << Bits.SHIFT_HORIZONTAL)) != 0) move = (move << Bits.SHIFT_HORIZONTAL) | (move >>> Bits.SHIFT_HORIZONTAL); else if ((move & (move << Bits.SHIFT_SLANT)) != 0) move = (move << Bits.SHIFT_SLANT) | (move >>> Bits.SHIFT_SLANT); else // if ((move & (move << Bits.SHIFT_BACKSLANT)) != 0) move = (move << Bits.SHIFT_BACKSLANT) | (move >>> Bits.SHIFT_BACKSLANT); storedTo = Bits.ON_BOARD &~ (move | myPieces | b.opponentPieces | b.alreadyVisited); return; } else { storedFrom = myPieces; storedTo = Bits.ON_BOARD &~ (myPieces | b.opponentPieces); return; } } // are there any ungenerated moves? public final boolean hasMoreElements() { if (set == 0) findNextSet(); return (captureType != NO_MORE_MOVES); } // find next move in sequence by pulling bits out of set public final long nextElement() { if (set == 0) findNextSet(); // make sure we have a move to generate long bit = set; bit &= -bit; // bit = Bits.lastBit(set) set ^= bit; switch (captureType) { case CAPTURE_FORWARD: long retval = bit | (bit << shift); bit <<= 2*shift; while ((bit & board.opponentPieces) != 0) { retval |= bit; bit <<= shift; } return retval; case CAPTURE_BACKWARD: retval = bit | (bit << shift); bit >>>= shift; while ((bit & board.opponentPieces) != 0) { retval |= bit; bit >>>= shift; } return retval; case NO_CAPTURE: return bit | (bit << shift); case PASS: return 0; case NO_MORE_MOVES: } return -1L; } // find an arbitrary move in a position static int arbitraryMoveIndex = 0; static public final Board arbitraryMove(Board board) { int i = arbitraryMoveIndex; // remember which move we want arbitraryMoveIndex++; // and next time take the following move instead MoveGenerator moveGenerator = new MoveGenerator(board); while (moveGenerator.hasMoreElements()) { // loop through moves taking i'th long move = moveGenerator.nextElement(); if (--i < 0) return new Board(board,move); } arbitraryMoveIndex = 1; // ran out, had to return 0'th, return 1'st next time return new Board(board, new MoveGenerator(board).nextElement()); } }