// UCCI支持 - 输出叶子结点的局面信息 void PopLeaf(PositionStruct &pos) { int vl; Search2.nAllNodes = 0; vl = SearchQuiesc(pos, -MATE_VALUE, MATE_VALUE); printf("pophash lowerbound %d depth 0 upperbound %d depth 0\n", vl, vl); fflush(stdout); }
// 主搜索例程 void SearchMain(int maxNode) { int i, vl, vlLast, nDraw; int nCurrTimer, nLimitTimer, nLimitNodes; bool bUnique; #ifndef CCHESS_A3800 int nBookMoves; uint32_t dwMoveStr; BookStruct bks[MAX_GEN_MOVES]; #endif // 主搜索例程包括以下几个步骤: // 1. 遇到和棋则直接返回 if (Search.pos.IsDraw() || Search.pos.RepStatus(3) > 0) { #ifndef CCHESS_A3800 printf("nobestmove\n"); fflush(stdout); #endif return; } // 3. 如果深度为零则返回静态搜索值 if (maxNode == 0) { #ifndef CCHESS_A3800 printf("info depth 0 score %d\n", SearchQuiesc(Search.pos, -MATE_VALUE, MATE_VALUE)); fflush(stdout); printf("nobestmove\n"); fflush(stdout); #endif return; } MonteCarlo(maxNode); }
// 主搜索例程 void SearchMain(int nDepth) { int i, vl, vlLast, nDraw; int nCurrTimer, nLimitTimer, nLimitNodes; bool bUnique; #ifndef CCHESS_A3800 int nBookMoves; uint32_t dwMoveStr; BookStruct bks[MAX_GEN_MOVES]; #endif // 主搜索例程包括以下几个步骤: // 1. 遇到和棋则直接返回 if (Search.pos.IsDraw() || Search.pos.RepStatus(3) > 0) { #ifndef CCHESS_A3800 printf("nobestmove\n"); fflush(stdout); #endif return; } #ifndef CCHESS_A3800 // 2. 从开局库中搜索着法 if (Search.bUseBook) { // a. 获取开局库中的所有走法 nBookMoves = GetBookMoves(Search.pos, Search.szBookFile, bks); if (nBookMoves > 0) { vl = 0; for (i = 0; i < nBookMoves; i ++) { vl += bks[i].wvl; dwMoveStr = MOVE_COORD(bks[i].wmv); printf("info depth 0 score %d pv %.4s\n", bks[i].wvl, (const char *) &dwMoveStr); fflush(stdout); } // b. 根据权重随机选择一个走法 vl = Search.rc4Random.NextLong() % (uint32_t) vl; for (i = 0; i < nBookMoves; i ++) { vl -= bks[i].wvl; if (vl < 0) { break; } } __ASSERT(vl < 0); __ASSERT(i < nBookMoves); // c. 如果开局库中的着法够成循环局面,那么不走这个着法 Search.pos.MakeMove(bks[i].wmv); if (Search.pos.RepStatus(3) == 0) { dwMoveStr = MOVE_COORD(bks[i].wmv); printf("bestmove %.4s", (const char *) &dwMoveStr); // d. 给出后台思考的着法(开局库中第一个即权重最大的后续着法) nBookMoves = GetBookMoves(Search.pos, Search.szBookFile, bks); Search.pos.UndoMakeMove(); if (nBookMoves > 0) { dwMoveStr = MOVE_COORD(bks[0].wmv); printf(" ponder %.4s", (const char *) &dwMoveStr); } printf("\n"); fflush(stdout); return; } Search.pos.UndoMakeMove(); } } #endif // 3. 如果深度为零则返回静态搜索值 if (nDepth == 0) { #ifndef CCHESS_A3800 printf("info depth 0 score %d\n", SearchQuiesc(Search.pos, -MATE_VALUE, MATE_VALUE)); fflush(stdout); printf("nobestmove\n"); fflush(stdout); #endif return; } // 4. 生成根结点的每个着法 Search2.MoveSort.InitRoot(Search.pos, Search.nBanMoves, Search.wmvBanList); // 5. 初始化时间和计数器 Search2.bStop = Search2.bPonderStop = Search2.bPopPv = Search2.bPopCurrMove = false; Search2.nPopDepth = Search2.vlPopValue = 0; Search2.nAllNodes = Search2.nMainNodes = Search2.nUnchanged = 0; Search2.wmvPvLine[0] = 0; ClearKiller(Search2.wmvKiller); ClearHistory(); ClearHash(); // 由于 ClearHash() 需要消耗一定时间,所以计时从这以后开始比较合理 Search2.llTime = GetTime(); vlLast = 0; // 如果走了10回合无用着法,那么允许主动提和,以后每隔8回合提和一次 nDraw = -Search.pos.LastMove().CptDrw; if (nDraw > 5 && ((nDraw - 4) / 2) % 8 == 0) { Search.bDraw = true; } bUnique = false; nCurrTimer = 0; // 6. 做迭代加深搜索 for (i = 1; i <= nDepth; i ++) { // 需要输出主要变例时,第一个"info depth n"是不输出的 #ifndef CCHESS_A3800 if (Search2.bPopPv || Search.bDebug) { printf("info depth %d\n", i); fflush(stdout); } // 7. 根据搜索的时间决定,是否需要输出主要变例和当前思考的着法 Search2.bPopPv = (nCurrTimer > 300); Search2.bPopCurrMove = (nCurrTimer > 3000); #endif // 8. 搜索根结点 vl = SearchRoot(i); if (Search2.bStop) { if (vl > -MATE_VALUE) { vlLast = vl; // 跳出后,vlLast会用来判断认输或投降,所以需要给定最近一个值 } break; // 没有跳出,则"vl"是可靠值 } nCurrTimer = (int) (GetTime() - Search2.llTime); // 9. 如果搜索时间超过适当时限,则终止搜索 if (Search.nGoMode == GO_MODE_TIMER) { // a. 如果没有使用空着裁剪,那么适当时限减半(因为分枝因子加倍了) nLimitTimer = (Search.bNullMove ? Search.nProperTimer : Search.nProperTimer / 2); // b. 如果当前搜索值没有落后前一层很多,那么适当时限减半 nLimitTimer = (vl + DROPDOWN_VALUE >= vlLast ? nLimitTimer / 2 : nLimitTimer); // c. 如果最佳着法连续多层没有变化,那么适当时限减半 nLimitTimer = (Search2.nUnchanged >= UNCHANGED_DEPTH ? nLimitTimer / 2 : nLimitTimer); if (nCurrTimer > nLimitTimer) { if (Search.bPonder) { Search2.bPonderStop = true; // 如果处于后台思考模式,那么只是在后台思考命中后立即中止搜索。 } else { vlLast = vl; break; // 不管是否跳出,"vlLast"都已更新 } } } else if (Search.nGoMode == GO_MODE_NODES) { // nLimitNodes的计算方法与nLimitTimer是一样的 nLimitNodes = (Search.bNullMove ? Search.nNodes : Search.nNodes / 2); nLimitNodes = (vl + DROPDOWN_VALUE >= vlLast ? nLimitNodes / 2 : nLimitNodes); nLimitNodes = (Search2.nUnchanged >= UNCHANGED_DEPTH ? nLimitNodes / 2 : nLimitNodes); // GO_MODE_NODES下是不延长后台思考时间的 if (Search2.nAllNodes > nLimitNodes) { vlLast = vl; break; } } vlLast = vl; // 10. 搜索到杀棋则终止搜索 if (vlLast > WIN_VALUE || vlLast < -WIN_VALUE) { break; } // 11. 是唯一着法,则终止搜索 if (SearchUnique(1 - WIN_VALUE, i)) { bUnique = true; break; } } #ifdef CCHESS_A3800 Search.mvResult = Search2.wmvPvLine[0]; #else // 12. 输出最佳着法及其最佳应对(作为后台思考的猜测着法) if (Search2.wmvPvLine[0] != 0) { PopPvLine(); dwMoveStr = MOVE_COORD(Search2.wmvPvLine[0]); printf("bestmove %.4s", (const char *) &dwMoveStr); if (Search2.wmvPvLine[1] != 0) { dwMoveStr = MOVE_COORD(Search2.wmvPvLine[1]); printf(" ponder %.4s", (const char *) &dwMoveStr); } // 13. 判断是否认输或提和,但是经过唯一着法检验的不适合认输或提和(因为搜索深度不够) if (!bUnique) { if (vlLast > -WIN_VALUE && vlLast < -RESIGN_VALUE) { printf(" resign"); } else if (Search.bDraw && !Search.pos.NullSafe() && vlLast < DRAW_OFFER_VALUE * 2) { printf(" draw"); } } } else { printf("nobestmove"); } printf("\n"); fflush(stdout); #endif }
/* 主要变例完全搜索例程,和零窗口完全搜索的区别有以下几点: * * 1. 启用内部迭代加深启发; * 2. 不使用有负面影响的裁剪; * 3. Alpha-Beta边界判定复杂; * 4. PV结点要获取主要变例; * 5. 考虑PV结点处理最佳着法的情况。 */ static int SearchPV(int vlAlpha, int vlBeta, int nDepth, uint16_t *lpwmvPvLine) { int nNewDepth, nHashFlag, vlBest, vl; int mvBest, mvHash, mv, mvEvade; MoveSortStruct MoveSort; uint16_t wmvPvLine[LIMIT_DEPTH]; // 完全搜索例程包括以下几个步骤: // 1. 在叶子结点处调用静态搜索; *lpwmvPvLine = 0; if (nDepth <= 0) { __ASSERT(nDepth >= -NULL_DEPTH); return SearchQuiesc(Search.pos, vlAlpha, vlBeta); } Search2.nAllNodes ++; // 2. 无害裁剪; vl = HarmlessPruning(Search.pos, vlBeta); if (vl > -MATE_VALUE) { return vl; } // 3. 置换裁剪; vl = ProbeHash(Search.pos, vlAlpha, vlBeta, nDepth, NO_NULL, mvHash); if (Search.bUseHash && vl > -MATE_VALUE) { // 由于PV结点不适用置换裁剪,所以不会发生PV路线中断的情况 return vl; } // 4. 达到极限深度,直接返回评价值; __ASSERT(Search.pos.nDistance > 0); if (Search.pos.nDistance == LIMIT_DEPTH) { return Evaluate(Search.pos, vlAlpha, vlBeta); } __ASSERT(Search.pos.nDistance < LIMIT_DEPTH); // 5. 中断调用; Search2.nMainNodes ++; vlBest = -MATE_VALUE; if ((Search2.nMainNodes & Search.nCountMask) == 0 && Interrupt()) { return vlBest; } // 6. 内部迭代加深启发; if (nDepth > IID_DEPTH && mvHash == 0) { __ASSERT(nDepth / 2 <= nDepth - IID_DEPTH); vl = SearchPV(vlAlpha, vlBeta, nDepth / 2, wmvPvLine); if (vl <= vlAlpha) { vl = SearchPV(-MATE_VALUE, vlBeta, nDepth / 2, wmvPvLine); } if (Search2.bStop) { return vlBest; } mvHash = wmvPvLine[0]; } // 7. 初始化; mvBest = 0; nHashFlag = HASH_ALPHA; if (Search.pos.LastMove().ChkChs > 0) { // 如果是将军局面,那么生成所有应将着法; mvEvade = MoveSort.InitEvade(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); } else { // 如果不是将军局面,那么使用正常的着法列表。 MoveSort.InitFull(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); mvEvade = 0; } // 8. 按照"MoveSortStruct::NextFull()"例程的着法顺序逐一搜索; while ((mv = MoveSort.NextFull(Search.pos)) != 0) { if (Search.pos.MakeMove(mv)) { // 9. 尝试选择性延伸; nNewDepth = (Search.pos.LastMove().ChkChs > 0 || mvEvade != 0 ? nDepth : nDepth - 1); // 10. 主要变例搜索; if (vlBest == -MATE_VALUE) { vl = -SearchPV(-vlBeta, -vlAlpha, nNewDepth, wmvPvLine); } else { vl = -SearchCut(-vlAlpha, nNewDepth); if (vl > vlAlpha && vl < vlBeta) { vl = -SearchPV(-vlBeta, -vlAlpha, nNewDepth, wmvPvLine); } } Search.pos.UndoMakeMove(); if (Search2.bStop) { return vlBest; } // 11. Alpha-Beta边界判定; if (vl > vlBest) { vlBest = vl; if (vl >= vlBeta) { mvBest = mv; nHashFlag = HASH_BETA; break; } if (vl > vlAlpha) { vlAlpha = vl; mvBest = mv; nHashFlag = HASH_PV; AppendPvLine(lpwmvPvLine, mv, wmvPvLine); } } } } // 12. 更新置换表、历史表和杀手着法表。 if (vlBest == -MATE_VALUE) { __ASSERT(Search.pos.IsMate()); return Search.pos.nDistance - MATE_VALUE; } else { RecordHash(Search.pos, nHashFlag, vlBest, nDepth, mvEvade == 0 ? mvBest : mvEvade); if (mvBest != 0 && !MoveSort.GoodCap(Search.pos, mvBest)) { SetBestMove(mvBest, nDepth, Search2.wmvKiller[Search.pos.nDistance]); } return vlBest; } }
// 零窗口完全搜索例程 static int SearchCut(int vlBeta, int nDepth, bool bNoNull = false) { int nNewDepth, vlBest, vl; int mvHash, mv, mvEvade; MoveSortStruct MoveSort; // 完全搜索例程包括以下几个步骤: // 1. 在叶子结点处调用静态搜索; if (nDepth <= 0) { __ASSERT(nDepth >= -NULL_DEPTH); return SearchQuiesc(Search.pos, vlBeta - 1, vlBeta); } Search2.nAllNodes ++; // 2. 无害裁剪; vl = HarmlessPruning(Search.pos, vlBeta); if (vl > -MATE_VALUE) { return vl; } // 3. 置换裁剪; vl = ProbeHash(Search.pos, vlBeta - 1, vlBeta, nDepth, bNoNull, mvHash); if (Search.bUseHash && vl > -MATE_VALUE) { return vl; } // 4. 达到极限深度,直接返回评价值; if (Search.pos.nDistance == LIMIT_DEPTH) { return Evaluate(Search.pos, vlBeta - 1, vlBeta); } __ASSERT(Search.pos.nDistance < LIMIT_DEPTH); // 5. 中断调用; Search2.nMainNodes ++; vlBest = -MATE_VALUE; if ((Search2.nMainNodes & Search.nCountMask) == 0 && Interrupt()) { return vlBest; } // 6. 尝试空着裁剪; if (Search.bNullMove && !bNoNull && Search.pos.LastMove().ChkChs <= 0 && Search.pos.NullOkay()) { Search.pos.NullMove(); vl = -SearchCut(1 - vlBeta, nDepth - NULL_DEPTH - 1, NO_NULL); Search.pos.UndoNullMove(); if (Search2.bStop) { return vlBest; } if (vl >= vlBeta) { if (Search.pos.NullSafe()) { // a. 如果空着裁剪不带检验,那么记录深度至少为(NULL_DEPTH + 1); RecordHash(Search.pos, HASH_BETA, vl, MAX(nDepth, NULL_DEPTH + 1), 0); return vl; } else if (SearchCut(vlBeta, nDepth - NULL_DEPTH, NO_NULL) >= vlBeta) { // b. 如果空着裁剪带检验,那么记录深度至少为(NULL_DEPTH); RecordHash(Search.pos, HASH_BETA, vl, MAX(nDepth, NULL_DEPTH), 0); return vl; } } } // 7. 初始化; if (Search.pos.LastMove().ChkChs > 0) { // 如果是将军局面,那么生成所有应将着法; mvEvade = MoveSort.InitEvade(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); } else { // 如果不是将军局面,那么使用正常的着法列表。 MoveSort.InitFull(Search.pos, mvHash, Search2.wmvKiller[Search.pos.nDistance]); mvEvade = 0; } // 8. 按照"MoveSortStruct::NextFull()"例程的着法顺序逐一搜索; while ((mv = MoveSort.NextFull(Search.pos)) != 0) { if (Search.pos.MakeMove(mv)) { // 9. 尝试选择性延伸; nNewDepth = (Search.pos.LastMove().ChkChs > 0 || mvEvade != 0 ? nDepth : nDepth - 1); // 10. 零窗口搜索; vl = -SearchCut(1 - vlBeta, nNewDepth); Search.pos.UndoMakeMove(); if (Search2.bStop) { return vlBest; } // 11. 截断判定; if (vl > vlBest) { vlBest = vl; if (vl >= vlBeta) { RecordHash(Search.pos, HASH_BETA, vlBest, nDepth, mv); if (!MoveSort.GoodCap(Search.pos, mv)) { SetBestMove(mv, nDepth, Search2.wmvKiller[Search.pos.nDistance]); } return vlBest; } } } } // 12. 不截断措施。 if (vlBest == -MATE_VALUE) { __ASSERT(Search.pos.IsMate()); return Search.pos.nDistance - MATE_VALUE; } else { RecordHash(Search.pos, HASH_ALPHA, vlBest, nDepth, mvEvade); return vlBest; } }
// 静态搜索例程 static int SearchQuiesc(PositionStruct &pos, int vlAlpha, int vlBeta) { int vlBest, vl, mv; bool bInCheck; MoveSortStruct MoveSort; // 静态搜索例程包括以下几个步骤: Search2.nAllNodes ++; // 1. 无害裁剪; vl = HarmlessPruning(pos, vlBeta); if (vl > -MATE_VALUE) { return vl; } #ifdef HASH_QUIESC // 3. 置换裁剪; vl = ProbeHashQ(pos, vlAlpha, vlBeta); if (Search.bUseHash && vl > -MATE_VALUE) { return vl; } #endif // 4. 达到极限深度,直接返回评价值; if (pos.nDistance == LIMIT_DEPTH) { return Evaluate(pos, vlAlpha, vlBeta); } __ASSERT(Search.pos.nDistance < LIMIT_DEPTH); // 5. 初始化; vlBest = -MATE_VALUE; bInCheck = (pos.LastMove().ChkChs > 0); // 6. 对于被将军的局面,生成全部着法; if (bInCheck) { MoveSort.InitAll(pos); } else { // 7. 对于未被将军的局面,在生成着法前首先尝试空着(空着启发),即对局面作评价; vl = Evaluate(pos, vlAlpha, vlBeta); __ASSERT_BOUND(1 - WIN_VALUE, vl, WIN_VALUE - 1); __ASSERT(vl > vlBest); if (vl >= vlBeta) { #ifdef HASH_QUIESC RecordHashQ(pos, vl, MATE_VALUE); #endif return vl; } vlBest = vl; vlAlpha = MAX(vl, vlAlpha); // 8. 对于未被将军的局面,生成并排序所有吃子着法(MVV(LVA)启发); MoveSort.InitQuiesc(pos); } // 9. 用Alpha-Beta算法搜索这些着法; while ((mv = MoveSort.NextQuiesc(bInCheck)) != 0) { __ASSERT(bInCheck || pos.ucpcSquares[DST(mv)] > 0); if (pos.MakeMove(mv)) { vl = -SearchQuiesc(pos, -vlBeta, -vlAlpha); pos.UndoMakeMove(); if (vl > vlBest) { if (vl >= vlBeta) { #ifdef HASH_QUIESC if (vl > -WIN_VALUE && vl < WIN_VALUE) { RecordHashQ(pos, vl, MATE_VALUE); } #endif return vl; } vlBest = vl; vlAlpha = MAX(vl, vlAlpha); } } } // 10. 返回分值。 if (vlBest == -MATE_VALUE) { __ASSERT(pos.IsMate()); return pos.nDistance - MATE_VALUE; } else { #ifdef HASH_QUIESC if (vlBest > -WIN_VALUE && vlBest < WIN_VALUE) { RecordHashQ(pos, vlBest > vlAlpha ? vlBest : -MATE_VALUE, vlBest); } #endif return vlBest; } }
// 静态搜索 int SearchQuiesc ( int alpha, int beta ) { int mv, vl; int bvl = - MATE_VALUE; MoveSortStruct mvsort; if ( TimeOut() ) { // 超时 return bvl; } // 无害裁剪 vl = HarmlessPruning (); if ( vl > - MATE_VALUE ) { return vl; } // 置换裁剪 vl = QueryValueInHashTableQC ( alpha, beta ); if ( vl > - MATE_VALUE ) { return vl; } // 生成着法 if ( pos.checked ) { mvsort.InitMove (); } else { bvl = pos.Evaluate (); if ( bvl >= beta ) { InsertInfoToHashTableQC ( bvl, MATE_VALUE ); return bvl; } if ( bvl > alpha ) { alpha = bvl; } mvsort.InitGoodCapMove (); } // 大搜索 while ( (mv = mvsort.NextMove()) != 0 ) { pos.MakeMove (mv); vl = - SearchQuiesc ( -beta, -alpha ); pos.UndoMakeMove (); if ( TimeOut() ) { // 超时 return bvl; } if ( vl > bvl ) { bvl = vl; if ( bvl >= beta ) { if ( bvl > - BAN_VALUE && bvl < BAN_VALUE ) { InsertInfoToHashTableQC ( bvl, MATE_VALUE ); } return vl; } if ( bvl > alpha ) { alpha = bvl; } } } // 最后 if ( bvl > - BAN_VALUE && bvl < BAN_VALUE ) { InsertInfoToHashTableQC ( bvl, bvl ); } return bvl; }
// Alpha-Beta 搜索 int SearchAlphaBeta ( int depth, int alpha, int beta ) { int mv, vl; int bmv[nBest], bvl[nBest]; ClearBmvBvl ( bmv, bvl ); int hash_type = HASH_TYPE_ALPHA; MoveSortStruct mvsort; if ( TimeOut() ) { // 超时 return bvl[0]; } // 无害裁剪 vl = HarmlessPruning (); if ( vl > - MATE_VALUE ) { return vl; } // 置换裁剪 vl = QueryValueInHashTable ( depth, alpha, beta ); if ( vl > - MATE_VALUE ) { Search.bmv[0] = QueryMoveInHashTable ( depth, alpha, beta ); Search.bvl[0] = vl; return vl; } // 达到极限深度 if ( depth <= 0 ) { return SearchQuiesc ( alpha, beta ); } // 内部迭代加深启发 if ( depth > 2 ) { mv = QueryMoveInHashTableWithoutLimit (); if ( mv == 0 ) { SearchAlphaBeta ( depth / 2, alpha, beta ); if ( TimeOut() ) { // 超时 return bvl[0]; } } } // 生成着法 int nMoveNum = mvsort.InitMove (); Search.nNode ++; // 大搜索 while ( (mv = mvsort.NextMove()) != 0 ) { pos.MakeMove ( mv ); int newDepth = ( pos.checked || nMoveNum == 1 ) ? depth : depth - 1; vl = - SearchCut ( newDepth, - alpha ); if ( vl > alpha && vl < beta ) { vl = - SearchAlphaBeta ( newDepth, -beta, -alpha ); } pos.UndoMakeMove (); if ( TimeOut() ) { return bvl[0]; } UpdateBmvBvl ( bmv, bvl, mv, vl ); if ( bvl[0] >= beta ) { Search.nBeta ++; hash_type = HASH_TYPE_BETA; break; } if ( bvl[0] > alpha ) { alpha = bvl[0]; hash_type = HASH_TYPE_PV; } } // 最后 InsertInfoToHashTable ( depth, bmv[0], bvl[0], hash_type ); InsertHistoryTable ( bmv[0], depth ); CopyBmvBvl ( Search.bmv, Search.bvl, bmv, bvl ); return bvl[0]; }
// 零窗口搜索 int SearchCut ( int depth, int beta, bool bNoNull = false ) { int mv, vl; int bmv = 0; int bvl = - MATE_VALUE; MoveSortStruct mvsort; if ( TimeOut() ) { // 超时 return bvl; } // 无害裁剪 vl = HarmlessPruning (); if ( vl > - MATE_VALUE ) { return vl; } // 置换裁剪 vl = QueryValueInHashTable ( depth, beta - 1, beta ); if ( vl > - MATE_VALUE ) { return vl; } // 达到极限深度 if ( depth <= 0 ) { return SearchQuiesc ( beta - 1, beta ); } // 空着裁剪 : 大切に if ( !bNoNull && pos.checked == false && pos.NullOkay() ) { pos.NullMove (); vl = - SearchCut ( depth - NULL_DEPTH - 1, beta, true ); pos.UndoNullMove (); if ( TimeOut() ) { // 超时 return bvl; } if ( vl >= beta ) { if ( pos.NullSafe() ) { InsertInfoToHashTable ( MAX(depth, NULL_DEPTH + 1), 0, vl, HASH_TYPE_BETA ); return vl; } else if ( SearchCut(depth - NULL_DEPTH, beta, true) >= beta ) { InsertInfoToHashTable ( MAX(depth, NULL_DEPTH), 0, vl, HASH_TYPE_BETA ); return vl; } } } // 生成着法 int nMoveNum = mvsort.InitMove (); Search.nNode ++; // 大搜索 while ( (mv = mvsort.NextMove()) != 0 ) { pos.MakeMove ( mv ); int newDepth = ( pos.checked || nMoveNum == 1 ) ? depth : depth - 1; vl = - SearchCut ( newDepth, 1 - beta ); pos.UndoMakeMove (); if ( TimeOut() ) { // 超时 return bvl; } // 边界 if ( vl > bvl ) { bvl = vl; bmv = mv; if ( bvl >= beta ) { Search.nBeta ++; InsertInfoToHashTable ( depth, bmv, bvl, HASH_TYPE_BETA ); InsertHistoryTable ( bmv, depth ); return bvl; } } } // 最后 InsertInfoToHashTable ( depth, bmv, bvl, HASH_TYPE_ALPHA ); return bvl; }