Package com.barrybecker4.game.twoplayer.checkers

Source Code of com.barrybecker4.game.twoplayer.checkers.MoveGenerator

/** Copyright by Barry G. Becker, 2000-2011. Licensed under MIT License: http://www.opensource.org/licenses/MIT  */
package com.barrybecker4.game.twoplayer.checkers;

import com.barrybecker4.common.geometry.ByteLocation;
import com.barrybecker4.game.common.GameContext;
import com.barrybecker4.game.common.MoveList;
import com.barrybecker4.game.common.board.BoardPosition;
import com.barrybecker4.game.common.board.CaptureList;
import com.barrybecker4.game.twoplayer.common.TwoPlayerMove;
import com.barrybecker4.optimization.parameter.ParameterArray;

import java.util.LinkedList;
import java.util.List;

/**
* Creates a list of possible next moves.
*
* @author Barry Becker
*/
public class MoveGenerator  {

    private CheckersSearchable searchable_;
    private CheckersBoard board_;


    private ParameterArray weights_;

    /**
     * Construct the Checkers game controller.
     * @param searchable thing to search
     * @param weights to use.
     */
    public MoveGenerator(CheckersSearchable searchable, ParameterArray weights) {
        searchable_ = searchable;
        board_ = (CheckersBoard) searchable_.getBoard();
        weights_ = weights;
    }

    public MoveList generateMoves(TwoPlayerMove lastMove) {

        int j, row,col;
        boolean player1 = (lastMove == null) || !(lastMove.isPlayer1());
        MoveList moveList = new MoveList();

        // scan through the board positions. For each each piece of the current player's,
        // add all the moves that it can make.
        for ( row = 1; row <= CheckersBoard.SIZE; row++ ) {
            int odd = row % 2;
            for ( j = 1; j <= CheckersBoard.SIZE/2; j++ ) {
                col = 2 * j - odd;
                BoardPosition p = board_.getPosition( row, col );
                if ( p.isOccupied() && p.getPiece().isOwnedByPlayer1() == player1 ) {
                    addMoves(p, lastMove, moveList);
                }
            }
        }
        return moveList;
    }

    /**
     * Find all the moves a piece p can make and insert them into moveList.
     *
     * @param p the piece to check.
     * @return the number of moves added.
     */
    public int addMoves( BoardPosition p, TwoPlayerMove lastMove, MoveList moveList) {

        int direction = -1;
        if ( p.getPiece().isOwnedByPlayer1() )
            direction = 1;

        int numMovesAdded = 0;
        int initialNumMoves = moveList.size();
        // check left and right forward diagonals
        numMovesAdded += addMovesForDirection( p, direction, -1, lastMove, moveList);
        numMovesAdded += addMovesForDirection( p, direction, 1, lastMove, moveList);

        // if its a KING we need to check the other direction too
        CheckersPiece piece = (CheckersPiece) p.getPiece();
        if ( piece.isKing() ) {
            numMovesAdded += addMovesForKing(p, lastMove, direction, numMovesAdded, initialNumMoves, moveList);
        }
        return numMovesAdded;
    }


    /**
     * Find all the moves piece p can make in a given diagonal direction.
     *
     * @param pos the piece to check
     * @return the number of moves added
     */
    private int addMovesForDirection( BoardPosition pos,
                                      int rowInc, int colInc, TwoPlayerMove lastMove, MoveList moveList) {
        BoardPosition next = board_.getPosition( pos.getRow() + rowInc, pos.getCol() + colInc );
        if (next!=null)
        {
            if (next.isUnoccupied()) {
                addSimpleMove(pos, rowInc, colInc, lastMove, moveList);
                // only one move added
                return 1;
            }
            // if just a simple move was not possible, we check for jump(s)
            BoardPosition beyondNext = board_.getPosition( pos.getRow() + 2 * rowInc, pos.getCol() + 2 * colInc );
            if (next.isOccupied() &&
                (next.getPiece().isOwnedByPlayer1() != pos.getPiece().isOwnedByPlayer1()) &&
                 beyondNext!=null && beyondNext.isUnoccupied()) {
                return addJumpMoves(pos, rowInc, lastMove, next, beyondNext, moveList);
            }
        }
        return 0; // no moves added
    }


    private void addSimpleMove(BoardPosition pos, int rowInc, int colInc, TwoPlayerMove lastMove, MoveList moveList) {
        CheckersMove m;
        int val = 0;
        if ( lastMove != null ) {
            // then not the first move of the game
            val = lastMove.getValue();
        }
        m = CheckersMove.createMove( pos.getLocation(), pos.getLocation().incrementOnCopy(rowInc, colInc),
                                     null, val, pos.getPiece().copy() );

        // no need to evaluate it since there were no captures
        moveList.add( m );
    }


    private int addJumpMoves(BoardPosition pos, int rowInc, TwoPlayerMove lastMove,
                             BoardPosition next, BoardPosition beyondNext, MoveList moveList) {
       CheckersMove m;
       CaptureList capture = new CaptureList();
       capture.add( next.copy() );
       m = CheckersMove.createMove( pos.getLocation(), beyondNext.getLocation(),
                                    capture, lastMove.getValue(), pos.getPiece().copy() );

       List<CheckersMove> jumps = findJumpMoves( beyondNext, rowInc, m, weights_ );
       moveList.addAll( jumps );

       return jumps.size();
   }

