Home Previous Bottom Next

Writing a chess program in 99 steps


step 49: perft.cpp

#include <iostream>
#include "defines.h"
#include "extglobals.h"
#include "protos.h"
 
U64 perft(int depth)
{
 
       // Raw node count, up to depth, doing a full tree search.
    // perft is very similar to the search algorithm - instead of evaluating the leaves, we count them.
       //
       // Be carefull when calling this function with depths > 7, because it can take a very long
       // time before the result is returned: the average branching factor in chess is 35, so every
       // increment in depth will require 35x more computer time.
       //
       // perft is a good way of verifying correctness of the movegenerator and (un)makeMove,
       // because you can compare the results with published results for certain test positions.
       //
       // perft is also used to measure the performance of the move generator and (un)makeMove in terms
       // of speed, and to compare different implementations of generating, storing and (un)making moves.
 
       U64 retVal = 0;     
       int i;
 
       // count this node
       if (depth == 0)
       {
              #ifdef WINGLET_DEBUG_EVAL
                     int before = board.eval();
                     board.mirror();
                     int after = board.eval();
                     board.mirror();
                     if (before != after)
                     {
                           std::cout << "evaluation is not symmetrical! " << before << std::endl;
                           for (int j = 0 ; j < board.endOfSearch ; j++)
                     {
                                  std::cout << j+1 << ". ";
                                  displayMove(board.gameLine[j].move);
                                  std::cout <<std::endl;
                           }
                           board.display();
                     }
              #endif
              return 1;
       }
 
       // generate moves from this position
       board.ply++;
       movegen();
 
       // loop over moves:
       for (i = board.moveBufLen[board.ply]; i < board.moveBufLen[board.ply+1]; i++)
       {
              makeMove(board.moveBuffer[i]);
              {
                     if (!isOtherKingAttacked())
                     {
                           // recursively call perft
                           retVal += perft(depth-1);
 
#ifdef WINGLET_DEBUG_PERFT
                           if (depth == 1)
                           {
                                  if (board.moveBuffer[i].isCapture()) ICAPT++;
                                  if (board.moveBuffer[i].isEnpassant()) IEP++;
                                  if (board.moveBuffer[i].isPromotion()) IPROM++;
                                  if (board.moveBuffer[i].isCastleOO()) ICASTLOO++;
                                  if (board.moveBuffer[i].isCastleOOO()) ICASTLOOO++;
                                  if (isOwnKingAttacked()) ICHECK++;
                           }
#endif
                     }
              }
              unmakeMove(board.moveBuffer[i]);
       }
       board.ply--;
       return retVal;
}

 
 
step 50: command.cpp  

#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE 1
#endif
#include <iostream>
#include "defines.h"
#include "protos.h"
#include "extglobals.h"
#include "board.h"
#include "timer.h"
 
void readCommands()
{
       int nextc;
 
       if (board.nextMove == WHITE_MOVE)
       {
                     std::cout << "wt> ";
       }
       else
       {
                     std::cout << "bl> ";
       }
       std::cout.flush();
 
//     ===========================================================================
//     Read a command and call doCommand:
//     ===========================================================================
       while ((nextc = getc(stdin)) != EOF)
       {
              if (nextc == '\n')
              {
                     CMD_BUFF[CMD_BUFF_COUNT] = '\0';
                     while (CMD_BUFF_COUNT)
                     {
                           if (!doCommand(CMD_BUFF)) return;
                     }     
                     if (board.nextMove == WHITE_MOVE)
                     {
                           std::cout << "wt> ";
                     }
                     else
                     {
                           std::cout << "bl> ";
                     }
                     std::cout.flush();
              }
              else
              {
                     if (CMD_BUFF_COUNT >= MAX_CMD_BUFF-1)
                     {
                           std::cout << "Warning: command buffer full !! " << std::endl;
                           CMD_BUFF_COUNT = 0;
                     }
                     CMD_BUFF[CMD_BUFF_COUNT++] = nextc;
              }
       }
}
 
