Exemple #1
0
 // Constructor
 public jcPlayerHuman(int which)
 {
     this.SetSide(which);
     linebuf = new char[10];
     Pseudos = new jcMoveListGenerator();
     Successor = new jcBoard();
 }
Exemple #2
0
        // public jcMove Query
        // Querying the table for a ready-made move to play.  Return null if there
        // is none
        public jcMove Query(jcBoard theBoard)
        {
            // First, look for a match in the table
            int key = Math.Abs(theBoard.HashKey() % TABLE_SIZE);
            int hashLock = theBoard.HashLock();

            // If the hash lock doesn't match the one for our position, get out
            if (Table[key].theLock != hashLock)
                return null;

            // If there is an entry for this board in the table, verify that it
            // contains a move for the current side
            if (theBoard.GetCurrentPlayer() == jcPlayer.SIDE_BLACK)
            {
                if (Table[key].BlackMove.MoveType != jcOpeningBookEntry.NO_MOVE)
                    return Table[key].BlackMove;
            }
            else
            {
                if (Table[key].WhiteMove.MoveType != jcOpeningBookEntry.NO_MOVE)
                    return Table[key].WhiteMove;
            }

            // If we haven't found anything useful, quit
            return null;
        }
        /****************************************************************************
         * PUBLIC METHODS
         ***************************************************************************/
        // Move selection: An iterative-deepening paradigm calling MTD(f) repeatedly
        public override jcMove PickBestMove(jcBoard theBoard)
        {
            // First things first: look in the Opening Book, and if it contains a
            // move for this position, don't search anything
            MoveCounter++;
            jcMove Mov = null;
            Mov = Openings.Query(theBoard);
            if(Mov != null)
                return Mov;

            // Store the identity of the moving side, so that we can tell Evaluator
            // from whose perspective we need to evaluate positions
            FromWhosePerspective = theBoard.GetCurrentPlayer();

            // Should we erase the history table?
            if((Rnd.Next() % 6) == 2)
                HistoryTable.Forget();

            // Begin search.  The search's maximum depth is determined on the fly,
            // according to how much effort has been spent; if it's possible to search
            // to depth 8 in 5 seconds, then by all means, do it!
            int bestGuess = 0;
            int iterdepth = 1;

            while(true) {
                // Searching to depth 1 is not very effective, so we begin at 2
                iterdepth++;

                // Compute efficiency statistics
                NumRegularNodes = 0;
                NumQuiescenceNodes = 0;
                NumRegularTTHits = 0;
                NumQuiescenceTTHits = 0;
                NumRegularCutoffs = 0;
                NumQuiescenceCutoffs = 0;

                // Look for a move at the current depth
                Mov = MTDF(theBoard, bestGuess, iterdepth);
                bestGuess = Mov.MoveEvaluation;

                // Feedback!
                Console.Write("Iteration of depth " + iterdepth + "; best move = ");
                Mov.Print();
                Console.Write("  --> Transposition Table hits for regular nodes: ");
                Console.WriteLine(NumRegularTTHits + " of " + NumRegularNodes);
                Console.Write("  --> Transposition Table hits for quiescence nodes: ");
                Console.WriteLine(NumQuiescenceTTHits + " of " + NumQuiescenceNodes);
                Console.WriteLine("  --> Number of cutoffs for regular nodes: " + NumRegularCutoffs);
                Console.WriteLine("  --> Number of cutoffs in quiescence search: " + NumQuiescenceCutoffs);

                // Get out if we have searched deep enough
                if((NumRegularNodes + NumQuiescenceNodes) > MaxSearchSize)
                    break;
                if(iterdepth >= 15)
                    break;
            }

            return Mov;
        }