    /**
     * Check to see if this jump requires additional jumps
     * If it does, we create a new move, because there could potentially be 2 jumps possible
     * from the last position.
     * @return number of additional jump moves added.
     */
    private int checkJumpMove( BoardPosition current,
                               CheckersMove m, int rowInc, int colInc,
                               List<CheckersMove> jumpMoves, ParameterArray weights ) {
        BoardPosition next = board_.getPosition( current.getRow() + rowInc, current.getCol() + colInc );
        BoardPosition beyondNext = board_.getPosition( current.getRow() + 2 * rowInc, current.getCol() + 2 * colInc );
        // if the adjacent square is an opponent's piece, and the space beyond it
        // is empty, and we have not already capture this piece, then take another jump.
        boolean opponentAdjacent =
                next!=null && next.isOccupied() && (next.getPiece().isOwnedByPlayer1() != m.isPlayer1());
        if ( opponentAdjacent
              && beyondNext!=null && beyondNext.isUnoccupied()
              && (m.captureList != null) && (!m.captureList.alreadyCaptured( next )) ) {
            // then there is another jump. We must take it.
            CheckersMove mm = m.copy()// base it on the original jump
            mm.setToLocation(new ByteLocation(beyondNext.getLocation().getRow(), beyondNext.getLocation().getCol()));
            mm.captureList.add( next.copy() );
            // next.setPiece(null); ?

            boolean justKinged = false;   // ?? may be superfluous
            GameContext.log( 2, "calling findJumpMoves on " +
                    beyondNext + " rowinc=" + rowInc + "magnitude of capturelist=" + mm.captureList.size() );
            if ( (mm.getPiece().getType() == CheckersPiece.REGULAR_PIECE) &&
                    ((mm.isPlayer1() && mm.getToRow() == CheckersBoard.SIZE)
                    || (!mm.isPlayer1() && mm.getToRow() == 1)) ) {
                mm.kinged = true;
                justKinged = true;
                mm.setPiece(new CheckersPiece(mm.isPlayer1(), CheckersPiece.KING));
                GameContext.log( 2, "KINGED: " + mm );
            }
            else  {
                mm.kinged = false;
            }

            List<CheckersMove> list;
            // we cannot make more jumps if we just got kinged.
            if (!justKinged) {    // may be superfluous
                list = findJumpMoves( beyondNext, rowInc, mm, weights );
                assert ( list!=null );
                jumpMoves.addAll( list );
                return list.size();
            }
        }
        return 0// no additional move added
    }

    /**
     * Find all the possible jumps using the specified piece.
     * Remember that in checkers, all possible jumps in a row must be taken.
     * For example, you can not do just a double jump if a triple jump is possible.
     * This cannot return null since there is at least the first jump.
     * When jumping we remove the piece and add it to the captureList so they
     * won't be taken twice in the same move. At the end we return the captured
     * pieces to the board so the state is not change.
     * @return list of jump moves.
     */
    private List<CheckersMove> findJumpMoves( BoardPosition current,
                                      int rowInc, CheckersMove m,
                                      ParameterArray weights )  {
        List<CheckersMove> jumpMoves = new LinkedList<CheckersMove>();
        // if there are jumps beyond this we have to make them.
        // We have at least the current jump m.

        // once moreJumps becomes true we must add additional moves
        boolean moreJumps = false;

        // first check the forward moves
        if ( checkJumpMove( current, m, rowInc, -1, jumpMoves, weights ) > 0 )
            moreJumps = true;
        if ( checkJumpMove( current, m, rowInc, 1, jumpMoves, weights ) > 0 )
            moreJumps = true;

        // note you cannot continue making jumps from the point at which you are kinged.
        if ( m.getPiece().getType() == CheckersPiece.KING && !m.kinged ) {
            if ( checkJumpMove( current, m, -rowInc, -1, jumpMoves, weights ) > 0 )
                moreJumps = true;
            if ( checkJumpMove( current, m, -rowInc, 1, jumpMoves, weights ) > 0 )
                moreJumps = true;
        }

        if ( !moreJumps ) { // base case of recursion
            // we can finally add the move after we evaluate its worth
            board_.makeMove( m );
            m.setValue(searchable_.worth( m, weights));
            board_.undoMove();

            jumpMoves.add( m );

            return jumpMoves;
        }
        return jumpMoves;
    }

    /**
     * @return number of king moves added.
     */
    private int addMovesForKing(BoardPosition p, TwoPlayerMove lastMove,
                                int direction, int numMovesAdded, int initialNumMoves, MoveList moveList) {
        int numKingMoves = 0;
        numKingMoves += addMovesForDirection( p, -direction, -1, lastMove, moveList);
        numKingMoves += addMovesForDirection( p, -direction, 1, lastMove, moveList);

        // we also need to verify that we are not cycling over previous moves (not allowed).
        // check moves in the list against the move 4 moves back if the same, we must remove it
        // we can skip if there were captures, since captures cannot be undone.
        int numMoves = searchable_.getNumMoves();

        if ( numMoves - 4 > 1 ) {
            CheckersMove moveToCheck = (CheckersMove) searchable_.getMoveList().get( numMoves - 4 );
            if ( moveToCheck.captureList == null ) {
                int i = 0;

                while ( i < numKingMoves ) {
                    CheckersMove m = (CheckersMove)moveList.get( initialNumMoves + numMovesAdded + i );
                    GameContext.log( 1, "lastMove="+ lastMove);
                    assert ( m.isPlayer1() == moveToCheck.isPlayer1()):
                            "player ownership not equal comparing \n"+m+" with \n"+moveToCheck;
                    if ( m.captureList == null &&  m.equals(moveToCheck)) {
                        GameContext.log(0, "found a cycle. new score = " + m.getValue() +
                                " old score=" + moveToCheck.getValue() + " remove move= " + m );
                        moveList.remove( m );
                        numKingMoves--;
                        break;
                    }
                    else {
                        i++;
                    }
                }
            }
        }
        return numKingMoves;
    }
}
TOP

Related Classes of com.barrybecker4.game.twoplayer.checkers.MoveGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.