BOOLTYPE doCommand(const char *buf)
{
       Move move;
       Timer timer;
    U64 msStart;
       U64 msStop;
    U64 perftcount;
       char userinput[80];
       int i, number;
 
//     =================================================================
//  return when command buffer is empty
//     =================================================================
 
       if (!strcmp(buf, ""))
       {
              CMD_BUFF_COUNT = '\0';
              return true;    
       }
 
//     =================================================================
//  help, h, or ?: show this help
//     =================================================================
       if ((!strcmp(buf, "help")) || (!strcmp(buf, "h")) || (!strcmp(buf, "?")))
       {
              std::cout << std::endl << "help:" << std::endl;
              std::cout << "black               : BLACK to move" << std::endl;
              std::cout << "cc                  : play computer-to-computer " << std::endl;
              std::cout << "d                   : display board " << std::endl;
              std::cout << "exit                : exit program " << std::endl;
              std::cout << "eval                : show static evaluation of this position" << std::endl;
              std::cout << "game                : show game moves " << std::endl;
              std::cout << "go                  : computer next move " << std::endl;
              std::cout << "help, h, or ?       : show this help " << std::endl;
              std::cout << "info                : display variables (for testing purposes)" << std::endl;
              std::cout << "move e2e4, or h7h8q : enter a move (use this format)" << std::endl;
              std::cout << "moves               : show all legal moves" << std::endl;
              std::cout << "new                 : start new game" << std::endl;
              std::cout << "perf                : benchmark a number of key functions" << std::endl;
              std::cout << "perft n             : calculate raw number of nodes from here, depth n " << std::endl;
              std::cout << "quit                : exit program " << std::endl;
              std::cout << "r                   : rotate board " << std::endl;
              std::cout << "readfen filename n  : reads #-th FEN position from filename" << std::endl;
              std::cout << "sd n                : set the search depth to n" << std::endl;
              std::cout << "setup               : setup board... " << std::endl;
              std::cout << "undo                : take back last move" << std::endl;
              std::cout << "white               : WHITE to move" << std::endl;
              std::cout << std::endl;
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  black: black to move
//     =================================================================
       if (!strcmp(buf, "black"))
       {
              board.nextMove = BLACK_MOVE;
              CMD_BUFF_COUNT = '\0';
              return true;
       }     
 
//     =================================================================
//  d: display board
//     =================================================================
       if (!strcmp(buf, "d"))
       {
              board.display();
              CMD_BUFF_COUNT = '\0';
              return true;
       }     
      
//     =================================================================
//  eval (do a static evaluation of the board)
//     =================================================================
 
       if (!strcmp(buf, "eval"))
       {
              number = board.eval();
              std::cout << "eval score = " << number << std::endl;
              #ifdef WINGLET_DEBUG_EVAL
                     board.mirror();
                     board.display();
                     i = board.eval();
                     std::cout << "eval score = " << i << std::endl;
                     board.mirror();
                     if (number != i) std::cout << "evaluation is not symmetrical! " << number << std::endl;
                     else std::cout << "evaluation is symmetrical" << std::endl;
              #endif
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  exit or quit: exit program
//     =================================================================
       if ((!strcmp(buf, "exit")) || (!strcmp(buf, "quit")))
       {
              CMD_BUFF_COUNT = '\0';
              return false;
       }
 
//     =================================================================
//  game: show game moves
//     =================================================================
       if (!strcmp(buf, "game"))
       {
              if (board.endOfGame)
              {
                     for (i = 0 ; i < board.endOfGame ; i++)
              {
                           std::cout << i+1 << ". ";
                           displayMove(board.gameLine[i].move);
                           std::cout <<std::endl;
                     }
              }
              else
              {
                     std::cout << "there are no game moves" << std::endl;         
              }
              CMD_BUFF_COUNT = '\0';
              return true;
       }
      
//     =================================================================
//  moves: show all legal moves
//     =================================================================
       if (!strcmp(buf, "moves"))
       {
              i = 0;
              board.ply = 0;
              movegen();
              std::cout << std::endl << "moves from this position:" << std::endl;
        for (number = board.moveBufLen[board.ply]; number < board.moveBufLen[board.ply+1]; number++)
              {
                     makeMove(board.moveBuffer[number]);
                     if (isOtherKingAttacked())             
                     {
                           unmakeMove(board.moveBuffer[number]);
                     }
                     else
                     {
                           std::cout << i + 1 << ". " ;
                           displayMove(board.moveBuffer[number]);         
                           std::cout << std::endl;
                           unmakeMove(board.moveBuffer[number]);
                           i++;
                     }
              }
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  move (do a move) [console mode only]
//     =================================================================
 
       if (!strncmp(buf, "move", 4))
       {
              sscanf(buf+4,"%s",userinput);
 
              // generate the pseudo-legal move list
              board.ply = 0;
              movegen();
 
              if (isValidTextMove(userinput, move))        // check to see if the user move is also found in the pseudo-legal move list
              {
                     makeMove(move);
 
                     if (isOtherKingAttacked())              // post-move check to see if we are leaving our king in check
                     {
                           unmakeMove(move);
                           std::cout << "    invalid move, leaving king in check: " << userinput << std::endl;
                     }
                     else
                     {
                           board.endOfGame++;
                           board.endOfSearch = board.endOfGame;
                           board.display();
                     }
              }
              else
              {
                     std::cout << "    move is invalid or not recognized: " << userinput << std::endl;
              }
              CMD_BUFF_COUNT = '\0';
              return true;
       }
      
//     =================================================================
//  info: display variables (for testing purposes)
//     =================================================================
       if (!strcmp(buf, "info"))
       {
              info();
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
 
//     =================================================================
//  new: start new game
//     =================================================================
       if (!strcmp(buf, "new"))
       {
              dataInit();
              board.init();
              board.display();
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  perft n  : calculate raw number of nodes from here, depth n
//     =================================================================
       if (!strncmp(buf, "perft", 5))
       {
              sscanf(buf+5,"%d", &number);
              std::cout << "    starting perft " << number << "..." << std::endl;
              timer.init();
              board.ply = 0;
 
#ifdef WINGLET_DEBUG_PERFT
              ICAPT = 0;
              IEP = 0;
              IPROM = 0;
              ICASTLOO = 0;
              ICASTLOOO = 0;
              ICHECK = 0;
#endif
 
              msStart = timer.getms();
              perftcount = perft(number);
              msStop = timer.getms();
 
              std::cout << "nodes        = " << perftcount << ", " << msStop - msStart << " ms, ";
              if ((msStop - msStart) > 0)
                     std::cout << (perftcount/(msStop - msStart)) << " knods/s";
              std::cout << std::endl;
              CMD_BUFF_COUNT = '\0';
 
#ifdef WINGLET_DEBUG_PERFT
              std::cout << "captures     = " << ICAPT << std::endl;
              std::cout << "en-passant   = " << IEP << std::endl;
              std::cout << "castlings    = " << ICASTLOO + ICASTLOOO << std::endl;
              std::cout << "promotions   = " << IPROM << std::endl;
              std::cout << "checks       = " << ICHECK << std::endl;
#endif
              return true;
       }
 
//     =================================================================
//  r: rotate board
//     =================================================================
       if (!strcmp(buf, "r"))
       {
              board.viewRotated = !board.viewRotated;
              board.display();
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  readfen filename n : reads #-th FEN position from filename
//     =================================================================
       if (!strncmp(buf, "readfen", 7))
       {
              sscanf(buf+7,"%s %d", userinput, &number);
              board.init();
              readFen(userinput, number);
              board.display();
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  setup : setup board...
//     =================================================================
       if (!strncmp(buf, "setup", 5))
       {
              setup();
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  undo: take back last move
//     =================================================================
       if (!strcmp(buf, "undo"))
       {
              if (board.endOfGame)
              {
                     unmakeMove(board.gameLine[--board.endOfGame].move);
                     board.endOfSearch = board.endOfGame;
                     board.display();
              }
              else
              {
                     std::cout << "already at start of game" << std::endl;
              }
              CMD_BUFF_COUNT = '\0';
              return true;
       }
 
//     =================================================================
//  white: white to move
//     =================================================================
       if (!strcmp(buf, "white"))
       {
              board.nextMove = WHITE_MOVE;
              CMD_BUFF_COUNT = '\0';
              return true;
       }     
 
//     =================================================================
//  unknown command
//     =================================================================
       std::cout << "    command not implemented: " << buf << ", type 'help' for more info" << std::endl;
       CMD_BUFF_COUNT = '\0';
       return true;
}

 


Home Previous Top Next

last update: Saturday 11 June 2011