Exemple #4
0
        // Loading the table from a file
        public bool Load(string fileName)
        {
            // Open the file as a Java tokenizer
            using (var sr = new StreamReader(fileName))
            {
                // Create a game board on which to "play" the opening sequences stored in
                // the book, so that we know which position to associate with which move
                jcBoard board = new jcBoard();
                jcMove mov = new jcMove();
                jcMoveListGenerator successors = new jcMoveListGenerator();

                // How many lines of play do we have in the book?
                var lines = File.ReadAllLines(fileName);
                int numLines = lines.Length;

                for (int wak = 0; wak < numLines; wak++)
                {
                    // Begin the line of play with a clean board
                    board.StartingBoard();

                    // Load the continuation
                    var lineNums = lines[wak].Split(' ');
                    for (int i = 0; i < lineNums.Length; i++)
                    {
                        if (lineNums[i] == "END")
                            break;
                        successors.ComputeLegalMoves(board);

                        var source = Convert.ToInt32(lineNums[i]);
                        var destination = Convert.ToInt32(lineNums[i + 1]);
                        i++;

                        mov = successors.FindMoveForSquares(source, destination);
                        StoreMove(board, mov);
                        board.ApplyMove(mov);

                    }
                }

            }
            return true;
        }
Exemple #5
0
        private bool StoreMove(jcBoard theBoard, jcMove theMove)
        {
            // Where should we store this data?
            int key = Math.Abs(theBoard.HashKey() % TABLE_SIZE);
            int hashLock = theBoard.HashLock();

            // Is there already an entry for a different board position where we
            // want to put this?  If so, mark it deleted
            if (Table[key].theLock != hashLock)
            {
                Table[key].BlackMove.MoveType = jcOpeningBookEntry.NO_MOVE;
                Table[key].WhiteMove.MoveType = jcOpeningBookEntry.NO_MOVE;
            }

            // And store the new move
            Table[key].theLock = hashLock;
            if (theBoard.GetCurrentPlayer() == jcPlayer.SIDE_BLACK)
            {
                Table[key].BlackMove.Copy(theMove);
            }
            else
            {
                Table[key].WhiteMove.Copy(theMove);
            }

            return true;
        }
