Home Previous Bottom Next

Writing a chess program in 99 steps


 

step 111: search.cpp

#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <conio.h>
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include "defines.h"
#include "extglobals.h"
#include "protos.h"
#include "board.h"
#include "timer.h"
 
Move Board::think()
{
 
//     ===========================================================================
//  This is the entry point for search, it is intended to drive iterative deepening
//  The search stops if (whatever comes first):
//  - there is no legal move (checkmate or stalemate)
//  - there is only one legal move (in this case we don't need to search)
//  - time is up
//  - the search is interrupted by the user, or by winboard
//  - the search depth is reached
//     ===========================================================================
 
       int score, legalmoves, currentdepth;
       Move singlemove;
 
//     ===========================================================================
//     Check if the game has ended, or if there is only one legal move,
//  because then we don't need to search:
//     ===========================================================================
       if (isEndOfgame(legalmoves, singlemove)) return NOMOVE;
       if (legalmoves == 1)
       {
              std::cout << "forced move: "; displayMove(singlemove); std::cout << std::endl;
              if (XB_MODE && XB_POST)
              {
                     printf("0 0 0 0 ");
                     displayMove(singlemove);
                     std::cout << std::endl;
              }
              return singlemove;
       }
 
//     ===========================================================================
//     There is more than legal 1 move, so prepare to search:
//     ===========================================================================
       if (XB_MODE) timeControl();
       lastPVLength = 0;
       memset(lastPV, 0 , sizeof(lastPV));
       memset(whiteHeuristics, 0, sizeof(whiteHeuristics));
       memset(blackHeuristics, 0, sizeof(blackHeuristics));
       inodes = 0;
       countdown = UPDATEINTERVAL;
       timedout = false;
       // display console header
       displaySearchStats(1, 0, 0); 
       timer.init();
       msStart = timer.getms();
 
       //  iterative deepening:
       for (currentdepth = 1; currentdepth <= searchDepth; currentdepth++)
       {
              //  clear the buffers:
              memset(moveBufLen, 0, sizeof(moveBufLen));
              memset(moveBuffer, 0, sizeof(moveBuffer));
              memset(triangularLength, 0, sizeof(triangularLength));
              memset(triangularArray, 0, sizeof(triangularArray));
              followpv = true;
              allownull = true;
              score = alphabetapvs(0, currentdepth, -LARGE_NUMBER, LARGE_NUMBER);
              // now check if time is up
              // if not decide if it makes sense to start another iteration:
              if (timedout)
              {
                     std::cout << std::endl;
                     return (lastPV[0]);
              }
              else
              {
                     if (!XB_NO_TIME_LIMIT)
                     {
                           msStop = timer.getms();
                           if ((msStop - msStart) > (STOPFRAC * maxTime))
                           {
                                  if (!XB_MODE) std::cout << "    ok" << std::endl;
                                  return (lastPV[0]);
                           }
                     }
              }
              displaySearchStats(2, currentdepth, score);
              // stop searching if the current depth leads to a forced mate:
              if ((score > (CHECKMATESCORE-currentdepth)) || (score < -(CHECKMATESCORE-currentdepth)))
                     currentdepth = searchDepth;
       }
       return (lastPV[0]);
}
 
(...) 
 
