// Enleve tout ce qui est en echec. void TMoveList::ChoisiMove(int ply, int wtm) { #ifdef JOURNAL journal.Log( "ChoisiMove: NbMove: %d\n", nbmove ); #endif if ( nbmove == 0 ) return; currmove = 0; int iDernierValide = -1; // Verifier chaque coup. while( currmove < nbmove ) { // Effectuer le coup et verifier si est en echec. MakeMove(ply, moves[currmove], wtm); int bEstEchec = Check(wtm); UnmakeMove(ply, moves[currmove], wtm); // Si le coup est valide le copier dans la case suivant le dernier // coup valide trouver. if ( !bEstEchec ) { iDernierValide++; moves[iDernierValide] = moves[currmove]; } currmove++; } // Nombre de coup valide. nbmove = iDernierValide+1; }
void cmd_pgnreplay(void) { char tmp_epd[]=".tmp.epd"; char data[MAXSTR]=""; FILE *epdfile=NULL; char epdline[MAXSTR]=""; PGNReadFromFile (token[1],1); SaveEPD( tmp_epd ); epdfile = fopen( tmp_epd, "r" ); if ( fgets( epdline, MAXSTR, epdfile ) == NULL ) { printf( _("Incorrect epd file\n") ); return; } strcpy( data, "setboard " ); int i=0; while ( epdline[i] != '\n' ) { data[i+9] = epdline[i]; ++i; } data[i+9] = '\0'; SetDataToEngine( data ); SetAutoGo( true ); pgnloaded = 1; pgncnt = GameCnt; while (GameCnt >= 0) { if (GameCnt >= 0) { CLEAR (flags, ENDED); CLEAR (flags, TIMEOUT); ChangeColor( true ); SetAutoGo( true ); UnmakeMove (board.side, &Game[GameCnt].move); if (GameCnt >= 0) { UnmakeMove (board.side, &Game[GameCnt].move); } } } cmd_first(); }
void cmd_first(void) { if (!pgnloaded) return; while (GameCnt >= 0) { if (GameCnt >= 0) { CLEAR (flags, ENDED); CLEAR (flags, TIMEOUT); ChangeColor( true ); SetAutoGo( true ); UnmakeMove (board.side, &Game[GameCnt].move); if (GameCnt >= 0) { UnmakeMove (board.side, &Game[GameCnt].move); } } } ShowBoard (); }
void PopfromRecord(Gamerec *g, Position *p, int len, int count) { int i; i = len * (len-1) / 2; i += count - 1; UnmakeMove(p, &(g->moves[i])); g->moves[i].mtype = EMPTY; }
void cmd_previous(void) { if (!pgnloaded) return; if (GameCnt >= 0) { ChangeColor( true ); SetAutoGo( true ); UnmakeMove (board.side, &Game[GameCnt].move); } else { printf(_("Initial position reached. There are no earlier moves.\n")); return; } printf("%d. ",GameCnt/2+1); printf("%s\n", Game[GameCnt].SANmv); ShowBoard (); }
bool Position::IsGameOver(std::string& message) { bool hasMoves = false; MoveList mvlist; mvlist.GenAllMoves(*this); for (int i = 0; i < mvlist.Size(); ++i) { if (MakeMove(mvlist[i].m_mv)) { hasMoves = true; UnmakeMove(); break; } } if (hasMoves == false) { if (InCheck()) message = (m_side == WHITE)? "0-1 {Black mates}" : "1-0 {White mates}"; else message = "1/2-1/2 {Draw: stalemate}"; return true; } if (EstimateDraw(*this) == EVAL_THEORETICAL_DRAW) { message = "1/2-1/2 {Draw: material}"; return true; } if (GetRepetitions() >= 3) { message = "1/2-1/2 {Draw: 3rd repetition}"; return true; } return false; }
// Algorithme de recherche MinMax avec des coupes Alpha-Beta. int Search(int depth, int ply, int wtm, int alpha, int beta, bool do_null) { register int Valeur, AlphaInitiale, check_ext = 0, extension = 0, MoveCherche, danger = 0; if ( ply >= MAXPLY-1 ) return beta; iNodes++; // Si on n'est pas en mode Analyse, Reste-t-il du temps? if ( interrupted || (timeabort && !g_bModeAnalyse) ) { return beta; } AlphaInitiale = alpha; // Calculer le temps restant. if ( (iNodes & 0xFF ) == 0xFF ) { if ( TimeCheck() ) { timeabort = true; } // Verifier si il y a quelque chose dans le tampon // d'entree. if ( inter() ) { char buf[10]; int c = fgetc(stdin); if (c == 10) { c = fgetc(stdin); ungetc(c, stdin); if (c == '.') { scanf("%s", &buf); printf( "stat01: %d %d %d %d %d\n", (TempsCenti()-timestamp), iNodes, iProfondeurIteration, cb.MoveList[1].nbmove - cb.MoveList[1].currmove-1, cb.MoveList[1].nbmove ); journal.Log("received a dot\n"); } else { interrupted = true; } } else { ungetc(c, stdin); interrupted = true; } } } // Verifier si ce n'est pas une nulle. if (Repetition(wtm)) { //if ( 0 < beta ) { // pv[ply-1][ply-1] = cb.CurrentPath.moves[ply-1]; return 0; // if ( wtm ) { // return DRAWSCORE; // } // else { // return -DRAWSCORE; // } //} } // Regarder dans la table de transposition pour voir si cette position // n'a pas deja ete calculer. #ifdef TRANSPOSITION switch( TableTrans->Lookup( cb, ply, depth, wtm, alpha, beta, danger ) ) { case SCORE_EXACTE: return alpha; case BORNE_SUPERIEUR: return alpha; case BORNE_INFERIEUR: return beta; case EVITER_NULL: do_null = false; } #endif #ifdef NULL_MOVE // En premier, on essai le NULL MOVE. if ( do_null ) { int nbPiece = wtm?cb.TotalMaterielBlanc:cb.TotalMaterielNoir; if ( !cb.inCheck[ply] && nbPiece > 5 && depth > 3 && alpha == beta-1) { int EnPassantB = cb.EnPassantB[ply+1]; int EnPassantN = cb.EnPassantN[ply+1]; cb.EnPassantB[ply+1] = -1; cb.EnPassantN[ply+1] = -1; Valeur = -Search(depth-3, ply+1, !wtm, -beta, -beta+1, false); cb.EnPassantB[ply+1] = EnPassantB; cb.EnPassantN[ply+1] = EnPassantN; if ( Valeur >= beta ) { #ifdef TRANSPOSITION TableTrans->StoreRefutation( cb, ply, depth, wtm, Valeur, alpha, beta, danger ); #endif return Valeur; } if ( Valeur <= -MATE+50 ) danger = 1; } } #endif // Internal Iterative Deepening. /* if ( depth > 2 && ((!(ply&1) && alpha==root_alpha && beta==root_beta) || ((ply&1) && alpha==-root_beta && beta==-root_alpha)) && cb.HashMove[ply].From == 0 && cb.HashMove[ply].To == 0 ) { Valeur = ABSearch( cb, depth-2, ply, wtm, alpha, beta, true ); if ( Valeur <= alpha ) { Valeur = ABSearch( cb, depth-2, ply, wtm, -MATE, beta, true ); } else { if ( Valeur < beta ) { cb.HashMove[ply] = pv[ply-1][ply]; } else cb.HashMove[ply] = cb.CurrentPath.moves[ply]; } }*/ #ifdef TRANSPOSITION Phase[ply] = HASH_MOVE; #else // if ( ((!(ply&1) && alpha==root_alpha && beta==root_beta) || // ( (ply&1) && alpha==-root_beta && beta==-root_alpha)) ) Phase[ply] = PV_MOVE; // else // Phase[ply] = GENERATE_CAPTURE_MOVES; #endif // Maintenant, evaluer chaque coup. MoveCherche = 0; while( Phase[ply] != NO_MORE_MOVES && !interrupted ) { if ( NextMove( cb, ply, wtm ) ) { // Si // On execute le coup. MakeMove(ply, cb.MoveList[ply].CurrentMove(), wtm); // Mettre le coup dans le chemin actuel. cb.CurrentPath.moves[ply] = cb.MoveList[ply].moves[cb.MoveList[ply].currmove]; if (!Check(wtm)) { MoveCherche++; // Si le coup met en echec etendre la recherche d'une profondeur // pour qu'il puisse en sortir. if (Check(!wtm) ) { cb.inCheck[ply+1] = true; extension = 1; cb.RaisonExtension[ply] = EXTENSION_ECHEC; } else { extension = 0; cb.inCheck[ply+1] = false; cb.RaisonExtension[ply] = PAS_EXTENSION; } // Si le coup est une recapture // etendre la recherche d'une profondeur. if ( cb.CurrentPath.moves[ply].Capture && abs(ValeurPiece[cb.CurrentPath.moves[ply-1].Capture] - ValeurPiece[cb.CurrentPath.moves[ply].Capture]) <= 20 && (cb.CurrentPath.moves[ply-1].To == cb.CurrentPath.moves[ply].To) && cb.RaisonExtension[ply-1] != EXTENSION_RECAPTURE) { extension = 1; cb.RaisonExtension[ply] = EXTENSION_RECAPTURE; } // Si on est sur le point de promouvoir un pion, étendre la recherche // pour voir si c'est une menace. if ( extension == 0 && cb.CurrentPath.moves[ply].Piece == 1 && PromoteExtension[ cb.CurrentPath.moves[ply].To ] ) { extension = 1; cb.RaisonExtension[ply] = EXTENSION_PROMOTION; } // Si on pousse un pion passe, pousser la recherche plus // loin pour voir si c'est un danger. if ( cb.CurrentPath.moves[ply].Piece == pion ) if ( wtm ) { if ( cb.CurrentPath.moves[ply].To <= H5 ) if ( cb.PionPasseB[cb.CurrentPath.moves[ply].To&7] ) { extension = 1; cb.RaisonExtension[ply] = EXTENSION_PIONPASSE; } } else { if ( cb.CurrentPath.moves[ply].To >= A4 ) if ( cb.PionPasseN[cb.CurrentPath.moves[ply].To&7] ) { extension = 1; cb.RaisonExtension[ply] = EXTENSION_PIONPASSE; } } // Razoring trick. Idee prise dans Crafty. if ( depth == 2 && !cb.inCheck[ply] && extension == 0 ) { int valeur; if ( wtm ) valeur = Eval(ply, wtm, alpha, beta); else valeur = -Eval(ply, wtm, alpha, beta); if ( valeur+50 < alpha ) extension = -1; } // On l'explore. // Si c'est la variation principale, Fenetre normale, sinon, // fenetre est n et n+1. int inpv = 1; if (ply&1) { if ( alpha != root_alpha || beta != root_beta ) inpv = 0; } else { if ( alpha != -root_beta || beta != -root_alpha ) inpv = 0; } if ( inpv ) { Valeur = -ABSearch(depth-1+extension+danger, ply+1, !wtm, -beta, -alpha, true); } else { Valeur = -ABSearch(depth-1+extension+danger, ply+1, !wtm, -alpha-1, -alpha, true); if ( Valeur > alpha && Valeur < beta ) { pvsresearch++; Valeur = -ABSearch(depth-1+extension+danger, ply+1, !wtm, -beta, -alpha, true); } } } else { Valeur = -INFINI; } cb.MoveList[ply].CurrentMove().Score = Valeur; // On defait le coup. UnmakeMove(ply, cb.MoveList[ply].CurrentMove(), wtm); if (interrupted) return Valeur; #ifdef DEBUG Consistence( cb, cb.MoveList[ply].CurrentMove() ); #endif // Est-il meilleur que notre valeur actuelle? if ( Valeur > alpha ) { if ( Valeur >= beta ) { #ifdef TRANSPOSITION TableTrans->StoreRefutation( cb, ply, depth, wtm, Valeur, alpha, beta, check_ext ); #endif g_iRefutation++; // Verifier si on peu l'utiliser comme killer move. if ( Phase[ply] == NON_CAPTURE_MOVES ) { cb.AddKiller( &cb.CurrentPath.moves[ply-1], ply-1 ); } else { if ( Phase[ply] == KILLER_MOVE_2 ) cb.Killers[ply-1][0].Score++; else if ( Phase[ply] == GENERATE_NON_CAPTURE_MOVES ) cb.Killers[ply-1][1].Score++; } return Valeur; } pv[ply][ply] = cb.CurrentPath.moves[ply]; pv_length[ply] = pv_length[ply+1]; memcpy(&pv[ply][ply+1], &pv[ply+1][ply+1], sizeof(TMove)*(pv_length[ply]-ply)); alpha = Valeur; } } // if } // Verifier si il y a mat ou pat. if ( MoveCherche == 0 ) { if (!Check(wtm)) { pv[ply][ply] = cb.CurrentPath.moves[ply]; return 0; // if ( wtm ) { // return DRAWSCORE; // } // else { // return -DRAWSCORE; // } } else { if ( ply < iMateInPly ) iMateInPly = ply; alpha = -MATE+iMateInPly; } } #ifdef TRANSPOSITION TableTrans->StoreBest( cb, ply, depth, wtm, alpha, AlphaInitiale, danger ); #endif return alpha; }
bool Position::MakeMove(Move mv) { Undo& undo = _undos[_undoSize++]; undo.m_castlings = m_castlings; undo.m_ep = m_ep; undo.m_fifty = m_fifty; undo.m_hash = m_hash; undo.m_mv = mv; FLD from = mv.From(); FLD to = mv.To(); PIECE piece = mv.Piece(); PIECE captured = mv.Captured(); PIECE promotion = mv.Promotion(); COLOR side = m_side; COLOR opp = side ^ 1; m_hash ^= s_hashSide[1]; ++m_fifty; if (captured) { m_fifty = 0; if (to == m_ep) Remove(to + 8 - 16 * side); else Remove(to); } Remove(from); Put(to, piece); m_ep = NF; switch (piece) { case PW: case PB: m_fifty = 0; if (to - from == -16 + 32 * side) m_ep = to + 8 - 16 * side; else if (promotion) { Remove(to); Put(to, promotion); } break; case KW: m_Kings[WHITE] = to; if (from == E1) { if (to == G1) { Remove(H1); Put(F1, RW); m_castlings ^= WHITE_DID_O_O; } else if (to == C1) { Remove(A1); Put(D1, RW); m_castlings ^= WHITE_DID_O_O_O; } } break; case KB: m_Kings[BLACK] = to; if (from == E8) { if (to == G8) { Remove(H8); Put(F8, RB); m_castlings ^= BLACK_DID_O_O; } else if (to == C8) { Remove(A8); Put(D8, RB); m_castlings ^= BLACK_DID_O_O_O; } } break; default: break; } static const U8 castlingDelta[64] = { 0xf7, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xfe }; m_castlings &= castlingDelta[from]; m_castlings &= castlingDelta[to]; ++m_ply; m_side ^= 1; if (IsAttacked(m_Kings[side], opp)) { UnmakeMove(); return false; } return true; }
int AnyLegalMoves(Position *pos) { int i, j, x, y, direc; unsigned char side = pos->side, xside; SQUARE board[12][12]; Listnode l, *ml = &l; ClearMoves(ml); for(i = 0; i < MAXLEGAL; i++) { ml->moves[i].oldep = pos->epstatus; ml->moves[i].oldcastle = pos->castle; } if(side == WHITE) { xside = BLACK; direc = 1; } else { xside = WHITE; direc = -1; } memcpy(&(board[0][0]), &(pos->board[0][0]), sizeof(SQUARE) * 144); for(i = 2; i < 10; i++) for(j = 2; j < 10; j++) { if(board[i][j] != 0 && (board[i][j] & side)) { if(board[i][j] & KNIGHT) { x = -1; y = -2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = -1; y = 2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 1; y = -2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 1; y = 2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = -2; y = -1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = -2; y = 1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 2; y = -1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 2; y = 1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } } if((board[i][j] & BISHOP) || (board[i][j] & QUEEN)) { for(x = 1; (board[i-x][j-x] != OFFBOARD) && (!(board[i-x][j-x] & side)); x++) if(board[i-x][j-x] & xside) { AddMove(ml, i, j, i-x, j-x, CAPTURE); break; } else AddMove(ml, i, j, i-x, j-x, NORMAL); for(x = 1; (board[i-x][j+x] != OFFBOARD) && (!(board[i-x][j+x] & side)); x++) if(board[i-x][j+x] & xside) { AddMove(ml, i, j, i-x, j+x, CAPTURE); break; } else AddMove(ml, i, j, i-x, j+x, NORMAL); for(x = 1; (board[i+x][j-x] != OFFBOARD) && (!(board[i+x][j-x] & side)); x++) if(board[i+x][j-x] & xside) { AddMove(ml, i, j, i+x, j-x, CAPTURE); break; } else AddMove(ml, i, j, i+x, j-x, NORMAL); for(x = 1; (board[i+x][j+x] != OFFBOARD) && (!(board[i+x][j+x] & side)); x++) if(board[i+x][j+x] & xside) { AddMove(ml, i, j, i+x, j+x, CAPTURE); break; } else AddMove(ml, i, j, i+x, j+x, NORMAL); } if((board[i][j] & ROOK) || (board[i][j] & QUEEN)) { for(x = 1; (board[i-x][j] != OFFBOARD) && (!(board[i-x][j] & side)); x++) if(board[i-x][j] & xside) { AddMove(ml, i, j, i-x, j, CAPTURE); break; } else AddMove(ml, i, j, i-x, j, NORMAL); for(x = 1; (board[i+x][j] != OFFBOARD) && (!(board[i+x][j] & side)); x++) if(board[i+x][j] & xside) { AddMove(ml, i, j, i+x, j, CAPTURE); break; } else AddMove(ml, i, j, i+x, j, NORMAL); for(x = 1; (board[i][j-x] != OFFBOARD) && (!(board[i][j-x] & side)); x++) if(board[i][j-x] & xside) { AddMove(ml, i, j, i, j-x, CAPTURE); break; } else AddMove(ml, i, j, i, j-x, NORMAL); for(x = 1; (board[i][j+x] != OFFBOARD) && (!(board[i][j+x] & side)); x++) if(board[i][j+x] & xside) { AddMove(ml, i, j, i, j+x, CAPTURE); break; } else AddMove(ml, i, j, i, j+x, NORMAL); } if(board[i][j] & KING) { for(x = -1; x <= 1; x++) for(y = -1; y <= 1; y++) { if(x == 0 && y == 0) continue; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } } } if(board[i][j] & PAWN) { if(board[i][j+direc] == 0) { if((direc == -1 && j == 3) || (direc == 1 && j == 8)) AddMove(ml, i, j, i, j+direc, PROMOTE); else AddMove(ml, i, j, i, j+direc, NORMAL); } if((direc == -1 && j == 8) || (direc == 1 && j == 3)) if(board[i][j+direc+direc] == 0 && board[i][j+direc] == 0) AddMove(ml, i, j, i, j+direc+direc, TWOMOVE); if((board[i-1][j+direc] != OFFBOARD) && (board[i-1][j+direc] & xside)) { if((direc == -1 && j == 3) || (direc == 1 && j == 8)) AddMove(ml, i, j, i-1, j+direc, PROMCAP); else AddMove(ml, i, j, i-1, j+direc, CAPTURE); } if((board[i+1][j+direc] != OFFBOARD) && (board[i+1][j+direc] & xside)) { if((direc == -1 && j == 3) || (direc == 1 && j == 8)) AddMove(ml, i, j, i+1, j+direc, PROMCAP); else AddMove(ml, i, j, i+1, j+direc, CAPTURE); } if(pos->epstatus > 0 && direc == 1) /* there is at least one ep square */ { if(j == 6 && IsEpSquare(i-1, 6, pos->epstatus) == 1 && board[i-1][j+direc] == 0) AddMove(ml, i, j, i-1, j+direc, ENPASSANT); if(j == 6 && IsEpSquare(i+1, 6, pos->epstatus) == 1 && board[i+1][j+direc] == 0) AddMove(ml, i, j, i+1, j+direc, ENPASSANT); } if(pos->epstatus > 0 && direc == -1) { if(j == 5 && IsEpSquare(i-1, 5, pos->epstatus) == 1 && board[i-1][j+direc] == 0) AddMove(ml, i, j, i-1, j+direc, ENPASSANT); if(j == 5 && IsEpSquare(i+1, 5, pos->epstatus) == 1 && board[i+1][j+direc] == 0) AddMove(ml, i, j, i+1, j+direc, ENPASSANT); } } for(x = 0; ml->moves[x].mtype != EMPTY; x++) { MakeMove(pos, &(ml->moves[x])); if(IsCheck(pos, side) == 1) { UnmakeMove(pos, &(ml->moves[x])); ml->moves[x].mtype = EMPTY; ml->num--; } else UnmakeMove(pos, &(ml->moves[x])); } if(ml->num > 0) { return 1; } } } return 0; }
void GenerateMoves(Listnode *ml, Position *pos) { int i, j, x, y, direc, cancastle; unsigned char side = pos->side, xside; SQUARE board[12][12]; if(side == WHITE) { xside = BLACK; direc = 1; } else { xside = WHITE; direc = -1; } ClearMoves(ml); for(i = 0; i < MAXLEGAL; i++) { ml->moves[i].oldep = pos->epstatus; ml->moves[i].oldcastle = pos->castle; } memcpy(&(board[0][0]), &(pos->board[0][0]), sizeof(SQUARE) * 144); for(i = 2; i < 10; i++) { for(j = 2; j < 10; j++) { if(board[i][j] != 0 && (board[i][j] & side)) { if(board[i][j] & KNIGHT) { x = -1; y = -2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = -1; y = 2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 1; y = -2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 1; y = 2; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = -2; y = -1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = -2; y = 1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 2; y = -1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } x = 2; y = 1; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } } if((board[i][j] & BISHOP) || (board[i][j] & QUEEN)) { for(x = 1; (board[i-x][j-x] != OFFBOARD) && (!(board[i-x][j-x] & side)); x++) if(board[i-x][j-x] & xside) { AddMove(ml, i, j, i-x, j-x, CAPTURE); break; } else AddMove(ml, i, j, i-x, j-x, NORMAL); for(x = 1; (board[i-x][j+x] != OFFBOARD) && (!(board[i-x][j+x] & side)); x++) if(board[i-x][j+x] & xside) { AddMove(ml, i, j, i-x, j+x, CAPTURE); break; } else AddMove(ml, i, j, i-x, j+x, NORMAL); for(x = 1; (board[i+x][j-x] != OFFBOARD) && (!(board[i+x][j-x] & side)); x++) if(board[i+x][j-x] & xside) { AddMove(ml, i, j, i+x, j-x, CAPTURE); break; } else AddMove(ml, i, j, i+x, j-x, NORMAL); for(x = 1; (board[i+x][j+x] != OFFBOARD) && (!(board[i+x][j+x] & side)); x++) if(board[i+x][j+x] & xside) { AddMove(ml, i, j, i+x, j+x, CAPTURE); break; } else AddMove(ml, i, j, i+x, j+x, NORMAL); } if((board[i][j] & ROOK) || (board[i][j] & QUEEN)) { for(x = 1; (board[i-x][j] != OFFBOARD) && (!(board[i-x][j] & side)); x++) if(board[i-x][j] & xside) { AddMove(ml, i, j, i-x, j, CAPTURE); break; } else AddMove(ml, i, j, i-x, j, NORMAL); for(x = 1; (board[i+x][j] != OFFBOARD) && (!(board[i+x][j] & side)); x++) if(board[i+x][j] & xside) { AddMove(ml, i, j, i+x, j, CAPTURE); break; } else AddMove(ml, i, j, i+x, j, NORMAL); for(x = 1; (board[i][j-x] != OFFBOARD) && (!(board[i][j-x] & side)); x++) if(board[i][j-x] & xside) { AddMove(ml, i, j, i, j-x, CAPTURE); break; } else AddMove(ml, i, j, i, j-x, NORMAL); for(x = 1; (board[i][j+x] != OFFBOARD) && (!(board[i][j+x] & side)); x++) if(board[i][j+x] & xside) { AddMove(ml, i, j, i, j+x, CAPTURE); break; } else AddMove(ml, i, j, i, j+x, NORMAL); } if(board[i][j] & KING) { for(x = -1; x <= 1; x++) for(y = -1; y <= 1; y++) { if(x == 0 && y == 0) continue; if((board[i+x][j+y] != OFFBOARD) && (!(board[i+x][j+y] & side))) { if(board[i+x][j+y] & xside) AddMove(ml, i, j, i+x, j+y, CAPTURE); else AddMove(ml, i, j, i+x, j+y, NORMAL); } } } if(board[i][j] & PAWN) { if(board[i][j+direc] == 0) { if((direc == -1 && j == 3) || (direc == 1 && j == 8)) AddMove(ml, i, j, i, j+direc, PROMOTE); else AddMove(ml, i, j, i, j+direc, NORMAL); } if((direc == -1 && j == 8) || (direc == 1 && j == 3)) if(board[i][j+direc+direc] == 0 && board[i][j+direc] == 0) AddMove(ml, i, j, i, j+direc+direc, TWOMOVE); if((board[i-1][j+direc] != OFFBOARD) && (board[i-1][j+direc] & xside)) { if((direc == -1 && j == 3) || (direc == 1 && j == 8)) AddMove(ml, i, j, i-1, j+direc, PROMCAP); else AddMove(ml, i, j, i-1, j+direc, CAPTURE); } if((board[i+1][j+direc] != OFFBOARD) && (board[i+1][j+direc] & xside)) { if((direc == -1 && j == 3) || (direc == 1 && j == 8)) AddMove(ml, i, j, i+1, j+direc, PROMCAP); else AddMove(ml, i, j, i+1, j+direc, CAPTURE); } /* there is at least one ep square */ if(pos->epstatus > 0 && direc == 1) { if(j == 6 && IsEpSquare(i-1, 6, pos->epstatus) == 1 && board[i-1][j+direc] == 0) AddMove(ml, i, j, i-1, j+direc, ENPASSANT); if(j == 6 && IsEpSquare(i+1, 6, pos->epstatus) == 1 && board[i+1][j+direc] == 0) AddMove(ml, i, j, i+1, j+direc, ENPASSANT); } if(pos->epstatus > 0 && direc == -1) { if(j == 5 && IsEpSquare(i-1, 5, pos->epstatus) == 1 && board[i-1][j+direc] == 0) AddMove(ml, i, j, i-1, j+direc, ENPASSANT); if(j == 5 && IsEpSquare(i+1, 5, pos->epstatus) == 1 && board[i+1][j+direc] == 0) AddMove(ml, i, j, i+1, j+direc, ENPASSANT); } } } } } if((pos->castle & WKC) && side == WHITE) { cancastle = 1; for(i = KFILE; i <= 8; i++) if(Attacked(pos, i, 2, xside) == 1) cancastle = 0; for(i = Minval(7, KFILE); i <= 8; i++) if(i != KFILE && i != KRFILE && board[i][2] != 0) cancastle = 0; if(cancastle == 1) AddMove(ml, KFILE, 2, 8, 2, KSCASTLE); } if((pos->castle & BKC) && side == BLACK) { cancastle = 1; for(i = KFILE; i <= 8; i++) if(Attacked(pos, i, 9, xside) == 1) cancastle = 0; for(i = Minval(7, KFILE); i <= 8; i++) if(i != KFILE && i != KRFILE && board[i][9] != 0) cancastle = 0; if(cancastle == 1) AddMove(ml, KFILE, 9, 8, 9, KSCASTLE); } if((pos->castle & WQC) && side == WHITE) { cancastle = 1; for(i = Minval(4, KFILE); i <= Maxval(4, KFILE); i++) if(Attacked(pos, i, 2, xside) == 1) cancastle = 0; for(i = Minval(QRFILE + 1, 4); i <= Maxval(5, KFILE); i++) if(i != KFILE && i != QRFILE && board[i][2] != 0) cancastle = 0; if(cancastle == 1) AddMove(ml, KFILE, 2, 4, 2, QSCASTLE); } if((pos->castle & BQC) && side == BLACK) { cancastle = 1; for(i = Minval(4, KFILE); i <= Maxval(4, KFILE); i++) if(Attacked(pos, i, 9, xside) == 1) cancastle = 0; for(i = Minval(QRFILE + 1, 4); i <= Maxval(5, KFILE); i++) if(i != KFILE && i != QRFILE && board[i][9] != 0) cancastle = 0; if(cancastle == 1) AddMove(ml, KFILE, 9, 4, 9, QSCASTLE); } for(i = 0; ml->moves[i].mtype != EMPTY; i++) { MakeMove(pos, &(ml->moves[i])); if(IsCheck(pos, side) == 1) { UnmakeMove(pos, &(ml->moves[i])); ml->moves[i].mtype = EMPTY; ml->num--; } else UnmakeMove(pos, &(ml->moves[i])); } }
int SearchRoot (int depth, int alpha, int beta) /************************************************************************** * * This perform searches at ply=1. For ply>1, it calls the more generic * search() routine. The rationale for splitting these is because at * ply==1, things are done slightly differently than from the other plies, * e.g. print PVs, not testing null move etc. * **************************************************************************/ { int best, score, savealpha; int side, xside; int ply, nodetype; leaf *p, *pbest; ply = 1; side = board.side; xside = 1^side; ChkCnt[2] = ChkCnt[1]; ThrtCnt[2] = ThrtCnt[1]; KingThrt[white][ply] = MateScan (white); KingThrt[black][ply] = MateScan (black); InChk[ply] = SqAtakd (board.king[side], xside); if (InChk[ply] && ChkCnt[ply] < 3*Idepth/DEPTH) { ChkExtCnt++; ChkCnt[ply+1]++; depth += DEPTH; } best = -INFINITY; savealpha = alpha; nodetype = PV; pbest = NULL; for (p = TreePtr[1]; p < TreePtr[2]; p++) { pick (p, 1); ShowThinking (p, ply); MakeMove (side, &p->move); NodeCnt++; /* If first move, search against full alpha-beta window */ if (p == TreePtr[1]) { score = -Search (2, depth-DEPTH, -beta, -alpha, nodetype); /* The following occurs when we are re-searching a fail high move and now it has fail low. This can be disastrous, so immediately adjust alpha and research. */ if (beta == INFINITY && score <= alpha) { alpha = -INFINITY; score = -Search (2, depth-DEPTH, -beta, -alpha, nodetype); } } /* Else search against zero window */ else { nodetype = CUT; alpha = MAX (best, alpha); score = -Search (2, depth-DEPTH, -alpha-1, -alpha, nodetype); if (score > best) { if (alpha < score && score < beta) { nodetype = PV; score = -Search (2, depth-DEPTH, -beta, -score, nodetype); } } } UnmakeMove (xside, &p->move); ply1score = p->score = score; if (score > best) { best = score; pbest = p; if (best > alpha) { rootscore = best; RootPV = p->move; if (best >= beta) goto done; ShowLine (RootPV, best, '&'); } } if (flags & TIMEOUT) { /* XXX: It seems that ply == 1 always at this point */ best = (ply & 1 ? rootscore : -rootscore ); return (best); } if (SearchDepth == 0 && (NodeCnt & TIMECHECK) == 0) { GetElapsed (); if ((et >= SearchTime && (rootscore == -INFINITY-1 || ply1score > lastrootscore - 25 || flags & SOLVE)) || et >= maxtime) SET (flags, TIMEOUT); } if (MATE+1 == best+1) return (best); } /* If none of the move is good, we still want to try the same first move */ if (best <= savealpha) TreePtr[1]->score = savealpha; /***************************************************************************** * * Out of main search loop. * *****************************************************************************/ done: /* Update history */ if (best > savealpha) history[side][pbest->move & 0x0FFF] += HISTSCORE(depth/DEPTH); rootscore = best; return (best); }
int CNegaScout_TT_HH::NegaScout(int depth, int alpha, int beta){ int count,i; int type; int a,b,t; int side; int score; i = IsGameOver(m_cur_position, depth); if (i != 0) return i; side = 1-(m_max_depth-depth)%2; score = m_tt->LookupHashTable(alpha, beta, depth, side); if (score != INVALID_SCORE && depth != m_max_depth){ /*if (depth == m_max_depth){ ChessMove move = new ChessMove(); move.Move = m_tt.LookupBestMove(side); m_best_move = move; }*/ return score; } if (depth <= 0) { score = m_evaluator->Evaluate(m_cur_position, side == 0, depth); m_tt->EnterHashTable(EXACT, score, depth, side); return score; } //m_nMovecount = 0; count = m_move_generator->CreatePossibleMove(m_cur_position, m_moves, depth, side); AddMoves(count, depth); for (i = 0; i < count; ++i) { m_move_list[depth][i].m_score = m_hh->GetHistoryScore(m_move_list[depth][i]); } m_hh->MergeSort(m_move_list[depth], count, false); int bestmove = -1; a = alpha; b = beta; int eval_is_exact = 0; for (i = 0; i < count; ++i){ m_tt->HashMakeMove(m_move_list[depth][i], m_cur_position); type = MakeMove(m_move_list[depth][i]); t = -NegaScout(depth-1 , -b, -a); if (t > a && t < beta && i > 0){ a = -NegaScout (depth-1, -beta, -t); /* re-search */ eval_is_exact = 1; if(depth == m_max_depth){ m_best_move = m_move_list[depth][i]; //m_tt.EnterHashBestMove(m_best_move.Move, side); } bestmove = i; } m_tt->HashUnmakeMove(m_move_list[depth][i], type, m_cur_position); UnmakeMove(m_move_list[depth][i],type); if (a < t){ eval_is_exact = 1; a = t; if(depth == m_max_depth) { m_best_move = m_move_list[depth][i]; //m_tt.EnterHashBestMove(m_best_move.Move, side); } } if (a >= beta) { m_tt->EnterHashTable(LOWER_BOUND, a, depth, side); m_hh->EnterHistoryScore(m_move_list[depth][i], depth); return a; } b = a + 1; /* set new null window */ } if (bestmove != -1) m_hh->EnterHistoryScore(m_move_list[depth][bestmove], depth); if (eval_is_exact != 0) m_tt->EnterHashTable(EXACT, a, depth, side); else m_tt->EnterHashTable(UPPER_BOUND, a, depth, side); return a; }
int Quiesce (uint8_t ply, int alpha, int beta) /************************************************************************** * * Our quiescent search. This quiescent search is able to recognize * mates. * **************************************************************************/ { uint8_t side, xside; int best, delta, score, savealpha; leaf *p, *pbest; if (EvaluateDraw ()) return (DRAWSCORE); side = board.side; xside = 1^side; InChk[ply] = SqAtakd (board.king[side], xside); best = Evaluate (alpha, beta); if (best >= beta && !InChk[ply]) return (best); TreePtr[ply+1] = TreePtr[ply]; if (InChk[ply]) { GenCheckEscapes (ply); if (TreePtr[ply] == TreePtr[ply+1]) return (-MATE+ply-2); if (best >= beta) return (best); SortMoves (ply); } else { GenCaptures (ply); if (TreePtr[ply] == TreePtr[ply+1]) return (best); SortCaptures (ply); } savealpha = alpha; pbest = NULL; alpha = MAX(best, alpha); delta = MAX (alpha - 150 - best, 0); for (p = TreePtr[ply]; p < TreePtr[ply+1]; p++) { pick (p, ply); /* We are in check or capture cannot bring score near alpha, give up */ if (!InChk[ply] && SwapOff (p->move) < delta) continue; /* If capture cannot bring score near alpha, give up */ if (p->score == -INFINITY) continue; #ifdef THREATEXT /* See search.c for an explanation of the code below */ if (threatply+1 == ply) { if ((TOSQ(p->move) == FROMSQ(threatmv)) || (FROMSQ(p->move) == TOSQ(threatmv))) continue; } if (threatply && threatply+3 == ply && FROMSQ(p->move) == TOSQ(threatmv)) continue; #endif MakeMove (side, &p->move); QuiesCnt++; if (SqAtakd (board.king[side], xside)) { UnmakeMove (xside, &p->move); continue; } score = -Quiesce (ply+1, -beta, -alpha); UnmakeMove (xside, &p->move); if (score > best) { best = score; pbest = p; if (best >= beta) goto done; alpha = MAX (alpha, best); } } done: if (flags & USEHASH && pbest != NULL) TTPut (side, 0, ply, savealpha, beta, best, pbest->move); return (best); }
// Algorithme de recherche MinMax avec des coupes Alpha-Beta. // Ici on considere seulement les prises. int Quiescence(int ply, int wtm, int alpha, int beta ) { register int Valeur, AlphaInitiale; if ( ply >= MAXPLY-1 ) return beta; iNodes++; if ( wtm ) { Valeur = Eval(ply, wtm, alpha, beta); } else { Valeur = -Eval(ply, wtm, alpha, beta); } // Remplace si le score est egal au score pour la NULLE. if ( Valeur == DRAWSCORE ) Valeur = DRAWSCORE+1; AlphaInitiale = alpha; if ( Valeur > alpha ) { if ( Valeur >= beta ) return Valeur; alpha = Valeur; pv_length[ply] = ply-1; } // On genere toutes les prises. cb.MoveList[ply].nbmove = 0; GenMoveAttaque(ply, wtm, cb.MoveList[ply]); int keep = -1; // Le materiel. int iScoreMateriel = cb.ScoreMaterielBlanc-cb.ScoreMaterielNoir; iScoreMateriel = wtm?iScoreMateriel:-iScoreMateriel; int delta = alpha-100-iScoreMateriel; int iScoreGain; for( int i=0; i<cb.MoveList[ply].nbmove; i++ ) { // Garder le coup. bool bGarde = false; // On cherche seulement les coups qui ramene le materiel proche de alpha. // ex. Si celui qui jouent vient de se faire prendre une dame et que alpha // dit que le meilleur score est la perte d'un pion et bien ignorer tout // les captures qui ramene pas au moins la dame. if ( ValeurPiece[cb.MoveList[ply].moves[i].Capture] >= delta ) { if (cb.MoveList[ply].moves[i].Capture == ROI) return beta; #ifdef USE_SEE int iScoreCapture = ValeurPiece[cb.MoveList[ply].moves[i].Capture]; iScoreGain = iScoreCapture-ValeurPiece[cb.MoveList[ply].moves[i].Piece]; if ( iScoreGain > 0 || iScoreGain >= 0 && delta <= 0 ) { bGarde = true; } else { iScoreGain = Echange(cb.MoveList[ply].moves[i].From, cb.MoveList[ply].moves[i].To, wtm ); if ( iScoreGain >= 0 ) { bGarde = true; } } #else bGarde = true; #endif } if ( bGarde ) { keep++; cb.MoveList[ply].moves[i].Score = iScoreGain; memcpy( &cb.MoveList[ply].moves[keep], &cb.MoveList[ply].moves[i], sizeof( TMove ) ); } } cb.MoveList[ply].nbmove = keep+1; cb.MoveList[ply].currmove = -1; cb.MoveList[ply].Tri(); Phase[ply] = CAPTURE_MOVES; // Maintenant, evaluer chaque coup. while( NextMove( cb, ply, wtm ) ) { // On execute le coup. MakeMove(ply, cb.MoveList[ply].CurrentMove(), wtm); // Mettre le coup dans le chemin actuel. cb.CurrentPath.moves[ply] = cb.MoveList[ply].moves[cb.MoveList[ply].currmove]; if (!Check(wtm)) Valeur = -Quiescence(ply+1, !wtm, -beta, -alpha); cb.MoveList[ply].CurrentMove().Score = Valeur; // On defait le coup. UnmakeMove(ply, cb.MoveList[ply].CurrentMove(), wtm); // Est-il meileur que notre valeur actuelle? if ( Valeur > alpha ) { if ( Valeur >= beta ) return Valeur; pv_length[ply] = pv_length[ply+1]; pv[ply][ply] = cb.CurrentPath.moves[ply]; memcpy(&pv[ply][ply+1], &pv[ply+1][ply], sizeof(TMove)*(pv_length[ply]-ply)); alpha = Valeur; } #ifdef DEBUG Consistence( cb, cb.MoveList[ply].CurrentMove() ); #endif } return alpha; }
// Cette routine est la racine de l'arbre de recherche. Elle doit etre separee // car plusieurs chose ne sont pas utile a la racine. Comme le null move et // le lookup dans la table de transposition. int SearchRacine(int depth, int wtm, int alpha, int beta) { register int Valeur, AlphaInitiale, extension = 0; AlphaInitiale = alpha; // Calculer le temps restant. if ( (iNodes & 0xFF ) == 0xFF ) { if ( TimeCheck() ) { timeabort = true; } // Verifier si il y a quelque chose dans le tampon // d'entree. if ( inter() ) { interrupted = true; } } // Maintenant, evaluer chaque coup. while( NextMoveRacine( cb, wtm ) && !g_bAbort ) { if ( g_bModeAnalyse ) { printf( "stat01: %d %d %d %d %d\n", (TempsCenti()-timestamp), iNodes, iProfondeurIteration, cb.MoveList[1].nbmove - cb.MoveList[1].currmove-1, cb.MoveList[1].nbmove ); } else if ( !xboard ) { printf( "\r [%d] (%2d/%d)", iProfondeurIteration, cb.MoveList[1].currmove+1, cb.MoveList[1].nbmove ); } // On execute le coup. iNodes++; MakeMove(1, cb.MoveList[1].CurrentMove(), wtm); // Mettre le coup dans le chemin actuel. cb.CurrentPath.moves[1] = cb.MoveList[1].moves[cb.MoveList[1].currmove]; // Si le coup met en echec etendre la recherche d'une profondeur // pour qu'il puisse en sortir. if ( Check(!wtm) ) { cb.inCheck[2] = true; extension = 1; cb.RaisonExtension[1] = EXTENSION_ECHEC; } else { extension = 0; cb.inCheck[2] = false; cb.RaisonExtension[1] = PAS_EXTENSION; } // On l'explore. // Si c'est la variation principale, Fenetre normale, sinon, // fenetre est n et n+1. if ( cb.MoveList[1].currmove == 0 ) { Valeur = -ABSearch(depth-1+extension, 2, !wtm, -beta, -alpha, true); } else { Valeur = -ABSearch(depth-1+extension, 2, !wtm, -alpha-1, -alpha, true); if ( Valeur > alpha && Valeur < beta ) { Valeur = -ABSearch(depth-1+extension, 2, !wtm, -beta, -alpha, true); } } // La nouvelle valeur. cb.CurrentPath.moves[1].Score = Valeur; cb.MoveList[1].moves[cb.MoveList[1].currmove].Score = Valeur; // On defait le coup. UnmakeMove(1, cb.MoveList[1].CurrentMove(), wtm); if (interrupted) return Valeur; #ifdef DEBUG Consistence( cb, cb.MoveList[1].CurrentMove() ); #endif // Est-elle meileur que notre valeur actuelle? if ( !timeabort || g_bModeAnalyse ) { if ( Valeur > alpha ) { pv[1][1] = cb.CurrentPath.moves[1]; pv_length[1] = pv_length[2]; memcpy( &pv[1][2], &pv[2][2], sizeof( TMove )*(pv_length[1]-1)); AffichePV(iProfondeurIteration); if ( Valeur >= beta ) { return Valeur; } alpha = Valeur; } else if ( cb.MoveList[1].currmove == 0 ) return alpha; } } // while return alpha; }
int Search (int ply, int depth, int alpha, int beta, int nodetype) /************************************************************************** * * The basic algorithm for this search routine came from Anthony * Marsland. It is a PVS (Principal Variation Search) algorithm. * The fail-soft alpha-beta technique is also used for improved * pruning. * **************************************************************************/ { int best, score, nullscore, savealpha; int side, xside; int rc, t0, t1, firstmove; int fcut, fdel, donull, savenode, nullthreatdone, extend; leaf *p, *pbest; int g0, g1; int upperbound; /* Check if this position is a known draw */ if (EvaluateDraw ()) return (DRAWSCORE); if (GameCnt >= Game50+3 && Repeat()) { RepeatCnt++; return (DRAWSCORE); } side = board.side; xside = 1^side; donull = true; /************************************************************************* * * Perform some basic search extensions. * 1. One reply extensions. * 2. If in check, extend (maximum of Idepth-1). * 3. If there is a threat to the King, extend (not beyond 2*Idepth) * 4. If recapture to same square and not beyond Idepth+2 * 5. If pawn move to 7th rank at the leaf node, extend. * *************************************************************************/ extend = false; InChk[ply] = SqAtakd (board.king[side], xside); if (InChk[ply]) { TreePtr[ply+1] = TreePtr[ply]; GenCheckEscapes (ply); if (TreePtr[ply] == TreePtr[ply+1]) return (-MATE+ply-2); if (TreePtr[ply]+1 == TreePtr[ply+1]) { depth += DEPTH; extend = true; OneRepCnt++; } } /* We've already found a mate at the next ply. If we aren't being mated by a shorter line, so just return the current material value. */ if (rootscore + ply >= MATE) return (MATERIAL); g0 = Game[GameCnt].move; g1 = GameCnt > 0 ? Game[GameCnt-1].move : 0; t0 = TOSQ(g0); t1 = TOSQ(g1); ChkCnt[ply+1] = ChkCnt[ply]; ThrtCnt[ply+1] = ThrtCnt[ply]; KingThrt[white][ply] = MateScan (white); KingThrt[black][ply] = MateScan (black); if (InChk[ply] && /* ChkCnt[ply] < Idepth-1*/ ply <= 2*Idepth/DEPTH) { ChkExtCnt++; ChkCnt[ply+1]++; depth += DEPTH; extend = true; } else if (!KingThrt[side][ply-1] && KingThrt[side][ply] && ply <= 2*Idepth/DEPTH) { KingExtCnt++; extend = true; depth += DEPTH; extend = true; donull = false; } /* Promotion extension */ else if (g0 & PROMOTION) { PawnExtCnt++; depth += DEPTH; extend = true; } /* Recapture extension */ else if ((g0 & CAPTURE) && (board.material[computer] - board.material[1^computer] == RootMaterial)) { RcpExtCnt++; depth += DEPTH; extend = true; } /* 6th or 7th rank extension */ else if (depth <= DEPTH && cboard[t0] == pawn && (RANK(t0) == rank7[xside] || RANK(t0) == rank6[xside])) { PawnExtCnt++; depth += DEPTH; extend = true; } /**************************************************************************** * * The following extension is to handle cases when the opposing side is * delaying the mate by useless interposing moves. * ****************************************************************************/ if (ply > 2 && InChk[ply-1] && cboard[t0] != king && t0 != t1 && !SqAtakd (t0, xside)) { HorzExtCnt++; depth += DEPTH; extend = true; } /*************************************************************************** * * This is a new code to perform search reductiion. We introduce some * form of selectivity here. * **************************************************************************/ if (depth <= 0) return (Quiesce (ply, alpha, beta)); /**************************************************************************** * * Probe the transposition table for a score and a move. * If the score is an upperbound, then we can use it to improve the value * of beta. If a lowerbound, we improve alpha. If it is an exact score, * if we now get a cut-off due to the new alpha/beta, return the score. * ***************************************************************************/ Hashmv[ply] = 0; upperbound = INFINITY; if (flags & USEHASH) { rc = TTGet (side, depth, ply, alpha, beta, &score, &g1); if (rc) { Hashmv[ply] = g1 & MOVEMASK; switch (rc) { case POORDRAFT : break; case EXACTSCORE : return (score); case UPPERBOUND : beta = MIN (beta, score); upperbound = score; donull = false; break; case LOWERBOUND : /*alpha = MAX (alpha, score);*/ alpha = score; break; case QUIESCENT : Hashmv[ply] = 0; break; default : break; } if (alpha >= beta) return (score); } } /***************************************************************************** * * Perform the null move here. There are certain cases when null move * is not done. * 1. When the previous move is a null move. * 2. At the frontier (depth == 1) * 3. At a PV node. * 4. If side to move is in check. * 5. If the material score + pawn value is still below beta. * 6. If we are being mated at next ply. * 7. If hash table indicate the real score is below beta (UPPERBOUND). * 8. If side to move has less than or equal to a bishop in value. * 9. If Idepth <= 3. This allows us to find mate-in 2 problems quickly. * 10. We are looking for a null threat. * *****************************************************************************/ if (ply > 4 && InChk[ply-2] && InChk[ply-4]) donull = false; if (flags & USENULL && g0 != NULLMOVE && depth > DEPTH && nodetype != PV && !InChk[ply] && MATERIAL+ValueP > beta && beta > -MATE+ply && donull && board.pmaterial[side] > ValueB && !threatply) { TreePtr[ply+1] = TreePtr[ply]; MakeNullMove (side); nullscore = -Search (ply+1, depth-DEPTH-R, -beta, -beta+1, nodetype); UnmakeNullMove (xside); if (nullscore >= beta) { NullCutCnt++; return (nullscore); } if ( depth-DEPTH-R >= 1 && MATERIAL > beta && nullscore <= -MATE+256) { depth += DEPTH; extend = true; } } if (InChk[ply] && TreePtr[ply]+1 < TreePtr[ply+1]) SortMoves (ply); pickphase[ply] = PICKHASH; GETNEXTMOVE; /************************************************************************* * * Razoring + Futility. * At depth 3, if there is no extensions and we are really bad, decrease * the search depth by 1. * At depth 2, if there is no extensions and we are quite bad, then we * prune all non checking moves and capturing moves that don't bring us up * back to alpha. * Caveat: Skip all this if we are in the ending. * *************************************************************************/ fcut = false; fdel = MAX (ValueQ, maxposnscore[side]); if (!extend && nodetype != PV && depth == 3*DEPTH && FUTSCORE <= alpha) { depth = 2*DEPTH; RazrCutCnt++; } fdel = MAX (ValueR, maxposnscore[side]); fcut = (!extend && nodetype != PV && depth == 2*DEPTH && FUTSCORE <= alpha); if (!fcut) { fdel = MAX (3*ValueP, maxposnscore[side]); fcut = (nodetype != PV && depth == DEPTH && FUTSCORE <= alpha); } MakeMove (side, &p->move); NodeCnt++; g0 = g1 = 0; while ((g0 = SqAtakd (board.king[side], xside)) > 0 || (fcut && FUTSCORE < alpha && !SqAtakd (board.king[xside], side) && !MateScan (xside))) { if (g0 == 0) g1++; UnmakeMove (xside, &p->move); if (GETNEXTMOVE == false) return (g1 ? Evaluate(alpha,beta) : DRAWSCORE); MakeMove (side, &p->move); NodeCnt++; } firstmove = true; pbest = p; best = -INFINITY; savealpha = alpha; nullthreatdone = false; nullscore = INFINITY; savenode = nodetype; if (nodetype != PV) nodetype = (nodetype == CUT) ? ALL : CUT; while (1) { /* We have already made the move before the loop. */ if (firstmove) { firstmove = false; score = -Search (ply+1, depth-DEPTH, -beta, -alpha, nodetype); } /* Zero window search for rest of moves */ else { if (GETNEXTMOVE == false) break; #ifdef THREATEXT /**************************************************************************** * * This section needs to be explained. We are doing a null threat search * and the previous ply was the null move. Inhibit any move which captures * the fail-high moving piece. Also inhibit any move by the piece which is * captured by the fail-high move. Both these moves cannot be executed in * the actual threat, so..... * Also 3 plies later, inhibit moves out of the target square of the PV/fail * high move as this is also not possible. * ****************************************************************************/ if (threatply+1 == ply) { if ((TOSQ(p->move) == FROMSQ(threatmv)) || (FROMSQ(p->move) == TOSQ(threatmv))) continue; } if (threatply && threatply+3 == ply && FROMSQ(p->move)==TOSQ(threatmv)) continue; #endif MakeMove (side, &p->move); NodeCnt++; if (SqAtakd (board.king[side], xside)) { UnmakeMove (xside, &p->move); continue; } /***************************************************************************** * * Futility pruning. The idea is that at the frontier node (depth == 1), * if the side on the move is materially bad, then if the move doesn't win * back material or the move isn't a check or doesn't threatened the king, * then there is no point in searching this move. So skip it. * Caveat: However if the node is a PV, we skip this test. * *****************************************************************************/ if (fcut && FUTSCORE <= alpha && !SqAtakd (board.king[xside], side) && !MateScan (xside)) { UnmakeMove (xside, &p->move); FutlCutCnt++; NodeCnt--; continue; } NodeCnt++; if (nodetype == PV) nodetype = CUT; alpha = MAX (best, alpha); /* fail-soft condition */ score = -Search (ply+1, depth-DEPTH, -alpha-1, -alpha, nodetype); if (score > best) { if (savenode == PV) nodetype = PV; if (alpha < score && score < beta) { score = -Search (ply+1, depth-DEPTH, -beta, -score, nodetype); } if (nodetype == PV && score <= alpha && Game[GameCnt+1].move == NULLMOVE) { score = -Search (ply+1, depth-DEPTH, -alpha, INFINITY, nodetype); } } } UnmakeMove (xside, &p->move); /* Perform threat extensions code */ #ifdef THREATEXT if ((score >= beta || nodetype == PV) && !InChk[ply] && g0 != NULLMOVE && !threatply && depth == 4 && ThrtCnt[ply] < 1) { if (!nullthreatdone) { threatply = ply; threatmv = p->move; MakeNullMove (side); nullscore = -Search(ply+1, depth-1-R, -alpha+THREATMARGIN, -alpha+THREATMARGIN+1, nodetype); UnmakeNullMove (xside); nullthreatdone = true; threatply = 0; } if (nullscore <= alpha-THREATMARGIN) { ThrtExtCnt++; ThrtCnt[ply+1]++; MakeMove (side, &p->move); score = -Search (ply+1, depth, -beta, -alpha, nodetype); UnmakeMove (xside, &p->move); ThrtCnt[ply+1]--; } } #endif if (score > best) { best = score; pbest = p; if (best >= beta) goto done; } if (flags & TIMEOUT) { best = (ply & 1 ? rootscore : -rootscore); return (best); } if (SearchDepth == 0 && (NodeCnt & TIMECHECK) == 0) { GetElapsed (); if ((et >= SearchTime && (rootscore == -INFINITY-1 || ply1score > lastrootscore - 25 || flags & SOLVE)) || et >= maxtime) SET (flags, TIMEOUT); } /* The following line should be explained as I occasionally forget too :) */ /* This code means that if at this ply, a mating move has been found, */ /* then we can skip the rest of the moves! */ if (MATE+1 == best+ply) goto done; } /***************************************************************************** * * Out of main search loop. * *****************************************************************************/ done: /* if (upperbound < best) printf ("Inconsistencies %d %d\n", upperbound, best); */ /* Save the best move inside the transposition table */ if (flags & USEHASH) TTPut (side, depth, ply, savealpha, beta, best, pbest->move); /* Update history */ if (best > savealpha) history[side][pbest->move & 0x0FFF] += HISTSCORE(depth/DEPTH); /* Don't store captures as killers as they are tried before killers */ if (!(pbest->move & (CAPTURE | PROMOTION)) && best > savealpha) { if (killer1[ply] == 0) killer1[ply] = pbest->move & MOVEMASK; else if ((pbest->move & MOVEMASK) != killer1[ply]) killer2[ply] = pbest->move & MOVEMASK; } return (best); }
/* ******************************************************************************* * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: Find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. Then, make this move and * * do a search from the resulting position. While pondering, one of three * * things can happen: (1) A move is entered, and it matches the predicted * * move. We then switch from pondering to thinking and search as normal; * * (2) A move is entered, but it does not match the predicted move. We then * * abort the search, unmake the pondered move, and then restart with the * * move entered. (3) A command is entered. If it is a simple command, it * * can be done without aborting the search or losing time. If not, we abort * * the search, execute the command, and then attempt to restart pondering if * * the command didn't make that impossible. * * * ******************************************************************************* */ int Ponder(int wtm) { TREE *const tree = block[0]; int dalpha = -999999, dbeta = 999999, i; unsigned *n_ponder_moves, *mv; int save_move_number, tlom, value; /* ************************************************************ * * * First, let's check to see if pondering is allowed, or * * if we should avoid pondering on this move since it is * * the first move of a game, or if the game is over, or * * "force" mode is active, or there is input in the queue * * that needs to be read and processed. * * * ************************************************************ */ if (!ponder || force || over || CheckInput()) return 0; save_move_number = move_number; /* ************************************************************ * * * Check the ponder move for legality. If it is not a * * legal move, we have to take action to find something to * * ponder. * * * ************************************************************ */ strcpy(ponder_text, "none"); if (ponder_move) { if (!VerifyMove(tree, 1, wtm, ponder_move)) { ponder_move = 0; Print(4095, "ERROR. ponder_move is illegal (1).\n"); Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); } } /* ************************************************************ * * * First attempt, do a hash probe. However, since a hash * * collision is remotely possible, we still need to verify * * that the transposition/refutation best move is actually * * legal. * * * ************************************************************ */ if (!ponder_move) { HashProbe(tree, 0, 0, wtm, dalpha, dbeta, &value); if (tree->hash_move[0]) ponder_move = tree->hash_move[0]; if (ponder_move) { if (!VerifyMove(tree, 1, wtm, ponder_move)) { Print(4095, "ERROR. ponder_move is illegal (2).\n"); Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); ponder_move = 0; } } } /* ************************************************************ * * * Second attempt. If that didn't work, then we try what * * I call a "puzzling" search. Which is simply a shorter * * time-limit search for the other side, to find something * * to ponder. * * * ************************************************************ */ if (!ponder_move) { TimeSet(puzzle); if (time_limit < 20) return 0; puzzling = 1; tree->status[1] = tree->status[0]; Print(32, " puzzling over a move to ponder.\n"); last_pv.pathl = 0; last_pv.pathd = 0; for (i = 0; i < MAXPLY; i++) { tree->killers[i].move1 = 0; tree->killers[i].move2 = 0; } Iterate(wtm, puzzle, 0); for (i = 0; i < MAXPLY; i++) { tree->killers[i].move1 = 0; tree->killers[i].move2 = 0; } puzzling = 0; if (tree->pv[0].pathl) ponder_move = tree->pv[0].path[1]; if (!ponder_move) return 0; for (i = 1; i < (int) tree->pv[0].pathl; i++) last_pv.path[i] = tree->pv[0].path[i + 1]; last_pv.pathl = tree->pv[0].pathl - 1; last_pv.pathd = 0; if (!VerifyMove(tree, 1, wtm, ponder_move)) { ponder_move = 0; Print(4095, "ERROR. ponder_move is illegal (3).\n"); Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); return 0; } } /* ************************************************************ * * * Display the move we are going to "ponder". * * * ************************************************************ */ if (wtm) Print(32, "White(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, wtm, ponder_move)); else Print(32, "Black(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, wtm, ponder_move)); sprintf(ponder_text, "%s", OutputMove(tree, 0, wtm, ponder_move)); if (post) printf("Hint: %s\n", ponder_text); /* ************************************************************ * * * Set the ponder move list and eliminate illegal moves. * * This list is used to test the move entered while we are * * pondering, since we need a move list for the input * * screening process. * * * ************************************************************ */ n_ponder_moves = GenerateCaptures(tree, 0, wtm, ponder_moves); num_ponder_moves = GenerateNoncaptures(tree, 0, wtm, n_ponder_moves) - ponder_moves; for (mv = ponder_moves; mv < ponder_moves + num_ponder_moves; mv++) { MakeMove(tree, 0, wtm, *mv); if (Check(wtm)) { UnmakeMove(tree, 0, wtm, *mv); *mv = 0; } else UnmakeMove(tree, 0, wtm, *mv); } /* ************************************************************ * * * Now, perform an iterated search, but with the special * * "pondering" flag set which changes the time controls * * since there is no need to stop searching until the * * opponent makes a move. * * * ************************************************************ */ MakeMove(tree, 0, wtm, ponder_move); tree->curmv[0] = ponder_move; tree->rep_list[++rep_index] = HashKey; tlom = last_opponent_move; last_opponent_move = ponder_move; if (kibitz) strcpy(kibitz_text, "n/a"); thinking = 0; pondering = 1; if (!wtm) move_number++; ponder_value = Iterate(Flip(wtm), think, 0); rep_index--; move_number = save_move_number; pondering = 0; thinking = 0; last_opponent_move = tlom; UnmakeMove(tree, 0, wtm, ponder_move); /* ************************************************************ * * * Search completed. the possible return values are: * * * * (0) No pondering was done, period. * * * * (1) Pondering was done, opponent made the predicted * * move, and we searched until time ran out in a * * normal manner. * * * * (2) Pondering was done, but the ponder search * * terminated due to either finding a mate, or the * * maximum search depth was reached. The result of * * this ponder search are valid, but only if the * * opponent makes the correct (predicted) move. * * * * (3) Pondering was done, but the opponent either made a * * different move, or entered a command that has to * * interrupt the pondering search before the command * * (or move) can be processed. This forces Main() to * * avoid reading in a move/command since one has been * * read into the command buffer already. * * * ************************************************************ */ if (input_status == 1) return 1; if (input_status == 2) return 3; return 2; }
void ShowLine (int move __attribute__ ((unused)), int score, char c) /***************************************************************************** * * Print out the latest PV found during the search. * The only move we know is the root move. The rest of the PV is taken * from the hash table. This strategy avoids all the headaches associated * with returning the PV up from the leaf to the root. * *****************************************************************************/ { int i, len; int pvar[MAXPLYDEPTH]; /* SMC */ if (!(flags & POST)) return; if (NodeCnt < 500000 && (flags & SOLVE)) { /* printf("NodeCnt = %d\n",NodeCnt); getchar(); */ return; } if (Idepth == DEPTH && c == '&') return; if ((flags & XBOARD) && c == '&') return; if (rootscore == -INFINITY-1) return; GetElapsed (); /* * What is the reason for these different output formats, in * particular for et? */ if (flags & XBOARD) { if (score > MATE-255) { printf ("%d%c Mat%d %d %lu\t", Idepth/DEPTH, c, (int)(MATE+2-abs(score))/2, (int)(et*100), NodeCnt+QuiesCnt); if (ofp != stdout) fprintf (ofp,"%2d%c%7.2f Mat%02d%10lu\t", Idepth/DEPTH, c, et, (MATE+2-abs(score))/2, NodeCnt+QuiesCnt); } else if (score < -MATE+255) { printf ("%d%c -Mat%2d %d %lu\t", Idepth/DEPTH, c, (int)(MATE+2-abs(score))/2, (int)(et*100), NodeCnt+QuiesCnt); if (ofp != stdout) fprintf (ofp,"%2d%c%7.2f -Mat%02d%10lu\t", Idepth/DEPTH, c, et, (MATE+2-abs(score))/2, NodeCnt+QuiesCnt); } else { printf ("%d%c %d %d %lu\t", Idepth/DEPTH, c, (int)score, (int)(et*100), NodeCnt+QuiesCnt); if (ofp != stdout) fprintf (ofp,"%2d%c%7.2f%7d%10lu\t", Idepth/DEPTH, c, et, score, NodeCnt+QuiesCnt); } } else { if (score > MATE-255) { printf ("\r%2d%c%7.2f Mat%02d%10lu\t", Idepth/DEPTH, c, et, (MATE+2-abs(score))/2, NodeCnt+QuiesCnt); if (ofp != stdout) fprintf (ofp,"\r%2d%c%7.2f Mat%02d%10lu\t", Idepth/DEPTH, c, et, (MATE+2-abs(score))/2, NodeCnt+QuiesCnt); } else if (score < -MATE+255) { printf ("\r%2d%c%7.2f -Mat%02d%10lu\t", Idepth/DEPTH, c, et, (MATE+2-abs(score))/2, NodeCnt+QuiesCnt); if (ofp != stdout) fprintf (ofp,"\r%2d%c%7.2f -Mat%02d%10lu\t", Idepth/DEPTH, c, et, (MATE+2-abs(score))/2, NodeCnt+QuiesCnt); } else { printf ("\r%2d%c%7.2f%7d%10lu\t", Idepth/DEPTH, c, et, score, NodeCnt+QuiesCnt); if (ofp != stdout) fprintf (ofp,"\r%2d%c%7.2f%7d%10lu\t", Idepth/DEPTH, c, et, score, NodeCnt+QuiesCnt); } } if (c == '-') { printf ("\n"); if (ofp != stdout) fprintf(ofp, "\n"); return; } else if (c == '+') { SANMove (RootPV, 1); printf (" %s\n", SANmv); if (ofp != stdout) fprintf (ofp," %s\n", SANmv); return; } SANMove (RootPV, 1); printf (" %s", SANmv); if (ofp != stdout) fprintf (ofp," %s", SANmv); MakeMove (board.side, &RootPV); TreePtr[3] = TreePtr[2]; GenMoves (2); len = strlen (SANmv); i = 2; pvar[1] = RootPV; /* We fill the rest of the PV with moves from the hash table */ if ((flags & USEHASH)) { while (TTGetPV (board.side, i, rootscore, &pvar[i])) { if ((MATESCORE(score) && abs(score) == MATE+2-i) || Repeat ()) break; if (len >= 32) { printf ("\n\t\t\t\t"); if (ofp != stdout) fprintf (ofp,"\n\t\t\t\t"); len = 0; } SANMove (pvar[i], i); printf (" %s", SANmv); if (ofp != stdout) fprintf (ofp," %s", SANmv); MakeMove (board.side, &pvar[i]); TreePtr[i+2] = TreePtr[i+1]; GenMoves (++i); len += strlen (SANmv); } } printf ("\n"); if (ofp != stdout) fprintf(ofp,"\n"); for (--i; i; i--) UnmakeMove (board.side, &pvar[i]); fflush (stdout); if (ofp != stdout) fflush (ofp); }
void InputCmd () /************************************************************************* * * This is the main user command interface driver. * *************************************************************************/ { const char *color[2] = { "White", "Black" }; int suffix; int i; leaf *ptr; int ncmds; char *x,*trim; CLEAR (flags, THINK); memset(userinput,0,sizeof(userinput)); memset(cmd,0,sizeof(cmd)); #ifndef HAVE_LIBREADLINE /* Why is this necessary anyway? */ memset(inputstr,0,sizeof(inputstr)); #endif #ifdef HAVE_LIBREADLINE if (isatty(STDIN_FILENO)) { sprintf(s,"%s (%d) %c ", color[board.side], (GameCnt+1)/2 + 1, prompt); inputstr = readline(s); if (inputstr == NULL) return; if (*inputstr) { add_history(inputstr); } if (strlen(inputstr) > INPUT_SIZE-1) { printf("Warning: Input line truncated to %d characters.\n", INPUT_SIZE -1 ); inputstr[INPUT_SIZE-1] = '\000'; } } else { inputstr = malloc(INPUT_SIZE); if (inputstr == NULL) { perror("InputCmd"); exit(EXIT_FAILURE); } fgets(inputstr, INPUT_SIZE, stdin); if (inputstr[0]) { inputstr[strlen(inputstr)-1] = 0; } } #else /* !HAVE_LIBREADLINE */ if (!(flags & XBOARD)) { printf ("%s (%d) %c ", color[board.side], (GameCnt+1)/2 + 1, prompt); fflush(stdout); } fgets (inputstr, INPUT_SIZE, stdin) ; #endif /* HAVE_LIBREADLINE */ cmd[0] = '\n'; strcpy(userinput,inputstr); sscanf (inputstr, "%s %[^\n]", cmd, inputstr); if (cmd[0] == '\n') goto done; cmd[0] = subcmd[0] = setting[0] = subsetting[0] = '\0'; ncmds = sscanf (userinput,"%s %s %s %[^\n]", cmd,subcmd,setting,subsetting); /* Put options after command back in inputstr - messy */ sprintf(inputstr,"%s %s %s",subcmd,setting,subsetting); trim = inputstr + strlen(inputstr) - 1; while ( trim>=inputstr && *trim==' ') *trim--='\0'; if (strcmp (cmd, "quit") == 0 || strcmp (cmd, "exit") == 0) SET (flags, QUIT); else if (strcmp (cmd, "help") == 0) ShowHelp (inputstr); else if (strcmp (cmd, "show") == 0) ShowCmd (inputstr); else if (strncmp (cmd, "book", 4) == 0) { if (strncmp(inputstr, "add",3) == 0) { sscanf (inputstr, "add %s", file); if (access(file,F_OK) < 0) { printf("The syntax to add a new book is:\n\n\tbook add file.pgn\n"); } else { BookPGNReadFromFile (file); } } else if (strncmp (inputstr, "on", 2) == 0 || strncmp (inputstr, "prefer", 6) == 0) { bookmode = BOOKPREFER; printf("book now on.\n"); } else if (strncmp (inputstr, "off", 3) == 0) { bookmode = BOOKOFF; printf("book now off.\n"); } else if (strncmp (inputstr, "best", 4) == 0) { bookmode = BOOKBEST; printf("book now best.\n"); } else if (strncmp (inputstr, "worst", 5) == 0) { bookmode = BOOKWORST; printf("book now worst.\n"); } else if (strncmp (inputstr, "random", 6) == 0) { bookmode = BOOKRAND; printf("book now random.\n"); } } else if (strcmp (cmd, "test") == 0) TestCmd (inputstr); else if (strcmp (cmd, "version") == 0) ShowVersion (); else if (strcmp (cmd, "pgnsave") == 0) { if ( strlen(inputstr) > 0 && strlen(inputstr) < INPUT_SIZE ) PGNSaveToFile (inputstr,""); else printf("Invalid filename.\n"); } else if (strcmp (cmd, "pgnload") == 0) PGNReadFromFile (inputstr); else if (strcmp (cmd, "manual") == 0) SET (flags, MANUAL); else if (strcmp (cmd, "debug") == 0) { SET (flags, DEBUGG); Debugmvl = 0; if (strcmp (inputstr, "debug") == 0) { while (strcmp (inputstr, s)) { sscanf (inputstr, "%s %[^\n]", s, inputstr); ptr = ValidateMove (s); Debugmv[Debugmvl++] = ptr->move; MakeMove (board.side, &ptr->move); } i = Debugmvl; while (i) { UnmakeMove (board.side, &Debugmv[--i]); } } } else if (strcmp (cmd, "force") == 0) SET (flags, MANUAL); else if (strcmp (cmd, "white") == 0) ; else if (strcmp (cmd, "black") == 0) ; else if (strcmp (cmd, "hard") == 0) ; else if (strcmp (cmd, "easy") == 0) ; else if (strcmp (cmd, "list") == 0) { if (inputstr[0] == '?') { printf("name - list known players alphabetically\n"); printf("score - list by GNU best result first \n"); printf("reverse - list by GNU worst result first\n"); } else { sscanf (inputstr, "%s %[^\n]", cmd, inputstr); if (inputstr == '\000') DBListPlayer("rscore"); else DBListPlayer(inputstr); } } else if (strcmp (cmd, "post") == 0) SET (flags, POST); else if (strcmp (cmd, "nopost") == 0) CLEAR (flags, POST); else if (strcmp (cmd, "name") == 0) { strcpy(name, inputstr); x = name; while (*x != '\000') { if (*x == ' ') { *x = '\000'; break; } x++; } suffix = 0; for (;;) { sprintf(logfile,"log.%03d",suffix); sprintf(gamefile,"game.%03d",suffix); if (access(logfile,F_OK) < 0) { ofp = fopen(logfile,"w"); break; } else suffix++; } } else if (strcmp (cmd, "result") == 0) { if (ofp != stdout) { fprintf(ofp, "result: %s\n",inputstr); fclose(ofp); ofp = stdout; printf("Save to %s\n",gamefile); PGNSaveToFile (gamefile, inputstr); DBUpdatePlayer (name, inputstr); } } else if (strcmp (cmd, "rating") == 0) { sscanf(inputstr,"%d %d",&myrating,&opprating); fprintf(ofp,"my rating = %d, opponent rating = %d\n",myrating,opprating); /* Change randomness of book based on opponent rating. */ /* Basically we play narrower book the higher the opponent */ if (opprating >= 1700) bookfirstlast = 2; else if (opprating >= 1700) bookfirstlast = 2; else bookfirstlast = 2; } else if (strcmp (cmd, "activate") == 0) { CLEAR (flags, TIMEOUT); CLEAR (flags, ENDED); } else if (strcmp (cmd, "new") == 0) { InitVars (); NewPosition (); CLEAR (flags, MANUAL); CLEAR (flags, THINK); myrating = opprating = 0; } else if (strcmp (cmd, "time") == 0) { sscanf (inputstr, "%s %[^\n]", s, inputstr); TimeLimit[1^board.side] = atoi(s) / 100.0f ; } else if (strcmp (cmd, "otim") == 0) ; else if (strcmp (cmd, "random") == 0) ; else if (strcmp (cmd, "hash") == 0) { sscanf (inputstr, "%s %[^\n]", cmd, inputstr); if (strcmp (cmd, "off") == 0) CLEAR (flags, USEHASH); else if (strcmp (cmd, "on") == 0) SET (flags, USEHASH); printf ("Hashing %s\n", flags & USEHASH ? "on" : "off"); } else if (strcmp (cmd, "hashsize") == 0) { if (inputstr[0] == 0) { printf("Current HashSize is %u slots\n", HashSize); } else { i = atoi (inputstr); TTHashMask = 0; while ((i >>= 1) > 0) { TTHashMask <<= 1; TTHashMask |= 1; } HashSize = TTHashMask + 1; printf ("Adjusting HashSize to %u slots\n", HashSize); InitHashTable (); } } else if (strcmp (cmd, "null") == 0)