Exemple #6
0
        // public jcMove GetMove( theBoard )
        // Getting a move from the human player.  Sorry, but this is very, very
        // primitive: you need to enter square numbers instead of piece ID's, and
        // both square numbers must be entered with two digits.  Ex.: 04 00
        public override jcMove GetMove(jcBoard theBoard)
        {
            // Read the move from the command line
            bool ok = false;
            jcMove Mov = new jcMove();
            do {
                Console.WriteLine("Your move, " + PlayerStrings[this.GetSide()] + "?");

                // Get data from the command line
                string line = null;
                do {
                    try {
                        line = Console.ReadLine();
                    } catch(Exception) { }
                } while(line.Length < 3);

                if(line.ToUpper().Equals("RESIG")) {
                    Mov.MoveType = jcMove.MOVE_RESIGN;
                    return (Mov);
                }

                // Extract the source and destination squares from the line buffer
                Mov.SourceSquare = Convert.ToInt32(line.Substring(0, 2));
                Mov.DestinationSquare = Convert.ToInt32(line.Substring(3, 2));
                if((Mov.SourceSquare < 0) || (Mov.SourceSquare > 63)) {
                    Console.WriteLine("Sorry, illegal source square " + Mov.SourceSquare);
                    continue;
                }
                if((Mov.DestinationSquare < 0) || (Mov.DestinationSquare > 63)) {
                    Console.WriteLine("Sorry, illegal destination square " + Mov.DestinationSquare);
                    continue;
                }

                // Time to try to figure out what the move means!
                if(theBoard.GetCurrentPlayer() == jcPlayer.SIDE_WHITE) {
                    // Is there a piece (of the moving player) on SourceSquare?
                    // If not, abort
                    Mov.MovingPiece = theBoard.FindWhitePiece(Mov.SourceSquare);
                    if(Mov.MovingPiece == jcBoard.EMPTY_SQUARE) {
                        Console.WriteLine("Sorry, You don't have a piece at square " + Mov.SourceSquare);
                        continue;
                    }

                    // Three cases: there is a piece on the destination square (a capture),
                    // the destination square allows an en passant capture, or it is a
                    // simple non-capture move.  If the destination contains a piece of the
                    // moving side, abort
                    if(theBoard.FindWhitePiece(Mov.DestinationSquare) != jcBoard.EMPTY_SQUARE) {
                        Console.WriteLine("Sorry, can't capture your own piece!");
                        continue;
                    }
                    Mov.CapturedPiece = theBoard.FindBlackPiece(Mov.DestinationSquare);
                    if(Mov.CapturedPiece != jcBoard.EMPTY_SQUARE)
                        Mov.MoveType = jcMove.MOVE_CAPTURE_ORDINARY;
                    else if((theBoard.GetEnPassantPawn() == (1 << Mov.DestinationSquare)) &&
                              (Mov.MovingPiece == jcBoard.WHITE_PAWN)) {
                        Mov.CapturedPiece = jcBoard.BLACK_PAWN;
                        Mov.MoveType = jcMove.MOVE_CAPTURE_EN_PASSANT;
                    }

                    // If the move isn't a capture, it may be a castling attempt
                    else if((Mov.MovingPiece == jcBoard.WHITE_KING) &&
                              ((Mov.SourceSquare - Mov.DestinationSquare) == 2))
                        Mov.MoveType = jcMove.MOVE_CASTLING_KINGSIDE;
                    else if((Mov.MovingPiece == jcBoard.WHITE_KING) &&
                              ((Mov.SourceSquare - Mov.DestinationSquare) == -2))
                        Mov.MoveType = jcMove.MOVE_CASTLING_QUEENSIDE;
                    else
                        Mov.MoveType = jcMove.MOVE_NORMAL;
                } else {
                    Mov.MovingPiece = theBoard.FindBlackPiece(Mov.SourceSquare);
                    if(Mov.MovingPiece == jcBoard.EMPTY_SQUARE) {
                        Console.WriteLine("Sorry, you don't have a piece in square " + Mov.SourceSquare);
                        continue;
                    }

                    if(theBoard.FindBlackPiece(Mov.DestinationSquare) != jcBoard.EMPTY_SQUARE) {
                        Console.WriteLine("Sorry, you can't capture your own piece in square " + Mov.DestinationSquare);
                        continue;
                    }
                    Mov.CapturedPiece = theBoard.FindWhitePiece(Mov.DestinationSquare);
                    if(Mov.CapturedPiece != jcBoard.EMPTY_SQUARE)
                        Mov.MoveType = jcMove.MOVE_CAPTURE_ORDINARY;
                    else if((theBoard.GetEnPassantPawn() == (1 << Mov.DestinationSquare)) &&
                              (Mov.MovingPiece == jcBoard.BLACK_PAWN)) {
                        Mov.CapturedPiece = jcBoard.WHITE_PAWN;
                        Mov.MoveType = jcMove.MOVE_CAPTURE_EN_PASSANT;
                    } else if((Mov.MovingPiece == jcBoard.BLACK_KING) &&
                                ((Mov.SourceSquare - Mov.DestinationSquare) == 2))
                        Mov.MoveType = jcMove.MOVE_CASTLING_KINGSIDE;
                    else if((Mov.MovingPiece == jcBoard.BLACK_KING) &&
                              ((Mov.SourceSquare - Mov.DestinationSquare) == -2))
                        Mov.MoveType = jcMove.MOVE_CASTLING_QUEENSIDE;
                    else
                        Mov.MoveType = jcMove.MOVE_NORMAL;
                }

                // Now, if the move results in a pawn promotion, we must ask the user
                // for the type of promotion!
                if(((Mov.MovingPiece == jcBoard.WHITE_PAWN) && (Mov.DestinationSquare < 8)) ||
                     ((Mov.MovingPiece == jcBoard.BLACK_PAWN) && (Mov.DestinationSquare > 55))) {
                    int car = -1;
                    Console.WriteLine("Promote the pawn to [K]night, [R]ook, [B]ishop, [Q]ueen?");
                    do {
                        try { car = Console.Read(); } catch(Exception) { }
                    } while((car != 'K') && (car != 'k') && (car != 'b') && (car != 'B')
                           && (car != 'R') && (car != 'r') && (car != 'Q') && (car != 'q'));
                    if((car == 'K') || (car == 'k'))
                        Mov.MoveType += jcMove.MOVE_PROMOTION_KNIGHT;
                    else if((car == 'B') || (car == 'b'))
                        Mov.MoveType += jcMove.MOVE_PROMOTION_BISHOP;
                    else if((car == 'R') || (car == 'r'))
                        Mov.MoveType += jcMove.MOVE_PROMOTION_ROOK;
                    else
                        Mov.MoveType += jcMove.MOVE_PROMOTION_QUEEN;
                }

                // OK, now let's see if the move is actually legal!  First step: a check
                // for pseudo-legality, i.e., is it a valid successor to the current
                // board?
                Pseudos.ComputeLegalMoves(theBoard);
                if(!Pseudos.Find(Mov)) {
                    Console.Write("Sorry, this move is not in the pseudo-legal list: ");
                    Mov.Print();
                    Pseudos.Print();
                    continue;
                }

                // If pseudo-legal, then verify whether it leaves the king in check
                Successor.Clone(theBoard);
                Successor.ApplyMove(Mov);
                if(!Pseudos.ComputeLegalMoves(Successor)) {
                    Console.Write("Sorry, this move leaves your king in check: ");
                    Mov.Print();
                    continue;
                }

                // If we have made it here, we have a valid move to play!
                Console.WriteLine("Move is accepted...");
                ok = true;

            } while(!ok);

            return (Mov);
        }