void Board::displaySearchStats(int mode, int depth, int score)
{
       // displays various search statistics
       // only to be used when ply = 0
       // mode = 1 : display header
       // mode = 2 : display full stats, including score and latest PV
       // mode = 3 : display current root move that is being searched
       //                     depth = ply, score = loop counter in the search move list
 
       char sanMove[12], timestring[8];
       U64 dt;
 
       dt = timer.getms() - msStart;
       switch (mode)
       {
              case 1:
                           if (!XB_MODE) std::cout << "depth  score   nodes     time  knps PV" << std::endl;
                           break;
 
              case 2:
                           if (XB_MODE && XB_POST)
                           {
                                  printf("%5d %6d %8d %9d ", depth, score, dt/10, inodes);
                                  rememberPV();
                                  displayPV();
                           }
                           else
                           {
                                  // depth
                                  printf("%5d ", depth);
 
                                  // score
                                  printf("%+6.2f ", float(score/100.0));
 
                                  // nodes searched
                                  if      (inodes > 100000000) printf("%6.0f%c ", float(inodes/1000000.0), 'M');
                                  else if (inodes > 10000000) printf("%6.2f%c ", float(inodes/1000000.0), 'M');
                                  else if (inodes > 1000000) printf("%6.0f%c ", float(inodes/1000.0),    'K');
                                  else if (inodes > 100000)  printf("%6.1f%c ", float(inodes/1000.0),    'K');
                                  else if (inodes > 10000)   printf("%6.2f%c ", float(inodes/1000.0),    'K');
                                  else                                     printf("%7d ", inodes);
 
                                  // search time
                                  mstostring(dt, timestring);
                                  printf("%8s ", timestring);
 
                                  // search speed
                                  if (dt > 0) std::cout << std::setw(5) << (inodes/dt) << " ";
                                  else          std::cout << "    - ";
 
                                  // store this PV:
                                  rememberPV();
 
                                  // display the PV
                                  displayPV();
                           }
                           break;
 
              case 3: // Note that the numbers refer to pseudo-legal moves:
                           if (!TO_CONSOLE) break;
                           if (XB_MODE)
                           {
 
                           }
                           else
                           {
                                  mstostring(dt, timestring);
                                  printf("             (%2d/%2d) %8s       ", score+1, moveBufLen[depth+1]-moveBufLen[depth], timestring);
                                  unmakeMove(moveBuffer[score]);
                                  toSan(moveBuffer[score], sanMove);
                                  std::cout << sanMove;
                                  makeMove(moveBuffer[score]);
                                  printf("...    \r");
                                  std::cout.flush();
                           }
                           break;
 
              default: break;
       }
 
       return;
}
 
(...)

void
timeControl()
{
 
//     ===========================================================================
//     This routine is used to calculate maxTime, the maximum time for this move
//     in millisceonds. Based on:
//     XB_CTIM = computer's time, milliseconds
//     XB_OTIM = opponents' time, millseconds
//     XB_INC = time increment, milliseconds
//     ===========================================================================
       int xb_ctim, movesLeft;
 
//     ===========================================================================
//     First build in a safety buffer of 2000 milliseconds:
//     ===========================================================================
       xb_ctim = XB_CTIM - 2000;
       if (xb_ctim < 1) xb_ctim = 1;
 
//     ===========================================================================
//     Estimate the number of moves per side that are left. Assume 80 half moves
//     per game with a minimum of 10 half moves left to play, no matter how many moves are played:
//     ===========================================================================
       movesLeft = 80 - board.endOfSearch;
       if (movesLeft < 20) movesLeft = 20;
 
       #ifdef WINGLET_DEBUG_WINBOARD
              std::cout << "#-winglet : in timecontrol, movesleft=" << movesLeft << std::endl;
       #endif
//     ===========================================================================
//     Use up part of the thinking time advantage that we may have:
//     ===========================================================================
       if ((XB_OTIM + XB_INC) < xb_ctim)
              board.maxTime = (xb_ctim / movesLeft) + XB_INC + (int)(0.80*(xb_ctim - XB_OTIM - XB_INC));
       else
              board.maxTime = (xb_ctim / movesLeft);
 
 
//     ===========================================================================
//     If an XB_INC is defined, then there is no reason to run out of time:
//     ===========================================================================
       if ((XB_INC) && (xb_ctim < XB_INC)) board.maxTime = xb_ctim;
 
//     ===========================================================================
//     Final checks, all moves should be > something:
//     ===========================================================================
       if (board.maxTime > xb_ctim) board.maxTime = (int)(0.8 * xb_ctim);
       if (board.maxTime < 1) board.maxTime = 1;
 
       #ifdef WINGLET_DEBUG_WINBOARD
              std::cout << "#-winglet : in timecontrol, XB_CTIM=" << XB_CTIM << std::endl;
              std::cout << "#-winglet : in timecontrol, XB_OTIM=" << XB_OTIM << std::endl;
              std::cout << "#-winglet : in timecontrol, XB_INC =" << XB_INC << std::endl;
              std::cout << "#-winglet : in timecontrol, maxtime=" << board.maxTime << std::endl;
       #endif
      
       return;
}

 

