Home Previous Bottom Next

Writing a chess program in 99 steps


 
step 58: 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;
       }
 
//     =================================================================
//  go: computer next move
//     =================================================================
       if (!strcmp(buf, "go"))
       {
           move = board.think();
           if (move.moveInt)
           {
               makeMove(move);
               board.endOfGame++;
               board.endOfSearch = board.endOfGame;
           }
           board.display();
           CMD_BUFF_COUNT = '\0';
           return true;
       }
 
//     =================================================================
//  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"))
       {
              board.moveBufLen[0] = 0;
              board.moveBufLen[1] = movegen(board.moveBufLen[0]);
              std::cout << std::endl << "moves from this position:" << std::endl;
              for (i = board.moveBufLen[0]; i < board.moveBufLen[1]; i++)
              {
                     makeMove(board.moveBuffer[i]);
                     if (isOtherKingAttacked())            
                     {
                           unmakeMove(board.moveBuffer[i]);
                     }
                     else
                     {
                           std::cout << i+1 << ". " ;
                           displayMove(board.moveBuffer[i]);      
                           std::cout << std::endl;
                           unmakeMove(board.moveBuffer[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.moveBufLen[0] = 0;
              board.moveBufLen[1] = movegen(board.moveBufLen[0]);
 
              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;
       }
 
//     =================================================================
//  moves: show all legal moves
//     =================================================================
       if (!strcmp(buf, "moves"))
       {
              board.moveBufLen[0] = 0;
              board.moveBufLen[1] = movegen(board.moveBufLen[0]);
              std::cout << std::endl << "pseudo-legal moves from this position:" << std::endl;
              for (i = board.moveBufLen[0]; i < board.moveBufLen[1]; i++)
              {
                           std::cout << i+1 << ". " ;
                           displayMove(board.moveBuffer[i]);      
                           std::cout << std::endl;
              }
              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.moveBufLen[0] = 0;
 
#ifdef WINGLET_DEBUG_PERFT
              ICAPT = 0;
              IEP = 0;
              IPROM = 0;
              ICASTLOO = 0;
              ICASTLOOO = 0;
              ICHECK = 0;
#endif
 
              msStart = timer.getms();
              perftcount = perft(0, 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;
       }
 
//     =================================================================
//  sd n: set the search depth to n
//     =================================================================
       if (!strncmp(buf, "sd", 2))
       {
              sscanf(buf+2,"%d", &board.searchDepth);
              std::cout << "winglet> search depth " << board.searchDepth << std::endl;
              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;
}



There still are some serious issues with the current search functionality, we will address these one by one in the next sections. The most important shortcomings are:

  • the search assumes that there will always be a legal move. Of course that is not true in case one of the variations leads to a stalemate, or a checkmate, before the end of the tree is reached.  Checkmates are not identified a such !

  • there is no move ordering, which makes the search still kind of sluggish, and doing a lot of unnecessary work.

  • you will often see a score that is oscillating for even/odd search depths (high for odd search depths, low for even search depths), and the last move in the PV is a capture.  This is because we are searching to a fixed depth and then stop, even if we are in the middle of an exchange of pieces.   Actually, since there is no check for this, the search will now show a preference to end the PV with a capture for the side to move (or promotion), because the side not to move cannot recapture!

 

  setup, or read, a position, or just take the start position, and type "sd 5"  to set the search depth at a fixed value of 5 plies, then type "go" to have your computer search and carry out the best move.

  you can compare searches by the minimax, alphabeta, or alphabetapvs algorithms (just uncomment the respective code lines in function 'think') and see the differences in search times and nodes visited.

 


Home Previous Top Next

last update: Sunday 19 June 2011