void BuildPos(PositionStruct &pos, const UcciCommStruct &UcciComm) { int i, mv; pos.FromFen(UcciComm.szFenStr); for (i = 0; i < UcciComm.nMoveNum; i ++) { mv = COORD_MOVE(UcciComm.lpdwMovesCoord[i]); if (mv == 0) { break; } if (pos.LegalMove(mv) && pos.MakeMove(mv) && pos.LastMove().CptDrw > 0) { // 始终让pos.nMoveNum反映没吃子的步数 pos.SetIrrev(); } } }
void BuildPos(PositionStruct &pos, const UcciCommStruct &UcciComm) { int i, mv; pos.FromFen(UcciComm.szFenStr); for (i = 0; i < UcciComm.nMoveNum; i ++) { mv = COORD_MOVE(UcciComm.lpdwMovesCoord[i]); if (mv == 0) { break; } if (pos.ucpcSquares[SRC(mv)] == 0) { break; } pos.MakeMove(mv); } }
// UCCI支持 - 输出Hash表中的局面信息 bool PopHash(const PositionStruct &pos) { HashStruct hsh; uint32_t dwMoveStr; int i; for (i = 0; i < HASH_LAYERS; i ++) { hsh = HASH_ITEM(pos, i); if (HASH_POS_EQUAL(hsh, pos)) { printf("pophash"); if (hsh.wmv != 0) { __ASSERT(pos.LegalMove(hsh.wmv)); dwMoveStr = MOVE_COORD(hsh.wmv); printf(" bestmove %.4s", (const char *) &dwMoveStr); } if (hsh.ucBetaDepth > 0) { printf(" lowerbound %d depth %d", hsh.svlBeta, hsh.ucBetaDepth); } if (hsh.ucAlphaDepth > 0) { printf(" upperbound %d depth %d", hsh.svlAlpha, hsh.ucAlphaDepth); } printf("\n"); fflush(stdout); return true; } } return false; }
// 点击格子事件处理 static void ClickSquare(int sq) { int pc; Xqwl.hdc = GetDC(Xqwl.hWnd); Xqwl.hdcTmp = CreateCompatibleDC(Xqwl.hdc); sq = Xqwl.bFlipped ? SQUARE_FLIP(sq) : sq; pc = pos.ucpcSquares[sq]; if ((pc & SIDE_TAG(pos.sdPlayer)) != 0) { // 如果点击自己的子,那么直接选中该子 if (Xqwl.sqSelected != 0) { DrawSquare(Xqwl.sqSelected); } Xqwl.sqSelected = sq; DrawSquare(sq, DRAW_SELECTED); if (Xqwl.mvLast != 0) { DrawSquare(SRC(Xqwl.mvLast)); DrawSquare(DST(Xqwl.mvLast)); } PlayResWav(IDR_CLICK); // 播放点击的声音 } else if (Xqwl.sqSelected != 0) { // 如果点击的不是自己的子,但有子选中了(一定是自己的子),那么走这个子 Xqwl.mvLast = MOVE(Xqwl.sqSelected, sq); pos.MakeMove(Xqwl.mvLast); DrawSquare(Xqwl.sqSelected, DRAW_SELECTED); DrawSquare(sq, DRAW_SELECTED); Xqwl.sqSelected = 0; PlayResWav(pc == 0 ? IDR_MOVE : IDR_CAPTURE); // 播放走子或吃子的声音 } DeleteDC(Xqwl.hdcTmp); ReleaseDC(Xqwl.hWnd, Xqwl.hdc); }
// 翻转棋盘 void FlipBoard(PositionStruct &pos) { int i, sq; uint8_t ucsqList[32]; for (i = 16; i < 48; i ++) { sq = pos.ucsqPieces[i]; ucsqList[i - 16] = sq; if (sq != 0) { pos.AddPiece(sq, i, DEL_PIECE); } } for (i = 16; i < 48; i ++) { sq = ucsqList[i - 16]; // 这行不同于ExchangeSide if (sq != 0) { pos.AddPiece(SQUARE_FLIP(sq), i); } } }
// 检测下一个着法是否稳定,有助于减少置换表的不稳定性 inline bool MoveStable(PositionStruct &pos, int mv) { // 判断下一个着法是否稳定的依据是: // 1. 没有后续着法,则假定是稳定的; if (mv == 0) { return true; } // 2. 吃子着法是稳定的; __ASSERT(pos.LegalMove(mv)); if (pos.ucpcSquares[DST(mv)] != 0) { return true; } // 3. 可能因置换表引起路线迁移,使得路线超过"MAX_MOVE_NUM",此时应立刻终止路线,并假定是稳定的。 if (!pos.MakeMove(mv)) { return true; } return false; }
int Node::playRollout(){ PositionStruct p = pos; int player = p.sdPlayer; int k = K; int mv[128]; int i; for (i = 0; i < k; i++){ mv[k] = move[k]; } while (1){ p.MakeMove(mv[ppi(pos2fen(&p), k, mv)]); genMove(&p, k, mv); if (k == 0){ //无子可走被将死 if (p.sdPlayer == player){ //自己被将死 return 0; } else{ return 1; } } } }
static void AddEcco(const char *szPgnFile, const EccoApiStruct &EccoApi) { int i, nStatus; uint32_t dwEccoIndex, dwFileMove[20]; PgnFileStruct pgn; PositionStruct pos; if (pgn.Read(szPgnFile, NO_ADVERT)) { pos.FromFen(cszStartFen); for (i = 1; i <= MIN(pgn.nMaxMove, 20); i ++) { dwFileMove[i - 1] = Move2File(pgn.wmvMoveTable[i], pos); TryMove(pos, nStatus, pgn.wmvMoveTable[i]); } if (pgn.nMaxMove < 20) { dwFileMove[pgn.nMaxMove] = 0; } dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); strcpy(pgn.szEcco, (const char *) &dwEccoIndex); strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); pgn.Write(szPgnFile); } }
// 无害裁剪 static int HarmlessPruning(const PositionStruct &pos, int vlBeta) { int vl, vlRep; // 1. 杀棋步数裁剪; vl = pos.nDistance - MATE_VALUE; if (vl >= vlBeta) { return vl; } // 2. 和棋裁剪; if (pos.IsDraw()) { return 0; // 安全起见,这里不用"pos.DrawValue()"; } // 3. 重复裁剪; vlRep = pos.RepStatus(); if (vlRep > 0) { return pos.RepValue(vlRep); } return -MATE_VALUE; }
// 无害裁剪 int HarmlessPruning ( void ) { // 1. 和局局面 if ( pos.IsDraw() ) { return 0; // eleeye上表示,为了安全起见,不用pos.DrawValue() } // 2. 路径重复 const int vRep = roll.RepStatus (); if ( vRep != REP_NONE ) { return roll.RepValue ( vRep ); } return - MATE_VALUE; }
// 获取置换表局面信息(没有命中时,返回"-MATE_VALUE") int ProbeHash(const PositionStruct &pos, int vlAlpha, int vlBeta, int nDepth, bool bNoNull, int &mv) { HashStruct hsh; int i, vl; bool bBanNode, bMateNode; // 获取置换表局面信息的过程包括以下几个步骤: // 1. 逐层获取置换表项 mv = 0; for (i = 0; i < HASH_LAYERS; i ++) { hsh = HASH_ITEM(pos, i); if (HASH_POS_EQUAL(hsh, pos)) { mv = hsh.wmv; __ASSERT(mv == 0 || pos.LegalMove(mv)); break; } } if (i == HASH_LAYERS) { return -MATE_VALUE; } // 2. 判断是否符合Beta边界 if (hsh.ucBetaDepth > 0) { vl = ValueAdjust(pos, bBanNode, bMateNode, hsh.svlBeta); if (!bBanNode && !(hsh.wmv == 0 && bNoNull) && (hsh.ucBetaDepth >= nDepth || bMateNode) && vl >= vlBeta) { __ASSERT_BOUND(1 - MATE_VALUE, vl, MATE_VALUE - 1); if (hsh.wmv == 0 || PosStable(pos, hsh.wmv)) { return vl; } } } // 3. 判断是否符合Alpha边界 if (hsh.ucAlphaDepth > 0) { vl = ValueAdjust(pos, bBanNode, bMateNode, hsh.svlAlpha); if (!bBanNode && (hsh.ucAlphaDepth >= nDepth || bMateNode) && vl <= vlAlpha) { __ASSERT_BOUND(1 - MATE_VALUE, vl, MATE_VALUE - 1); if (hsh.wmv == 0 || PosStable(pos, hsh.wmv)) { return vl; } } } return -MATE_VALUE; }
// 尝试某个着法,并返回着法状态,参阅"cchess.h" bool TryMove(PositionStruct &pos, int &nStatus, int mv) { if (!pos.LegalMove(mv)) { nStatus = MOVE_ILLEGAL; return false; } if (!pos.MakeMove(mv)) { nStatus = MOVE_INCHECK; return false; } nStatus = 0; nStatus += (pos.LastMove().CptDrw > 0 ? MOVE_CAPTURE : 0); nStatus += (pos.LastMove().ChkChs > 0 ? MOVE_CHECK : 0); nStatus += (pos.IsMate() ? MOVE_MATE : 0); nStatus += pos.RepStatus(3) * MOVE_PERPETUAL; // 提示:参阅"position.cpp"中的"IsRep()"函数 nStatus += (pos.IsDraw() ? MOVE_DRAW : 0); pos.UndoMakeMove(); return true; }
/* 判断获取置换表要符合哪些条件,置换表的分值针对四个不同的区间有不同的处理: * 一、如果分值在"WIN_VALUE"以内(即介于"-WIN_VALUE"到"WIN_VALUE"之间,下同),则只获取满足搜索深度要求的局面; * 二、如果分值在"WIN_VALUE"和"BAN_VALUE"之间,则不能获取置换表中的值(只能获取最佳着法仅供参考),目的是防止由于长将而导致的“置换表的不稳定性”; * 三、如果分值在"BAN_VALUE"以外,则获取局面时不必考虑搜索深度要求,因为这些局面已经被证明是杀棋了; * 四、如果分值是"DrawValue()"(是第一种情况的特殊情况),则不能获取置换表中的值(原因与第二种情况相同)。 * 注意:对于第三种情况,要对杀棋步数进行调整! */ inline int ValueAdjust(const PositionStruct &pos, bool &bBanNode, bool &bMateNode, int vl) { bBanNode = bMateNode = false; if (vl > WIN_VALUE) { if (vl <= BAN_VALUE) { bBanNode = true; } else { bMateNode = true; vl -= pos.nDistance; } } else if (vl < -WIN_VALUE) { if (vl >= -BAN_VALUE) { bBanNode = true; } else { bMateNode = true; vl += pos.nDistance; } } else if (vl == pos.DrawValue()) { bBanNode = true; } return vl; }
// 存储置换表局面信息 void RecordHash(const PositionStruct &pos, int nFlag, int vl, int nDepth, int mv) { HashStruct hsh; int i, nHashDepth, nMinDepth, nMinLayer; // 存储置换表局面信息的过程包括以下几个步骤: // 1. 对分值做杀棋步数调整; __ASSERT_BOUND(1 - MATE_VALUE, vl, MATE_VALUE - 1); __ASSERT(mv == 0 || pos.LegalMove(mv)); if (vl > WIN_VALUE) { if (mv == 0 && vl <= BAN_VALUE) { return; // 导致长将的局面(不进行置换裁剪)如果连最佳着法也没有,那么没有必要写入置换表 } vl += pos.nDistance; } else if (vl < -WIN_VALUE) { if (mv == 0 && vl >= -BAN_VALUE) { return; // 同理 } vl -= pos.nDistance; } else if (vl == pos.DrawValue() && mv == 0) { return; // 同理 } // 2. 逐层试探置换表; nMinDepth = 512; nMinLayer = 0; for (i = 0; i < HASH_LAYERS; i ++) { hsh = HASH_ITEM(pos, i); // 3. 如果试探到一样的局面,那么更新置换表信息即可; if (HASH_POS_EQUAL(hsh, pos)) { // 如果深度更深,或者边界缩小,都可更新置换表的值 if ((nFlag & HASH_ALPHA) != 0 && (hsh.ucAlphaDepth <= nDepth || hsh.svlAlpha >= vl)) { hsh.ucAlphaDepth = nDepth; hsh.svlAlpha = vl; } // Beta结点要注意:不要用Null-Move的结点覆盖正常的结点 if ((nFlag & HASH_BETA) != 0 && (hsh.ucBetaDepth <= nDepth || hsh.svlBeta <= vl) && (mv != 0 || hsh.wmv == 0)) { hsh.ucBetaDepth = nDepth; hsh.svlBeta = vl; } // 最佳着法是始终覆盖的 if (mv != 0) { hsh.wmv = mv; } HASH_ITEM(pos, i) = hsh; return; } // 4. 如果不是一样的局面,那么获得深度最小的置换表项; nHashDepth = MAX((hsh.ucAlphaDepth == 0 ? 0 : hsh.ucAlphaDepth + 256), (hsh.wmv == 0 ? hsh.ucBetaDepth : hsh.ucBetaDepth + 256)); __ASSERT(nHashDepth < 512); if (nHashDepth < nMinDepth) { nMinDepth = nHashDepth; nMinLayer = i; } } // 5. 记录置换表。 hsh.dwZobristLock0 = pos.zobr.dwLock0; hsh.dwZobristLock1 = pos.zobr.dwLock1; hsh.wmv = mv; hsh.ucAlphaDepth = hsh.ucBetaDepth = 0; hsh.svlAlpha = hsh.svlBeta = 0; if ((nFlag & HASH_ALPHA) != 0) { hsh.ucAlphaDepth = nDepth; hsh.svlAlpha = vl; } if ((nFlag & HASH_BETA) != 0) { hsh.ucBetaDepth = nDepth; hsh.svlBeta = vl; } HASH_ITEM(pos, nMinLayer) = hsh; }
int Xqf2Pgn(const char *szXqfFile, const char *szPgnFile, const EccoApiStruct &EccoApi) { int i, nArg0, nArgs[4]; int nCommentLen, mv, nStatus; bool bHasNext; PgnFileStruct pgn; PositionStruct pos; FILE *fp; XqfHeaderStruct xqfhd; XqfMoveStruct xqfmv; // 版本号和加密偏移值 int nXqfVer, nPieceOff, nSrcOff, nDstOff, nCommentOff; // 密钥流 int nEncStream[32]; // 密钥流索引号 int nEncIndex; // 局面初始位置 int nPiecePos[32]; uint32_t dwEccoIndex, dwFileMove[20]; fp = fopen(szXqfFile, "rb"); if (fp == NULL) { return XQF2PGN_ERROR_OPEN; } fread(&xqfhd, sizeof(xqfhd), 1, fp); fseek(fp, sizeof(xqfhd), SEEK_CUR); if (xqfhd.szTag[0] == 'X' && xqfhd.szTag[1] == 'Q') { // PGN文件可以打开,现在正式解析XQF文件 nXqfVer = xqfhd.szTag[2]; if (nXqfVer < 11) { nPieceOff = nSrcOff = nDstOff = nCommentOff = 0; for (i = 0; i < 32; i ++) { nEncStream[i] = 0; } } else { // 局面初始位置的加密偏移值 nPieceOff = (uint8_t) (Square54Plus221((uint8_t) xqfhd.szTag[13]) * (uint8_t) xqfhd.szTag[13]); // 着法起点的加密偏移值 nSrcOff = (uint8_t) (Square54Plus221((uint8_t) xqfhd.szTag[14]) * nPieceOff); // 着法终点的加密偏移值 nDstOff = (uint8_t) (Square54Plus221((uint8_t) xqfhd.szTag[15]) * nSrcOff); // 注释的加密偏移值 nCommentOff = ((uint8_t) xqfhd.szTag[12] * 256 + (uint8_t) xqfhd.szTag[13]) % 32000 + 767; // 基本掩码 nArg0 = xqfhd.szTag[3]; // 密钥 = 前段密钥 | (后段密钥 & 基本掩码) for (i = 0; i < 4; i ++) { nArgs[i] = xqfhd.szTag[8 + i] | (xqfhd.szTag[12 + i] & nArg0); } // 密钥流 = 密钥 & 密钥流掩码 for (i = 0; i < 32; i ++) { nEncStream[i] = (uint8_t) (nArgs[i % 4] & cszEncStreamMask[i]); } } nEncIndex = 0; // 记录棋谱信息 if (xqfhd.szEvent[0] == 0) { GetXqfString(pgn.szEvent, xqfhd.szTitle); } else { GetXqfString(pgn.szEvent, xqfhd.szEvent); } GetXqfString(pgn.szDate, xqfhd.szDate); GetXqfString(pgn.szSite, xqfhd.szSite); GetXqfString(pgn.szRed, xqfhd.szRed); GetXqfString(pgn.szBlack, xqfhd.szBlack); pgn.nResult = cnResultTrans[(int) xqfhd.szResult[3]]; if (xqfhd.szSetUp[0] < 2) { // 如果是开局或者全局,那么直接设置起始局面 pgn.posStart.FromFen(cszStartFen); } else { // 如果是中局或者排局,那么根据"xqfhd.szPiecePos[32]"的内容摆放局面 // 当版本号达到12时,还要进一步解密局面初始位置 if (nXqfVer < 12) { for (i = 0; i < 32; i ++) { nPiecePos[i] = (uint8_t) (xqfhd.szPiecePos[i] - nPieceOff); } } else { for (i = 0; i < 32; i ++) { nPiecePos[(nPieceOff + 1 + i) % 32] = (uint8_t) (xqfhd.szPiecePos[i] - nPieceOff); } } // 把"nPiecePos[32]"的数据放到"PositionStruct"中 pgn.posStart.ClearBoard(); for (i = 0; i < 32; i ++) { if (nPiecePos[i] < 90) { pgn.posStart.AddPiece(cucsqXqf2Square[nPiecePos[i]], cpcXqf2Piece[i]); } } pgn.posStart.SetIrrev(); } pos = pgn.posStart; bHasNext = true; while (bHasNext && pgn.nMaxMove < MAX_MOVE_LEN) { // 读取着法记录 if (nXqfVer < 11) { fread(&xqfmv, sizeof(xqfmv), 1, fp); fread(&nCommentLen, sizeof(int), 1, fp); if ((xqfmv.ucTag & 0xf0) == 0) { bHasNext = false; } } else { ReadAndDecrypt(fp, &xqfmv, sizeof(xqfmv), nEncStream, nEncIndex); if ((xqfmv.ucTag & 0x20) != 0) { ReadAndDecrypt(fp, &nCommentLen, sizeof(int), nEncStream, nEncIndex); nCommentLen -= nCommentOff; } else { nCommentLen = 0; } if ((xqfmv.ucTag & 0x80) == 0) { bHasNext = false; } } if (pgn.nMaxMove > 0) { // 记录着法 mv = MOVE(cucsqXqf2Square[(uint8_t) (xqfmv.ucSrc - 24 - nSrcOff)], cucsqXqf2Square[(uint8_t) (xqfmv.ucDst - 32 - nDstOff)]); if (pgn.nMaxMove == 1) { if ((pgn.posStart.ucpcSquares[SRC(mv)] & 32) != 0) { pgn.posStart.ChangeSide(); pos.ChangeSide(); } } if (xqfhd.szSetUp[0] < 2 && pgn.nMaxMove <= 20) { dwFileMove[pgn.nMaxMove - 1] = Move2File(mv, pos); } TryMove(pos, nStatus, mv); pgn.wmvMoveTable[pgn.nMaxMove] = mv; if (pos.nMoveNum == MAX_MOVE_NUM) { pos.SetIrrev(); } } if (nCommentLen > 0) { pgn.szCommentTable[pgn.nMaxMove] = new char[nCommentLen + 1]; ReadAndDecrypt(fp, pgn.szCommentTable[pgn.nMaxMove], nCommentLen, nEncStream, nEncIndex); pgn.szCommentTable[pgn.nMaxMove][nCommentLen] = '\0'; } pgn.nMaxMove ++; } pgn.nMaxMove --; // 解析ECCO if (xqfhd.szSetUp[0] < 2) { if (pgn.nMaxMove < 20) { dwFileMove[pgn.nMaxMove] = 0; } if (EccoApi.Available()) { dwEccoIndex = EccoApi.EccoIndex((const char *) dwFileMove); strcpy(pgn.szEcco, (const char *) &dwEccoIndex); strcpy(pgn.szOpen, EccoApi.EccoOpening(dwEccoIndex)); strcpy(pgn.szVar, EccoApi.EccoVariation(dwEccoIndex)); } } fclose(fp); return (pgn.Write(szPgnFile) ? XQF2PGN_OK : XQF2PGN_ERROR_CREATE); } else { fclose(fp); return XQF2PGN_ERROR_FORMAT; } }
// 零窗口搜索 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; }
// 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]; }
// 静态搜索例程 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; } }
// 调整型局面评价函数 inline int Evaluate(const PositionStruct &pos, int vlAlpha, int vlBeta) { int vl; vl = Search.bKnowledge ? pos.Evaluate(vlAlpha, vlBeta) : pos.Material(); return vl == pos.DrawValue() ? vl - 1 : vl; }
// 初始化棋局 static void Startup(void) { pos.Startup(); Xqwl.sqSelected = Xqwl.mvLast = 0; }
// 主搜索函数 int SearchMain ( void ) { // 特殊情况 MoveSortStruct mvsort; int nMoveNum = mvsort.InitMove (); if ( nMoveNum == 0 ) { // 无着法 printf ( "bestmove a0a1 resign\n" ); fflush ( stdout ); return 0; } else if ( nMoveNum == 1 ) { // 唯一着法 printf ( "bestmove %s\n", MoveIntToStr(mvsort.move[0]).c_str() ); fflush ( stdout ); return mvsort.move[0]; } if ( pos.IsDraw() ) { // 和局 printf ( "bestmove a0a1 draw\n" ); fflush ( stdout ); // 注意不要return } // 初始化 ClearHistoryTable (); ClearHashTable (); ClearHashTableQC (); InitBeginTime ( SEARCH_TOTAL_TIME ); // 迭代加深搜索 printf("depth time nNode rBeta"); for ( int i = 0; i < 3; i ++ ) { printf(" bvl[%d] bmv[%d]", i, i); } printf("\n"); fflush ( stdout ); int lastbvl[nBest], lastbmv[nBest]; ClearBmvBvl ( lastbmv, lastbvl ); for ( int depth = 1; /*depth <= ?*/; depth ++ ) { InitBeginTime ( THIS_SEARCH_TIME ); InitSearchStruct (); SearchAlphaBeta ( depth, - MATE_VALUE, MATE_VALUE ); if ( TimeOut() ) { CopyBmvBvl ( Search.bmv, Search.bvl, lastbmv, lastbvl ); break; } else { CopyBmvBvl ( lastbmv, lastbvl, Search.bmv, Search.bvl ); } printf( "%5d %.2fs %7d %2.0f%%", depth, TimeCost(SEARCH_TOTAL_TIME), Search.nNode, 100.0*Search.nBeta/Search.nNode); for ( int i = 0; i < 3; i ++ ) { printf(" %6d %s", Search.bvl[i], MoveIntToStr(Search.bmv[i]).c_str()); } printf("\n"); fflush ( stdout ); if ( Search.bvl[0] <= - BAN_VALUE || Search.bvl[0] >= BAN_VALUE) { break; } if ( Search.bvl[1] <= - BAN_VALUE || Search.bmv[1] == 0 ) { break; } } printf ( "totaltime: %.2fs\n", TimeCost(SEARCH_TOTAL_TIME) ); fflush ( stdout ); // 输出最优着法 if ( Search.bmv[0] == 0 || Search.bvl[0] <= - BAN_VALUE ) { printf ( "bestmove a0a1 resign\n" ); // 认输 fflush ( stdout ); return 0; } else { printf ( "bestmove %s\n", MoveIntToStr(Search.bmv[0]).c_str() ); fflush ( stdout ); return Search.bmv[0]; } }
// 静态搜索 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; }