step 112: peek.cpp

#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <windows.h>
#include <iostream>
#include <conio.h>
#include "extglobals.h"
#include "protos.h"
#include "timer.h"
 
void Board::readClockAndInput()
{
       // check if we need to stop, because time is up, or because the user has hit the keyboard.
       // UPDATEINTERVAL defines how often this check is done in terms of nodes searched.
       // For example, if the search speed is 1000 knods per second, then a value of UPDATEINTERVAL = 100000
       // will result in 10 checks per second (or 0.1s time intervals)
 
       DWORD nchar;
       char command[80];
 
       // reset countdown
       countdown = UPDATEINTERVAL;
 
       if ((!XB_NO_TIME_LIMIT && ((timer.getms() - msStart) > maxTime)) || (!XB_MODE && _kbhit()))
       {
              timedout = true;
              #ifdef WINGLET_DEBUG_WINBOARD
                     if (XB_MODE)
                     {
                           if (_kbhit())
                           {
                                  std::cout << "#-winglet : timed out, kbhit" << std::endl;
                           }
                           else
                           {
                                  std::cout << "#-winglet : timed out, time" << std::endl;
                           }
                     }
              #endif
              return;
       }
 
noPonder:
 
       if ((XB_MODE) && (PeekNamedPipe(GetStdHandle(STD_INPUT_HANDLE), NULL, 0, NULL, &nchar, NULL)))
       {
 
              for (CMD_BUFF_COUNT = 0; CMD_BUFF_COUNT < (int)nchar; CMD_BUFF_COUNT++)
           {
                     CMD_BUFF[CMD_BUFF_COUNT] = getc(stdin);
                     // sometimes we do not receive a newline character
                     if (((CMD_BUFF_COUNT+1)==(int)nchar) || CMD_BUFF[CMD_BUFF_COUNT] == '\n')
//                   if (CMD_BUFF[CMD_BUFF_COUNT] == '\n')
 
                     {
                           if (CMD_BUFF[CMD_BUFF_COUNT] == '\n') CMD_BUFF[CMD_BUFF_COUNT] = '\0';
                            else CMD_BUFF[CMD_BUFF_COUNT+1] = '\0';
//                         CMD_BUFF[CMD_BUFF_COUNT] = '\0';
 
                           if (CMD_BUFF=="" || !CMD_BUFF_COUNT) return;
 
                           sscanf(CMD_BUFF, "%s", command);
                           #ifdef WINGLET_DEBUG_WINBOARD
                                  std::cout << "#-winglet : in peek, CMD_BUFF=" << CMD_BUFF << " command=" << command << std::endl;
                           #endif
 
                           // do not stop thinking/pondering/analyzing for any of the following commands:
                           if (!strcmp(command, ".")) return;
                           if (!strcmp(command, "?")) return;
                           if (!strcmp(command, "bk")) return;
                           if (!strcmp(command, "easy"))
                           {
                                  XB_PONDER = false;
                                  return;
                           }
                           if (!strcmp(command, "hint")) return;
                           if (!strcmp(command, "nopost"))
                           {
                                  XB_POST = false;
                                  return;
                           }     
                           if (!strcmp(command, "otim"))
                           {
                                  sscanf(CMD_BUFF, "otim %d", &XB_OTIM);
                                  XB_OTIM *= 10;  // convert centiseconds to miliseconds;
                                  goto noPonder;
                           }     
                           if (!strcmp(command, "post"))
                           {
                                  XB_POST = true;
                                  return;
                           }     
                           if (!strcmp(command, "time"))
                            {
                                  sscanf(CMD_BUFF,"time %d",&XB_CTIM);
                                  XB_CTIM *= 10; // convert centiseconds to milliseconds
                                  goto noPonder;
                           }
                           timedout = true;
                           CMD_BUFF_COUNT = (int)strlen(CMD_BUFF);
                           XB_DO_PENDING = true;
                           return;
                     }
              }
       }
       return;
}

 

 

  You can use winboard to play games, experiment with the code, or have winglet play matches against other winboard engines.

 


Home Previous Top Next

last update: Friday 09 September 2011