void SSD_Ddm::Decrement_Requests_Outstanding() { // Decrement the number of requests outstanding. CT_ASSERT(m_num_requests_outstanding, Respond_To_BSA_Request); m_num_requests_outstanding--; TRACEF(TRACE_L5, ("\nDecrement_Requests_Outstanding: requests outstanding = %d after decrement", m_num_requests_outstanding)); // Are there any requests outstanding? if (m_num_requests_outstanding == 0) { // Are we waiting to process a format request? if (m_p_format_message) { SSD_Ddm::Process_Format_Request(m_p_format_message); return; } // Are we waiting to process a quiesce request? if (m_p_quiesce_message) { Quiesce(m_p_quiesce_message); return; } } } // Decrement_Requests_Outstanding
int QuiesceChecks(POS *p, int ply, int alpha, int beta, int *pv) { int stand_pat, best, score, move, new_pv[MAX_PLY]; int is_pv = (beta > alpha + 1); MOVES m[1]; UNDO u[1]; if (InCheck(p)) return QuiesceFlee(p, ply, alpha, beta, pv); nodes++; CheckTimeout(); if (abort_search) return 0; *pv = 0; if (IsDraw(p)) return DrawScore(p); if (ply >= MAX_PLY - 1) return Eval.Return(p, 1); best = stand_pat = Eval.Return(p, 1); if (best >= beta) return best; if (best > alpha) alpha = best; if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, 0, ply)) return score; InitCaptures(p, m); while ((move = NextCaptureOrCheck(m))) { p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } score = -Quiesce(p, ply + 1, -beta, -alpha, new_pv); p->UndoMove(move, u); if (abort_search) return 0; if (score >= beta) { TransStore(p->hash_key, move, score, LOWER, 0, ply); return score; } if (score > best) { best = score; if (score > alpha) { alpha = score; BuildPv(pv, new_pv, move); } } } if (*pv) TransStore(p->hash_key, *pv, best, EXACT, 0, ply); else TransStore(p->hash_key, 0, best, UPPER, 0, ply); return best; }
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); }
/* ******************************************************************************** * * * Search() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Search() is * * called whenever there is "depth" remaining so that all moves are subject * * to searching, or when the side to move is in check, to make sure that this * * side isn't mated. Search() recursively calls itself until depth is ex- * * hausted, at which time it calls Quiesce() instead. * * * ******************************************************************************** */ int Search(int alpha, int beta, int wtm, int depth, int ply, int do_null) { register int first_move=1; register BITBOARD save_hash_key; register int initial_alpha, value; register int extensions; /* ---------------------------------------------------------- | | | check to see if we have searched enough nodes that it | | is time to peek at how much time has been used, or if | | is time to check for operator keyboard input. this is | | usually enough nodes to force a time/input check about | | once per second, except when the target time per move | | is very small, in which case we try to check the time | | at least 10 times during the search. | | | ---------------------------------------------------------- */ if (ply >= MAXPLY-2) return(beta); nodes_searched++; if (--next_time_check <= 0) { next_time_check=nodes_between_time_checks; if (CheckInput()) Interrupt(ply); time_abort+=TimeCheck(0); if (time_abort) { abort_search=1; return(0); } } /* ---------------------------------------------------------- | | | check for draw by repetition. | | | ---------------------------------------------------------- */ if (RepetitionCheck(ply,wtm)) { value=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if (value < beta) SavePV(ply,value,0); #if !defined(FAST) if(ply <= trace_level) printf("draw by repetition detected, ply=%d.\n",ply); #endif return(value); } /* ---------------------------------------------------------- | | | now call LookUp() to see if this position has been | | searched before. if so, we may get a real score, | | produce a cutoff, or get nothing more than a good move | | to try first. there are four cases to handle: | | | | 1. LookUp() returned "EXACT_SCORE" if this score is | | greater than beta, return beta. otherwise, return the | | score. In either case, no further searching is needed | | from this position. note that lookup verified that | | the table position has sufficient "draft" to meet the | | requirements of the current search depth remaining. | | | | 2. LookUp() returned "LOWER_BOUND" which means that | | when this position was searched previously, every move | | was "refuted" by one of its descendents. as a result, | | when the search was completed, we returned alpha at | | that point. we simply return alpha here as well. | | | | 3. LookUp() returned "UPPER_BOUND" which means that | | when we encountered this position before, we searched | | one branch (probably) which promptly refuted the move | | at the previous ply. | | | | 4. LookUp() returned "AVOID_NULL_MOVE" which means | | the hashed score/bound was no good, but it indicated | | that trying a null-move in this position will be a | | waste of time. | | | ---------------------------------------------------------- */ switch (LookUp(ply,depth,wtm,&alpha,beta)) { case EXACT_SCORE: if(alpha >= beta) return(beta); else { SavePV(ply,alpha,1); return(alpha); } case LOWER_BOUND: return(alpha); case UPPER_BOUND: return(beta); case AVOID_NULL_MOVE: do_null=0; } /* ---------------------------------------------------------- | | | now it's time to try a probe into the endgame table- | | base files. this is done if (a) the previous move was | | a capture or promotion, unless we are at very shallow | | plies (<4) in the search; (b) there are less than 5 | | pieces left (currently all interesting 4 piece endings | | are available.) | | | ---------------------------------------------------------- */ #if defined(TABLEBASES) if (TotalPieces < 5) do { register int wpawn, bpawn; int tb_value; if (TotalWhitePawns && TotalBlackPawns) { wpawn=FirstOne(WhitePawns); bpawn=FirstOne(BlackPawns); if (FileDistance(wpawn,bpawn) == 1) { if(((Rank(wpawn)==1) && (Rank(bpawn)>2)) || ((Rank(bpawn)==6) && (Rank(wpawn)<5)) || EnPassant(ply)) break; } } tb_probes++; if (EGTBScore(ply, wtm, &tb_value)) { tb_probes_successful++; alpha=tb_value; if (abs(alpha) > MATE-100) alpha+=(alpha > 0) ? -(ply-1) : +(ply-1); else if (alpha == 0) alpha=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if(alpha >= beta) return(beta); else { SavePV(ply,alpha,2); return(alpha); } } } while(0); # endif /* ---------------------------------------------------------- | | | initialize. | | | ---------------------------------------------------------- */ in_check[ply+1]=0; extended_reason[ply+1]=no_extension; initial_alpha=alpha; last[ply]=last[ply-1]; killer_count1[ply+1]=0; killer_count2[ply+1]=0; /* ---------------------------------------------------------- | | | first, we try a null move to see if we can get a quick | | cutoff with only a little work. this operates as | | follows. instead of making a legal move, the side on | | move 'passes' and does nothing. the resulting position | | is searched to a shallower depth than normal (usually | | one ply less but settable by the operator) this should | | result in a cutoff or at least should set the lower | | bound better since anything should be better than not | | doing anything. | | | | this is skipped for any of the following reasons: | | | | 1. the side on move is in check. the null move | | results in an illegal position. | | 2. no more than one null move can appear in succession | | or else the search will degenerate into nothing. | | 3. the side on move has little material left making | | zugzwang positions more likely. | | | ---------------------------------------------------------- */ # if defined(NULL_MOVE_DEPTH) if (do_null && !in_check[ply] && ((wtm) ? TotalWhitePieces : TotalBlackPieces)>2) { current_move[ply]=0; current_phase[ply]=NULL_MOVE; #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,depth,wtm,alpha,beta,"Search",0); #endif position[ply+1]=position[ply]; Rule50Moves(ply+1)++; save_hash_key=HashKey; if (EnPassant(ply)) { HashEP(EnPassant(ply+1),HashKey); EnPassant(ply+1)=0; } if ((depth-NULL_MOVE_DEPTH-INCREMENT_PLY) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth-NULL_MOVE_DEPTH-INCREMENT_PLY,ply+1,NO_NULL); else value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); HashKey=save_hash_key; if (abort_search) return(0); if (value >= beta) { StoreRefutation(ply,depth,wtm,beta); return(beta); } } # endif /* ---------------------------------------------------------- | | | if there is no best move from the hash table, and this | | is a PV node, then we need a good move to search | | first. while killers and history moves are good, they | | are not "good enough". the simplest action is to try | | a shallow search (depth-2) to get a move. note that | | when we call Search() with depth-2, it, too, will | | not have a hash move, and will therefore recursively | | continue this process, hence the name "internal | | iterative deepening." | | | ---------------------------------------------------------- */ next_status[ply].phase=FIRST_PHASE; if (hash_move[ply]==0 && (depth > 2*INCREMENT_PLY) && (((ply & 1) && alpha == root_alpha && beta == root_beta) || (!(ply & 1) && alpha == -root_beta && beta == -root_alpha))) { current_move[ply]=0; value=Search(alpha,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL); if (abort_search) return(0); if (value <= alpha) { value=Search(-MATE,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL); if (abort_search) return(0); } else if (value < beta) { if ((int) pv[ply-1].path_length >= ply) hash_move[ply]=pv[ply-1].path[ply]; } else hash_move[ply]=current_move[ply]; last[ply]=last[ply-1]; next_status[ply].phase=FIRST_PHASE; } /* ---------------------------------------------------------- | | | now iterate through the move list and search the | | resulting positions. note that Search() culls any | | move that is not legal by using Check(). the special | | case is that we must find one legal move to search to | | confirm that it's not a mate or draw. | | | ---------------------------------------------------------- */ while ((current_phase[ply]=(in_check[ply]) ? NextEvasion(ply,wtm) : NextMove(depth,ply,wtm))) { extended_reason[ply]&=check_extension; #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,depth,wtm,alpha,beta,"Search",current_phase[ply]); #endif /* ---------------------------------------------------------- | | | if two successive moves are capture / re-capture so | | that the material score is restored, extend the search | | by one ply on the re-capture since it is pretty much | | forced and easy to analyze. | | | ---------------------------------------------------------- */ extensions=-INCREMENT_PLY; if (Captured(current_move[ply]) && Captured(current_move[ply-1]) && To(current_move[ply-1]) == To(current_move[ply]) && (p_values[Captured(current_move[ply-1])+7] == p_values[Captured(current_move[ply])+7] || Promote(current_move[ply-1])) && !(extended_reason[ply-1]&recapture_extension)) { extended_reason[ply]|=recapture_extension; recapture_extensions_done++; extensions+=RECAPTURE; } /* ---------------------------------------------------------- | | | if we push a passed pawn, we need to look deeper to | | see if it is a legitimate threat. | | | ---------------------------------------------------------- */ if (Piece(current_move[ply])==pawn && !FutileAhead(wtm) && ((wtm && To(current_move[ply])>H5 && TotalBlackPieces<16 && !And(mask_pawn_passed_w[To(current_move[ply])],BlackPawns)) || (!wtm && To(current_move[ply])<A4 && TotalWhitePieces<16 && !And(mask_pawn_passed_b[To(current_move[ply])],WhitePawns)) || push_extensions[To(current_move[ply])]) && Swap(From(current_move[ply]),To(current_move[ply]),wtm) >= 0) { extended_reason[ply]|=passed_pawn_extension; passed_pawn_extensions_done++; extensions+=PASSED_PAWN_PUSH; } /* ---------------------------------------------------------- | | | now make the move and search the resulting position. | | if we are in check, the current move must be legal | | since NextEvasion ensures this, otherwise we have to | | make sure the side-on-move is not in check after the | | move to weed out illegal moves and save time. | | | ---------------------------------------------------------- */ MakeMove(ply,current_move[ply],wtm); if (in_check[ply] || !Check(wtm)) { /* ---------------------------------------------------------- | | | if the move to be made checks the opponent, then we | | need to remember that he's in check and also extend | | the depth by one ply for him to get out. | | | ---------------------------------------------------------- */ if (Check(ChangeSide(wtm))) { in_check[ply+1]=1; extended_reason[ply+1]=check_extension; check_extensions_done++; extensions+=IN_CHECK; } else { in_check[ply+1]=0; extended_reason[ply+1]=no_extension; } /* ---------------------------------------------------------- | | | now we toss in the "razoring" trick, which simply says | | if we are doing fairly badly, we can reduce the depth | | an additional ply, if there was nothing at the current | | ply that caused an extension. | | | ---------------------------------------------------------- */ if (depth < 3*INCREMENT_PLY && !in_check[ply] && extensions == -INCREMENT_PLY) { register int val=(wtm) ? Material : -Material; if (val+1500 < alpha) extensions-=INCREMENT_PLY; } /* ---------------------------------------------------------- | | | if there's only one legal move, extend the search one | | additional ply since this node is very easy to search. | | | ---------------------------------------------------------- */ if (first_move) { if (last[ply]-last[ply-1] == 1) { extended_reason[ply]|=one_reply_extension; one_reply_extensions_done++; extensions+=ONE_REPLY_TO_CHECK; } if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else { value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); } if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } first_move=0; } else { if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-alpha-1,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else { value=-Quiesce(-alpha-1,-alpha,ChangeSide(wtm),ply+1); } if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } if (value>alpha && value<beta) { if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } } } if (value > alpha) { if(value >= beta) { HistoryRefutation(ply,depth,wtm); UnMakeMove(ply,current_move[ply],wtm); StoreRefutation(ply,depth,wtm,beta); return(beta); } alpha=value; } } UnMakeMove(ply,current_move[ply],wtm); } /* ---------------------------------------------------------- | | | all moves have been searched. if none were legal, | | return either MATE or DRAW depending on whether the | | side to move is in check or not. | | | ---------------------------------------------------------- */ if (first_move == 1) { value=(Check(wtm)) ? -(MATE-ply) : ((wtm==root_wtm) ? DrawScore() : -DrawScore()); if(value > beta) value=beta; else if (value < alpha) value=alpha; if (value >=alpha && value <beta) { SavePV(ply,value,0); #if !defined(FAST) if (ply <= trace_level) printf("Search() no moves! ply=%d\n",ply); #endif } return(value); } else { if (alpha != initial_alpha) { memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply],(pv[ply].path_length-ply+1)*4); memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3); pv[ply-1].path[ply-1]=current_move[ply-1]; HistoryBest(ply,depth,wtm); } StoreBest(ply,depth,wtm,alpha,initial_alpha); /* ---------------------------------------------------------- | | | if the 50-move rule is drawing close, then adjust the | | score to reflect the impending draw. | | | ---------------------------------------------------------- */ if (Rule50Moves(ply) > 99) { value=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if (value < beta) SavePV(ply,value,0); #if !defined(FAST) if(ply <= trace_level) printf("draw by 50-move rule detected, ply=%d.\n",ply); #endif return(value); } return(alpha); } }
int CPartida::PVS(int depth, int alpha, int beta,char *Global,int doNull) { int ext_local; u64 hash; char PV[1024]; int value = 0; int InPV = beta-alpha > 1; int EsJaque,legales = 0; int fFoundPv = false; int Valor_i = 0; IncNodes(); if(cancelado) return alpha; if(DoDistancePruning) { // limite inferior value = ValorMate(-1); value += 2; if(value > alpha) { alpha = value; if(value >= beta) return value; } // limite superior value = ValorMate(1); value--; if(value < beta) { beta = value; if(value <= alpha) return value; } } hash = Taux.GetHash(); if(stHistory > 0 && HayRepeticion(hash) ) // Queremos evitar repeticiones pero no en raiz ya que no jugariamos { return 0; } if(EsJaque = Taux.EstoyEnJaque()) { depth++; } if((depth <= 0 || stHistory > MAXDEPTHC)) // extendiendo jaques agotamos la pila { NodosVisitados--; // correccion para no contarlos dos veces value = Quiesce(alpha,beta);//,Global); #ifdef _DEBUG if(value <= -MATE || value >= MATE) { value = value; } #endif assert(value > -MATE && value < MATE); return value; } /* ************************************************************ * Recuperamos el valor del cache * * * ************************************************************ */ if(UseCache) { if( depth < (Depth-depth) && !EsJaque && !InPV) // { int CV = HashJ.GetValue(hash); if(CV) { value = CV & VALUEMASK; // valor if(value > MATE) { value |= 0xffff0000; } // verificamos la profundidad en cache if((CV >> 20) >= (depth) ) { // el valor en cache significa algo if(CV & UBOUND) { if(CV & LBOUND) { if(value >= beta) return beta; if(value <= alpha) return alpha; return(value); } else { // solo UBOUND if(value <= alpha ) { return alpha; } if(value < beta) beta = value; } } else { if(value >= beta) { return beta; } if(value > alpha) { alpha = value; } } } } } } // UseCache
// buscamos capturas y jaques y analizamos las posibilidades de escape. int CPartida::Quiesce(int alpha,int beta) { int value,a; if(cancelado + (stHistory >> 6)) // si llenamos el pozo lo dejamos en tablas { return Taux.Evalua(); } IncNodesQ(); u64 hash = Taux.GetHash(); int Valor_i; Valor_i = Taux.Evalua(); // Vamos con material de sobra if(Valor_i >= beta ) { return Valor_i; } a = alpha; if(Valor_i > alpha ) a = Valor_i; value = a; alpha = a; SetHashHistory(hash); CJugada J; int legales = 0; CSort Sort; Sort.Init(Taux,true); for(J = Sort.GetNextQ();J.ToInt();J = Sort.GetNextQ()) { if(cancelado) break; if(J.desglose.captura == rey) { a = ValorMate(1); PopHistory(); return a; } if(J.desglose.peso < (PesoCaptura+PesoCapturaBuena)) continue; Move(J); if(!Taux.EsAtacada(Taux.PosReyes[Taux.ColorJuegan^1],Taux.ColorJuegan^1) ) { legales++; value = -Quiesce(-beta, -a); } TakeBack(); if(value > a) { a = value; if(a >= beta ) { break; } } } // for PopHistory(); return ( a ); }
/* ******************************************************************************** * * * Quiesce() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Quiesce() is * * called whenever there is no "depth" remaining so that only capture moves * * are searched deeper. * * * ******************************************************************************** */ int Quiesce(int alpha, int beta, int wtm, int ply) { register int initial_alpha, value, delta; register int *next_move; register int *goodmv, *movep, moves=0, *sortv, temp; /* ---------------------------------------------------------- | | | initialize. | | | ---------------------------------------------------------- */ if (ply >= MAXPLY-2) return(beta); nodes_searched++; next_time_check--; last[ply]=last[ply-1]; initial_alpha=alpha; /* ---------------------------------------------------------- | | | now call Evaluate() to produce the "stand-pat" score | | that will be returned if no capture is acceptable. | | if this score is > alpha, then we also have to save | | the "path" to this node as it is the PV that leads | | to this score. | | | ---------------------------------------------------------- */ value=Evaluate(ply,wtm,alpha,beta); if (value > alpha) { if (value >= beta) return(value); alpha=value; pv[ply].path_length=ply-1; pv[ply].path_hashed=0; pv[ply].path_iteration_depth=iteration_depth; } /* ---------------------------------------------------------- | | | generate captures and sort them based on (a) the value | | of the captured piece - the value of the capturing | | piece if this is > 0; or, (b) the value returned by | | Swap(). if the value of the captured piece won't | | bring the material score back up to near alpha, that | | capture is discarded as "futile." | | | ---------------------------------------------------------- */ last[ply]=GenerateCaptures(ply, wtm, last[ply-1]); delta=alpha-500-(wtm?Material:-Material); goodmv=last[ply-1]; sortv=sort_value; for (movep=last[ply-1];movep<last[ply];movep++) if (p_values[Captured(*movep)+7]+p_values[Promote(*movep)+7] >= delta) { if (Captured(*movep) == king) return(beta); if (p_values[Piece(*movep)+7] < p_values[Captured(*movep)+7] || (p_values[Piece(*movep)+7] <= p_values[Captured(*movep)+7] && delta<=0)) { *goodmv++=*movep; *sortv++=p_values[Captured(*movep)+7]; moves++; } else { temp=Swap(From(*movep),To(*movep),wtm); if (temp >= 0) { *sortv++=temp; *goodmv++=*movep; moves++; } } } /* ---------------------------------------------------------- | | | don't disdain the lowly bubble sort here. the list of | | captures is always short, and experiments with other | | algorithms are always slightly slower. | | | ---------------------------------------------------------- */ if (moves > 1) { register int done; register int *end=last[ply-1]+moves-1; do { done=1; sortv=sort_value; movep=last[ply-1]; for (;movep<end;movep++,sortv++) if (*sortv < *(sortv+1)) { temp=*sortv; *sortv=*(sortv+1); *(sortv+1)=temp; temp=*movep; *movep=*(movep+1); *(movep+1)=temp; done=0; } } while(!done); } next_move=last[ply-1]; /* ---------------------------------------------------------- | | | now iterate through the move list and search the | | resulting positions. | | | ---------------------------------------------------------- */ while (moves--) { current_move[ply]=*(next_move++); #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,0,wtm,alpha,beta,"quiesce",CAPTURE_MOVES); #endif MakeMove(ply,current_move[ply],wtm); value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); UnMakeMove(ply,current_move[ply],wtm); if (value > alpha) { if(value >= beta) return(value); alpha=value; } } /* ---------------------------------------------------------- | | | all moves have been searched. return the search | | result that was found. if the result is not the | | original alpha score, then we need to return the PV | | that is associated with this score. | | | ---------------------------------------------------------- */ if (alpha != initial_alpha) { memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply], (pv[ply].path_length-ply+1)*sizeof(int)); memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3); pv[ply-1].path[ply-1]=current_move[ply-1]; } return(alpha); }
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 Quiesce(POS *p, int ply, int alpha, int beta, int *pv) { int best, score, move, new_pv[MAX_PLY]; MOVES m[1]; UNDO u[1]; int op = Opp(p->side); // Statistics and attempt at quick exit if (InCheck(p)) return QuiesceFlee(p, ply, alpha, beta, pv); nodes++; CheckTimeout(); if (abort_search) return 0; *pv = 0; if (IsDraw(p)) return DrawScore(p); if (ply >= MAX_PLY - 1) return Eval.Return(p, 1); // Get a stand-pat score and adjust bounds // (exiting if eval exceeds beta) best = Eval.Return(p, 1); if (best >= beta) return best; if (best > alpha) alpha = best; #ifdef USE_QS_HASH // Transposition table read if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, 0, ply)) return score; #endif InitCaptures(p, m); // Main loop while ((move = NextCapture(m))) { // Pruning in quiescence search // (not applicable if we are capturing last enemy piece) if (p->cnt[op][N] + p->cnt[op][B] + p->cnt[op][R] + p->cnt[op][Q] > 1) { // 1. Delta pruning if (best + tp_value[TpOnSq(p, Tsq(move))] + 300 < alpha) continue; // 2. SEE-based pruning of bad captures if (BadCapture(p, move)) continue; } p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } score = -Quiesce(p, ply + 1, -beta, -alpha, new_pv); p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { #ifdef USE_QS_HASH TransStore(p->hash_key, *pv, best, LOWER, 0, ply); #endif return score; } // Adjust alpha and score if (score > best) { best = score; if (score > alpha) { alpha = score; BuildPv(pv, new_pv, move); } } } #ifdef USE_QS_HASH if (*pv) TransStore(p->hash_key, *pv, best, EXACT, 0, ply); else TransStore(p->hash_key, 0, best, UPPER, 0, ply); #endif return best; }
int QuiesceFlee(POS *p, int ply, int alpha, int beta, int *pv) { int best, score, move, new_pv[MAX_PLY]; int fl_check, mv_type; int is_pv = (beta > alpha + 1); MOVES m[1]; UNDO u[1]; // Periodically check for timeout, ponderhit or stop command nodes++; CheckTimeout(); // Quick exit on a timeout or on a statically detected draw if (abort_search) return 0; if (ply) *pv = 0; if (IsDraw(p) ) return DrawScore(p); // Retrieving data from transposition table. We hope for a cutoff // or at least for a move to improve move ordering. move = 0; if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, 0, ply)) return score; // Safeguard against exceeding ply limit if (ply >= MAX_PLY - 1) return Eval.Return(p, 1); // Are we in check? Knowing that is useful when it comes // to pruning/reduction decisions fl_check = InCheck(p); // Init moves and variables before entering main loop best = -INF; InitMoves(p, m, move, -1, ply); // Main loop while ((move = NextMove(m, &mv_type))) { p->DoMove(move, u); if (Illegal(p)) { p->UndoMove(move, u); continue; } score = -Quiesce(p, ply, -beta, -alpha, new_pv); p->UndoMove(move, u); if (abort_search) return 0; // Beta cutoff if (score >= beta) { TransStore(p->hash_key, move, score, LOWER, 0, ply); return score; } // Updating score and alpha if (score > best) { best = score; if (score > alpha) { alpha = score; BuildPv(pv, new_pv, move); } } } // end of the main loop // Return correct checkmate/stalemate score if (best == -INF) return InCheck(p) ? -MATE + ply : DrawScore(p); // Save score in the transposition table if (*pv) TransStore(p->hash_key, *pv, best, EXACT, 0, ply); else TransStore(p->hash_key, 0, best, UPPER, 0, ply); return best; }