Exemple #7
0
 // public bool Clone
 // Make a deep copy of a jcBoard object; assumes that memory has already
 // been allocated for the new object, which is always true since we
 // "allocate" jcBoards from a permanent array
 public bool Clone(jcBoard target)
 {
     EnPassantPawn = target.EnPassantPawn;
     for(int i = 0 ; i < 4 ; i++) {
         CastlingStatus[i] = target.CastlingStatus[i];
     }
     for(int i = 0 ; i < ALL_BITBOARDS ; i++) {
         BitBoards[i] = target.BitBoards[i];
     }
     MaterialValue[0] = target.MaterialValue[0];
     MaterialValue[1] = target.MaterialValue[1];
     NumPawns[0] = target.NumPawns[0];
     NumPawns[1] = target.NumPawns[1];
     ExtraKings[0] = target.ExtraKings[0];
     ExtraKings[1] = target.ExtraKings[1];
     HasCastled[0] = target.HasCastled[0];
     HasCastled[1] = target.HasCastled[1];
     CurrentPlayer = target.CurrentPlayer;
     return true;
 }
        // private jcMove UnrolledAlphabeta
        // The standard alphabeta, with the top level "unrolled" so that it can
        // return a jcMove structure instead of a mere minimax value
        // See jcAISearchAgent.Alphabeta for detailed comments on this code
        private jcMove UnrolledAlphabeta(jcBoard theBoard, int depth, int alpha, int beta)
        {
            jcMove BestMov = new jcMove();

            jcMoveListGenerator movegen = new jcMoveListGenerator();
            movegen.ComputeLegalMoves(theBoard);
            HistoryTable.SortMoveList(movegen, theBoard.GetCurrentPlayer());

            jcBoard newBoard = new jcBoard();
            int bestSoFar;

            bestSoFar = ALPHABETA_MINVAL;
            int currentAlpha = alpha;
            jcMove mov;

            // Loop on the successors
            while((mov = movegen.Next()) != null) {
                // Compute a board position resulting from the current successor
                newBoard.Clone(theBoard);
                newBoard.ApplyMove(mov);

                // And search it in turn
                int movScore = AlphaBeta(MINNODE, newBoard, depth - 1, currentAlpha, beta);

                // Ignore illegal moves in the alphabeta evaluation
                if(movScore == ALPHABETA_ILLEGAL)
                    continue;
                currentAlpha = Math.Max(currentAlpha, movScore);

                // Is the current successor better than the previous best?
                if(movScore > bestSoFar) {
                    BestMov.Copy(mov);
                    bestSoFar = movScore;
                    BestMov.MoveEvaluation = bestSoFar;

                    // Can we cutoff now?
                    if(bestSoFar >= beta) {
                        TransTable.StoreBoard(theBoard, bestSoFar, jcMove.EVALTYPE_UPPERBOUND, depth, MoveCounter);

                        // Add this move's efficiency in the HistoryTable
                        HistoryTable.AddCount(theBoard.GetCurrentPlayer(), mov);
                        return BestMov;
                    }
                }
            }

            // Test for checkmate or stalemate
            if(bestSoFar <= ALPHABETA_GIVEUP) {
                newBoard.Clone(theBoard);
                jcMoveListGenerator secondary = new jcMoveListGenerator();
                newBoard.SwitchSides();
                if(secondary.ComputeLegalMoves(newBoard)) {
                    // Then, we are not in check and may continue our efforts.
                    HistoryTable.SortMoveList(movegen, newBoard.GetCurrentPlayer());
                    movegen.ResetIterator();
                    BestMov.MoveType = jcMove.MOVE_STALEMATE;
                    BestMov.MovingPiece = jcBoard.KING + theBoard.GetCurrentPlayer();
                    while((mov = movegen.Next()) != null) {
                        newBoard.Clone(theBoard);
                        newBoard.ApplyMove(mov);
                        if(secondary.ComputeLegalMoves(newBoard)) {
                            BestMov.MoveType = jcMove.MOVE_RESIGN;
                        }
                    }
                } else {
                    // We're in check and our best hope is GIVEUP or worse, so either we are
                    // already checkmated or will be soon, without hope of escape
                    BestMov.MovingPiece = jcBoard.KING + theBoard.GetCurrentPlayer();
                    BestMov.MoveType = jcMove.MOVE_RESIGN;
                }
            }

            // If we haven't returned yet, we have found an accurate minimax score
            // for a position which is neither a checkmate nor a stalemate
            TransTable.StoreBoard(theBoard, bestSoFar, jcMove.EVALTYPE_ACCURATE, depth, MoveCounter);

            return BestMov;
        }
        /***************************************************************************
         * PRIVATE METHODS
         **************************************************************************/
        // private jcMove MTDF
        // Use the MTDF algorithm to find a good move.  MTDF repeatedly calls
        // alphabeta with a zero-width search window, which creates very many quick
        // cutoffs.  If alphabeta fails low, the next call will place the search
        // window lower; in a sense, MTDF is a sort of binary search mechanism into
        // the minimax space.
        private jcMove MTDF(jcBoard theBoard, int target, int depth)
        {
            int beta;
            jcMove Mov;
            int currentEstimate = target;
            int upperbound = ALPHABETA_MAXVAL;
            int lowerbound = ALPHABETA_MINVAL;

            // This is the trick: make repeated calls to alphabeta, zeroing in on the
            // actual minimax value of theBoard by narrowing the bounds
            do {
                if(currentEstimate == lowerbound)
                    beta = currentEstimate + 1;
                else
                    beta = currentEstimate;

                Mov = UnrolledAlphabeta(theBoard, depth, beta - 1, beta);
                currentEstimate = Mov.MoveEvaluation;

                if(currentEstimate < beta)
                    upperbound = currentEstimate;
                else
                    lowerbound = currentEstimate;

            } while(lowerbound < upperbound);

            return Mov;
        }