char *PrMove(const int move) { static char MvStr[6]; int ff = FilesBrd[FROMSQ(move)]; int rf = RanksBrd[FROMSQ(move)]; int ft = FilesBrd[TOSQ(move)]; int rt = RanksBrd[TOSQ(move)]; int promoted = PROMOTED(move); if (promoted) { char pchar = 'q'; if (IsKn(promoted)) { pchar = 'n'; } else if (IsRQ(promoted) && !IsBQ(promoted)) { pchar = 'r'; } else if (!IsRQ(promoted) && IsBQ(promoted)) { pchar = 'b'; } sprintf(MvStr, "%c%c%c%c%c", ('a' + ff), ('1' + rf), ('a' + ft), ('1' + rt), pchar); } else { sprintf(MvStr, "%c%c%c%c", ('a' + ff), ('1' + rf), ('a' + ft), ('1' + rt)); } return MvStr; }
static void addcapturemove(const S_BOARD *pos,int move, S_MOVELIST *list) { ASSERT(sqonboard(FROMSQ(move))); ASSERT(sqonboard(TOSQ(move))); ASSERT(piecevalid(CAPTURED(move))); list->moves[list->count].move = move; list->moves[list->count].score = MvvLvaScores[CAPTURED(move)][pos->pieces[FROMSQ(move)]]+1000000; list->count++; }
static void AddCaptureMove(const CHESS_BOARD *pos, int move, MOVELIST *list) { ASSERT(SqOnBoard(FROMSQ(move))); ASSERT(SqOnBoard(TOSQ(move))); ASSERT(PieceValid(CAPTURED(move))); ASSERT(CheckBoard(pos)); list->moves[list->count].move = move; list->moves[list->count].score = MvvLvaScores[CAPTURED(move)][pos->pieces[FROMSQ(move)]] + 1000000; list->count++; }
void SortCaptures (int ply) /*************************************************************************** * * Actually no sorting is done. Just scores are assigned to the captures. * When we need a move, we will select the highest score move from the * list, place it at the top and try it. This move might give us a cut * in which case, there is no reason to sort. * ***************************************************************************/ { leaf *p; int temp, f, t; for (p = TreePtr[ply]; p < TreePtr[ply+1]; p++) { f = Value[cboard[FROMSQ(p->move)]]; t = Value[cboard[TOSQ(p->move)]]; if (f < t) p->score = t - f; else { temp = SwapOff (p->move); p->score = (temp < 0 ? -INFINITY : temp); } } }
static void addenpassantmove(const S_BOARD *pos,int move, S_MOVELIST *list) { ASSERT(sqonboard(FROMSQ(move))); ASSERT(sqonboard(TOSQ(move))); list->moves[list->count].move = move; list->moves[list->count].score = 105+1000000; list->count++; }
static void AddQuietMove(const CHESS_BOARD *pos, int move, MOVELIST *list) { ASSERT(SqOnBoard(FROMSQ(move))); ASSERT(SqOnBoard(TOSQ(move))); ASSERT(CheckBoard(pos)); ASSERT(pos->ply >= 0 && pos->ply < MAXDEPTH); list->moves[list->count].move = move; if (pos->searchKillers[0][pos->ply] == move) { list->moves[list->count].score = 900000; } else if (pos->searchKillers[1][pos->ply] == move) { list->moves[list->count].score = 800000; } else { list->moves[list->count].score = pos->searchHistory[pos->pieces[FROMSQ(move)]][TOSQ(move)]; } list->count++; }
static void AddEnPassantMove(const CHESS_BOARD *pos, int move, MOVELIST *list) { ASSERT(SqOnBoard(FROMSQ(move))); ASSERT(SqOnBoard(TOSQ(move))); ASSERT(CheckBoard(pos)); ASSERT((RanksBrd[TOSQ(move)] == RANK_6 && pos->side == WHITE) || (RanksBrd[TOSQ(move)] == RANK_3 && pos->side == BLACK)); list->moves[list->count].move = move; list->moves[list->count].score = 105 + 1000000; list->count++; }
/* * Extracts a command from the engine input buffer. * * The command is removed from the buffer. * If the command is a move, the move is made. */ void NextEngineCmd( void ) { char engineinput[BUF_SIZE]=""; char enginemovestr[BUF_SIZE]=""; leaf* enginemove; if ( strlen( engineinputbuf ) > 0 ) { if ( GetNextLine( engineinputbuf, engineinput ) > 0 ) { dbg_printf("< ENGINE: %s\n", engineinput); if ( strncmp( engineinput, "move", 4 ) == 0 ) { /* Input from engine is a move */ sscanf( engineinput, "move %s", enginemovestr ); enginemove = ValidateMove( enginemovestr ); if ( enginemove == (leaf *) NULL ) { dbg_printf( "Bad move from engine\n" ); } else { dbg_printf( "Engine move: <%s> (%d,%d)\n", enginemovestr, (enginemove!=NULL ? enginemove->move : -1), (enginemove!=NULL ? enginemove->score : -1) ); SANMove (enginemove->move, 1); MakeMove( board.side, &(enginemove->move) ); strcpy (Game[GameCnt].SANmv, SANmv); if ( !(flags & XBOARD) ) { //ShowBoard(); dbg_printf("USER <: My move is : %s\n", SANmv); //printf( "\nMy move is : %s\n", SANmv ); printf( "\n<%i:%i>\n", FROMSQ(enginemove->move), TOSQ(enginemove->move) ); fflush( stdout ); } else { dbg_printf("USER <: %d. ... %s\n", GameCnt/2 + 1, enginemovestr ); printf ("%d. ... %s\n", GameCnt/2 + 1, enginemovestr ); fflush( stdout ); dbg_printf("USER <: My move is : %s\n", enginemovestr); printf( "My move is : %s\n", enginemovestr ); fflush( stdout ); } RealGameCnt = GameCnt; showprompt = 1; /* Check if the color must be changed, e.g. after a go command. */ if ( changeColor ) { RealGameCnt = GameCnt; RealSide = board.side; } } } else { dbg_printf( "USER <: %s\n",engineinput ); printf( "%s", engineinput ); if ( flags & XBOARD ) { fflush( stdout ); } } } } }
static void AddQuietMove(const S_BOARD *pos, int move, S_MOVELIST *list) { list->moves[list->count].move = move; if(pos->searchKillers[0][pos->ply] == move) { list->moves[list->count].score = 900000; } else if(pos->searchKillers[1][pos->ply] == move) { list->moves[list->count].score = 800000; } else { list->moves[list->count].score = pos->searchHistory[pos->pieces[FROMSQ(move)]][TOSQ(move)]; } list->count++; }
static void addquietmove(const S_BOARD *pos,int move, S_MOVELIST *list) { ASSERT(sqonboard(FROMSQ(move))); ASSERT(sqonboard(TOSQ(move))); list->moves[list->count].move = move; if(pos->searchkillers[0][pos->ply]==move) { list->moves[list->count].score = 900000; } else if(pos->searchkillers[1][pos->ply] == move) { list->moves[list->count].score = 800000; } else { list->moves[list->count].score = pos->searchhistory[pos->pieces[FROMSQ(move)]][TOSQ(move)]; } list->count++; }
void SortRoot (void) /***************************************************************************** * * Sort the moves at the root. The heuristic is simple. Try captures/ * promotions first. Other moves are ordered based on their swapoff values. * *****************************************************************************/ { leaf *p; int f, t ; int side, xside; BitBoard enemyP; side = board.side; xside = 1^side; enemyP = board.b[xside][pawn]; for (p = TreePtr[1]; p < TreePtr[2]; p++) { f = Value[cboard[FROMSQ(p->move)]]; if (cboard[TOSQ(p->move)] != 0 || (p->move & PROMOTION)) { t = Value[cboard[TOSQ(p->move)]]; if (f < t) p->score = -1000 + t - f; else p->score = -1000 + SwapOff (p->move); } else p->score = -3000 + SwapOff (p->move); p->score += taxicab[FROMSQ(p->move)][D5] - taxicab[TOSQ(p->move)][E4]; if ( f == ValueP ) { /* Look at pushing Passed pawns first */ if ( (enemyP & PassedPawnMask[side][TOSQ(p->move)]) == NULLBITBOARD ) p->score +=50; } } }
int ParseMove(char *ptrChar, CHESS_BOARD *pos) { ASSERT(CheckBoard(pos)); if (ptrChar[1] > '8' || ptrChar[1] < '1') return NOMOVE; if (ptrChar[3] > '8' || ptrChar[3] < '1') return NOMOVE; if (ptrChar[0] > 'h' || ptrChar[0] < 'a') return NOMOVE; if (ptrChar[2] > 'h' || ptrChar[2] < 'a') return NOMOVE; int from = FR2SQ(ptrChar[0] - 'a', ptrChar[1] - '1'); int to = FR2SQ(ptrChar[2] - 'a', ptrChar[3] - '1'); ASSERT(SqOnBoard(from) && SqOnBoard(to)); MOVELIST list[1]; GenerateAllMoves(pos, list); int MoveNum = 0; int Move = 0; int PromPce = EMPTY; for (MoveNum = 0; MoveNum < list->count; ++MoveNum) { Move = list->moves[MoveNum].move; if (FROMSQ(Move) == from && TOSQ(Move) == to) { PromPce = PROMOTED(Move); if (PromPce != EMPTY) { if (IsRQ(PromPce) && !IsBQ(PromPce) && ptrChar[4] == 'r') { return Move; } else if (!IsRQ(PromPce) && IsBQ(PromPce) && ptrChar[4] == 'b') { return Move; } else if (IsRQ(PromPce) && IsBQ(PromPce) && ptrChar[4] == 'q') { return Move; } else if (IsKn(PromPce) && ptrChar[4] == 'n') { return Move; } continue; } return Move; } } return NOMOVE; }
inline const char *printMove(const unsigned move) { static char result[6]; const unsigned from = FROMSQ(move); const unsigned to = TOSQ(move); ASSERT(squareOnBoard(to)); if (move & MFLAGDROP) { result[0] = pieceChar[PROMOTED(move) % 6]; result[1] = '@'; result[2] = 'a' + fileBoard[to]; result[3] = '1' + rankBoard[to]; result[4] = 0; } else { ASSERT(squareOnBoard(from)); result[0] = 'a' + fileBoard[from]; result[1] = '1' + rankBoard[from]; result[2] = 'a' + fileBoard[to]; result[3] = '1' + rankBoard[to]; result[4] = PROMOTED(move) ? "__nbrq__nbrq_"[PROMOTED(move)] : 0; result[5] = 0; } return result; }
int MoveListOk(const S_MOVELIST *list, const S_BOARD *pos) { if(list->count < 0 || list->count >= MAXPOSITIONMOVES) { return FALSE; } int MoveNum; int from = 0; int to = 0; for(MoveNum = 0; MoveNum < list->count; ++MoveNum) { to = TOSQ(list->moves[MoveNum].move); from = FROMSQ(list->moves[MoveNum].move); if(!SqOnBoard(to) || !SqOnBoard(from)) { return FALSE; } if(!PieceValid(pos->pieces[from])) { PrintBoard(pos); return FALSE; } } return TRUE; }
void SortMoves (int ply) /***************************************************************************** * * Sort criteria is as follows. * 1. The move from the hash table * 2. Captures as above. * 3. Killers. * 4. History. * 5. Moves to the centre. * *****************************************************************************/ { leaf *p; int f, t, m, tovalue; int side, xside; BitBoard enemyP; side = board.side; xside = 1^side; enemyP = board.b[xside][pawn]; for (p = TreePtr[ply]; p < TreePtr[ply+1]; p++) { p->score = -INFINITY; f = FROMSQ (p->move); t = TOSQ (p->move); m = p->move & MOVEMASK; /* Hash table move (highest score) */ if (m == Hashmv[ply]) p->score += HASHSORTSCORE; else if (cboard[t] != 0 || p->move & PROMOTION) { /* ***** SRW My Interpretation of this code ************* * Captures normally in other places but..... * * * * On capture we generally prefer to capture with the * * with the lowest value piece so to chose between * * pieces we should subtract the piece value .... but * * * * The original code was looking at some captures * * last, especially where the piece was worth more * * than the piece captured - KP v K in endgame.epd * * * * So code modified to prefer any capture by adding * * ValueK * ****************************************************** */ tovalue = (Value[cboard[t]] + Value[PROMOTEPIECE (p->move)]); p->score += tovalue + ValueK - Value[cboard[f]]; } /* Killers */ else if (m == killer1[ply] || m == killer2[ply]) p->score += KILLERSORTSCORE; else if (ply > 2 && (m == killer1[ply-2] || m == killer2[ply-2])) p->score += KILLERSORTSCORE; p->score += history[side][(p->move & 0x0FFF)] + taxicab[f][D5] - taxicab[t][E4]; if ( cboard[f] == pawn ) { /* Look at pushing Passed pawns first */ if ( (enemyP & PassedPawnMask[side][t]) == NULLBITBOARD ) p->score +=50; } } }
int PhasePick (leaf **p1, int ply) /*************************************************************************** * * A phase style routine which returns the next move to the search. * Hash move is first returned. If it doesn't fail high, captures are * generated, sorted and tried. If no fail high still occur, the rest of * the moves are generated and tried. * The idea behind all this is to save time generating moves which might * not be needed. * CAVEAT: To implement this, the way that genmoves & friends are called * have to be modified. In particular, TreePtr[ply+1] = TreePtr[ply] must * be set before the calls can be made. * If the board ever gets corrupted during the search, then there is a bug * in IsLegalMove() which has to be fixed. * ***************************************************************************/ { static leaf* p[MAXPLYDEPTH]; leaf *p2; int mv; int side; side = board.side; switch (pickphase[ply]) { case PICKHASH: TreePtr[ply+1] = TreePtr[ply]; pickphase[ply] = PICKGEN1; if (Hashmv[ply] && IsLegalMove (Hashmv[ply])) { TreePtr[ply+1]->move = Hashmv[ply]; *p1 = TreePtr[ply+1]++; return (true); } case PICKGEN1: pickphase[ply] = PICKCAPT; p[ply] = TreePtr[ply+1]; GenCaptures (ply); for (p2 = p[ply]; p2 < TreePtr[ply+1]; p2++) p2->score = SwapOff(p2->move) * WEIGHT + Value[cboard[TOSQ(p2->move)]]; case PICKCAPT: while (p[ply] < TreePtr[ply+1]) { pick (p[ply], ply); if ((p[ply]->move & MOVEMASK) == Hashmv[ply]) { p[ply]++; continue; } *p1 = p[ply]++; return (true); } case PICKKILL1: pickphase[ply] = PICKKILL2; if (killer1[ply] && killer1[ply] != Hashmv[ply] && IsLegalMove (killer1[ply])) { TreePtr[ply+1]->move = killer1[ply]; *p1 = TreePtr[ply+1]; TreePtr[ply+1]++; return (true); } case PICKKILL2: pickphase[ply] = PICKGEN2; if (killer2[ply] && killer2[ply] != Hashmv[ply] && IsLegalMove (killer2[ply])) { TreePtr[ply+1]->move = killer2[ply]; *p1 = TreePtr[ply+1]; TreePtr[ply+1]++; return (true); } case PICKGEN2: pickphase[ply] = PICKREST; p[ply] = TreePtr[ply+1]; GenNonCaptures (ply); for (p2 = p[ply]; p2 < TreePtr[ply+1]; p2++) { p2->score = history[side][(p2->move & 0x0FFF)] + taxicab[FROMSQ(p2->move)][D5] - taxicab[TOSQ(p2->move)][E4]; if (p2->move & CASTLING) p2->score += CASTLINGSCORE; } case PICKREST: while (p[ply] < TreePtr[ply+1]) { pick (p[ply], ply); mv = p[ply]->move & MOVEMASK; if (mv == Hashmv[ply] || mv == killer1[ply] || mv == killer2[ply]) { p[ply]++; continue; } *p1 = p[ply]++; return (true); } } return (false); }
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); }
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); }
int SwapOff (int move) /**************************************************************************** * * A Static Exchange Evaluator (or SEE for short). * First determine the target square. Create a bitboard of all squares * attacking the target square for both sides. Using these 2 bitboards, * we take turn making captures from smallest piece to largest piece. * When a sliding piece makes a capture, we check behind it to see if * another attacker piece has been exposed. If so, add this to the bitboard * as well. When performing the "captures", we stop if one side is ahead * and doesn't need to capture, a form of pseudo-minimaxing. * ****************************************************************************/ { int f, t, sq, piece, lastval; int side, xside; int swaplist[MAXPLYDEPTH], n; BitBoard b, c, *d, *e, r; f = FROMSQ (move); t = TOSQ (move); side = ((board.friends[white] & BitPosArray[f]) ? white : black); xside = 1^side; /* Squares attacking t for side and xside */ b = AttackTo (t, side); c = AttackTo (t, xside); CLEARBIT(b, f); if (xray[cboard[f]]) AddXrayPiece (t, f, side, &b, &c); d = board.b[side]; e = board.b[xside]; if (move & PROMOTION) { swaplist[0] = Value[PROMOTEPIECE (move)] - ValueP; lastval = -Value[PROMOTEPIECE (move)]; } else { swaplist[0] = (move & ENPASSANT ? ValueP : Value[cboard[t]]); lastval = -Value[cboard[f]]; } n = 1; while (1) { if (c == NULLBITBOARD) break; for (piece = pawn; piece <= king; piece++) { r = c & e[piece]; if (r) { sq = leadz (r); CLEARBIT (c, sq); if (xray[piece]) AddXrayPiece (t, sq, xside, &c, &b); swaplist[n] = swaplist[n-1] + lastval; n++; lastval = Value[piece]; break; } } if (b == NULLBITBOARD) break; for (piece = pawn; piece <= king; piece++) { r = b & d[piece]; if (r) { sq = leadz (r); CLEARBIT (b, sq); if (xray[piece]) AddXrayPiece (t, sq, side, &b, &c); swaplist[n] = swaplist[n-1] + lastval; n++; lastval = -Value[piece]; break; } } } /**************************************************************************** * * At this stage, we have the swap scores in a list. We just need to * mini-max the scores from the bottom up to the top of the list. * ****************************************************************************/ --n; while (n) { if (n & 1) { if (swaplist[n] <= swaplist[n-1]) swaplist[n-1] = swaplist[n]; } else { if (swaplist[n] >= swaplist[n-1]) swaplist[n-1] = swaplist[n]; } --n; } return (swaplist[0]); }
static void AddCaptureMove(const S_BOARD *pos, int move, S_MOVELIST *list) { list->moves[list->count].move = move; list->moves[list->count].score = MvvLvaScores[CAPTURED(move)][pos->pieces[FROMSQ(move)]] + 1000000; list->count++; }