Home Previous Bottom Next

Writing a chess program in 99 steps


 

 

 

step 41: 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;
       }    
     
//     =================================================================
//  exit or quit: exit program
//     =================================================================
       if ((!strcmp(buf, "exit")) || (!strcmp(buf, "quit")))
       {
              CMD_BUFF_COUNT = '\0';
              return false;
       }
     
//     =================================================================
//  info: display variables (for testing purposes)
//     =================================================================
       if (!strcmp(buf, "info"))
       {
              info();
              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;
       }
      
//     =================================================================
//  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;
       }
 
//     =================================================================
//  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;
}
 

 

 

step 42: defines.h

#ifndef WINGLET_DEFINES_H
#define WINGLET_DEFINES_H
 
#define WINGLET_PROG_VERSION "winglet 0.0, Copyright (C) 2011, Stef Luijten"
//#define WINGLET_DEBUG_PERFT
//#define WINGLET_DEBUG_MOVES
 
typedef unsigned long long U64;
typedef unsigned long long BitMap;
typedef short SHORTINT;
typedef unsigned short USHORTINT;
typedef int BOOLTYPE;
 
#define MAX_CMD_BUFF     256   // Console command input buffer
#define MAX_MOV_BUFF     4096   // Number of moves that we can store (all plies)
#define MAX_PLY          64    // Search depth
#define MAX_GAME_LINE    1024   // Number of moves in the (game + search) line that we can store
 
#endif

 

step 43: protos.h

#ifndef WINGLET_PROTOS_H
#define WINGLET_PROTOS_H
 
#include "board.h"
#include "move.h"
 
unsigned int  bitCnt(BitMap);
void          dataInit();
void          displayBitmap(BitMap);
void          displayMove(Move &);
BOOLTYPE      doCommand(const char *);
unsigned int  firstOne(BitMap);
void          info();
BOOLTYPE      isAttacked(BitMap &, const unsigned char &);
BOOLTYPE      isValidTextMove(char *, Move &);
BOOLTYPE      isOtherKingAttacked();
BOOLTYPE      isOwnKingAttacked();
unsigned int  lastOne(BitMap);
void          makeBlackPromotion(unsigned int, unsigned int &);
void          makeCapture(unsigned int &, unsigned int &);
void          makeMove(Move &);
void          makeWhitePromotion(unsigned int, unsigned int &);
void          movegen();
U64           perft(int, int);
void          readCommands();
BOOLTYPE      readFen(char *, int);
void          setup();
void          setupFen(char *, char *, char *, char *, int , int );
void          unmakeBlackPromotion(unsigned int, unsigned int &);
void          unmakeCapture(unsigned int &, unsigned int &);
void          unmakeMove(Move &);
void          unmakeWhitePromotion(unsigned int, unsigned int &);
 
#endif

 

  try "perft n", use small values of n (4 to 6). Here are some test positions with results.

  try making moves with the "move" command, for instance "move e2e4".

  this is also a good time to try out different compile options, or compare 32-bit with 64-bit executables (in case you have a 64-bit machine).  Perft will give you an indication of the differences in speed for movegen and (un)makemove. And if you're adventurous you can try out different ways of storing moves.

 


Home Previous Top Next

last update: Saturday 11 June 2011