// 获取置换表局面信息(没有命中时,返回"-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; }
// 存储置换表局面信息 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 PositionStruct::ChasedBy(int mv) const { int i, nSideTag, pcMoved, pcCaptured; int sqSrc, sqDst, x, y; uint8_t *lpucsqDst, *lpucsqPin; SlideMoveStruct *lpsmv; sqSrc = DST(mv); pcMoved = this->ucpcSquares[sqSrc]; nSideTag = SIDE_TAG(this->sdPlayer); __ASSERT_SQUARE(sqSrc); __ASSERT_PIECE(pcMoved); __ASSERT_BOUND(0, pcMoved - OPP_SIDE_TAG(this->sdPlayer), 15); // “捉”的判断包括以下几部分内容: switch (pcMoved - OPP_SIDE_TAG(this->sdPlayer)) { // 1. 走了马,判断是否捉车或捉有根的炮兵(卒) case KNIGHT_FROM: case KNIGHT_TO: // 逐一检测马踩的八个位置 lpucsqDst = PreGen.ucsqKnightMoves[sqSrc]; lpucsqPin = PreGen.ucsqKnightPins[sqSrc]; sqDst = *lpucsqDst; while (sqDst != 0) { __ASSERT_SQUARE(sqDst); if (ucpcSquares[*lpucsqPin] == 0) { pcCaptured = this->ucpcSquares[sqDst]; if ((pcCaptured & nSideTag) != 0) { pcCaptured -= nSideTag; __ASSERT_BOUND(0, pcCaptured, 15); // 技巧:优化兵种判断的分枝 if (pcCaptured <= ROOK_TO) { // 马捉仕(士)、相(象)和马的情况不予考虑 if (pcCaptured >= ROOK_FROM) { // 马捉到了车 return pcCaptured; } } else { if (pcCaptured <= CANNON_TO) { // 马捉到了炮,要判断炮是否受保护 if (!Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } else { // 马捉到了兵(卒),要判断兵(卒)是否过河并受保护 if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } } } } lpucsqDst ++; sqDst = *lpucsqDst; lpucsqPin ++; } break; // 2. 走了车,判断是否捉有根的马炮兵(卒) case ROOK_FROM: case ROOK_TO: x = FILE_X(sqSrc); y = RANK_Y(sqSrc); if (((SRC(mv) ^ sqSrc) & 0xf) == 0) { // 如果车纵向移动了,则判断车横向吃到的子 lpsmv = RankMovePtr(x, y); for (i = 0; i < 2; i ++) { sqDst = lpsmv->ucRookCap[i] + RANK_DISP(y); __ASSERT_SQUARE(sqDst); if (sqDst != sqSrc) { pcCaptured = this->ucpcSquares[sqDst]; if ((pcCaptured & nSideTag) != 0) { pcCaptured -= nSideTag; __ASSERT_BOUND(0, pcCaptured, 15); // 技巧:优化兵种判断的分枝 if (pcCaptured <= ROOK_TO) { // 车捉仕(士)、相(象)的情况不予考虑 if (pcCaptured >= KNIGHT_FROM) { if (pcCaptured <= KNIGHT_TO) { // 车捉到了马,要判断马是否受保护 if (!Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } // 车捉车的情况不予考虑 } } else { if (pcCaptured <= CANNON_TO) { // 车捉到了炮,要判断炮是否受保护 if (!Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } else { // 车捉到了兵(卒),要判断兵(卒)是否过河并受保护 if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } } } } } } else { // 如果车横向移动了,则判断车纵向吃到的子 lpsmv = FileMovePtr(x, y); for (i = 0; i < 2; i ++) { sqDst = lpsmv->ucRookCap[i] + FILE_DISP(x); __ASSERT_SQUARE(sqDst); if (sqDst != sqSrc) { pcCaptured = this->ucpcSquares[sqDst]; if ((pcCaptured & nSideTag) != 0) { pcCaptured -= nSideTag; __ASSERT_BOUND(0, pcCaptured, 15); // 技巧:优化兵种判断的分枝 if (pcCaptured <= ROOK_TO) { // 车捉仕(士)、相(象)的情况不予考虑 if (pcCaptured >= KNIGHT_FROM) { if (pcCaptured <= KNIGHT_TO) { // 车捉到了马,要判断马是否受保护 if (!Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } // 车捉车的情况不予考虑 } } else { if (pcCaptured <= CANNON_TO) { // 车捉到了炮,要判断炮是否受保护 if (!Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } else { // 车捉到了兵(卒),要判断兵(卒)是否过河并受保护 if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } } } } } } break; // 3. 走了炮,判断是否捉车或捉有根的马兵(卒) case CANNON_FROM: case CANNON_TO: x = FILE_X(sqSrc); y = RANK_Y(sqSrc); if (((SRC(mv) ^ sqSrc) & 0xf) == 0) { // 如果炮纵向移动了,则判断炮横向吃到的子 lpsmv = RankMovePtr(x, y); for (i = 0; i < 2; i ++) { sqDst = lpsmv->ucCannonCap[i] + RANK_DISP(y); __ASSERT_SQUARE(sqDst); if (sqDst != sqSrc) { pcCaptured = this->ucpcSquares[sqDst]; if ((pcCaptured & nSideTag) != 0) { pcCaptured -= nSideTag; __ASSERT_BOUND(0, pcCaptured, 15); // 技巧:优化兵种判断的分枝 if (pcCaptured <= ROOK_TO) { // 炮捉仕(士)、相(象)的情况不予考虑 if (pcCaptured >= KNIGHT_FROM) { if (pcCaptured <= KNIGHT_TO) { // 炮捉到了马,要判断马是否受保护 if (!Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } else { // 炮捉到了车 return pcCaptured; } } } else { // 炮捉炮的情况不予考虑 if (pcCaptured >= PAWN_FROM) { // 炮捉到了兵(卒),要判断兵(卒)是否过河并受保护 if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } } } } } } else { // 如果炮横向移动了,则判断炮纵向吃到的子 lpsmv = FileMovePtr(x, y); for (i = 0; i < 2; i ++) { sqDst = lpsmv->ucCannonCap[i] + FILE_DISP(x); __ASSERT_SQUARE(sqDst); if (sqDst != sqSrc) { pcCaptured = this->ucpcSquares[sqDst]; if ((pcCaptured & nSideTag) != 0) { pcCaptured -= nSideTag; __ASSERT_BOUND(0, pcCaptured, 15); // 技巧:优化兵种判断的分枝 if (pcCaptured <= ROOK_TO) { // 炮捉仕(士)、相(象)的情况不予考虑 if (pcCaptured >= KNIGHT_FROM) { if (pcCaptured <= KNIGHT_TO) { // 炮捉到了马,要判断马是否受保护 if (!Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } else { // 炮捉到了车 return pcCaptured; } } } else { // 炮捉炮的情况不予考虑 if (pcCaptured >= PAWN_FROM) { // 炮捉到了兵(卒),要判断兵(卒)是否过河并受保护 if (AWAY_HALF(sqDst, sdPlayer) && !Protected(this->sdPlayer, sqDst)) { return pcCaptured; } } } } } } } break; } return 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; } }
void PositionStruct::PreEvaluate(void) { int i, sq, nMidgameValue, nWhiteAttacks, nBlackAttacks, nWhiteSimpleValue, nBlackSimpleValue; uint8_t ucvlPawnPiecesAttacking[256], ucvlPawnPiecesAttackless[256]; if (!bInit) { bInit = true; // 初始化"PreEvalEx.cPopCnt16"数组,只需要初始化一次 for (i = 0; i < 65536; i ++) { PreEvalEx.cPopCnt16[i] = PopCnt16(i); } } // 首先判断局势处于开中局还是残局阶段,方法是计算各种棋子的数量,按照车=6、马炮=3、其它=1相加。 nMidgameValue = PopCnt32(this->dwBitPiece & BOTH_BITPIECE(ADVISOR_BITPIECE | BISHOP_BITPIECE | PAWN_BITPIECE)) * OTHER_MIDGAME_VALUE; nMidgameValue += PopCnt32(this->dwBitPiece & BOTH_BITPIECE(KNIGHT_BITPIECE | CANNON_BITPIECE)) * KNIGHT_CANNON_MIDGAME_VALUE; nMidgameValue += PopCnt32(this->dwBitPiece & BOTH_BITPIECE(ROOK_BITPIECE)) * ROOK_MIDGAME_VALUE; // 使用二次函数,子力很少时才认为接近残局 nMidgameValue = (2 * TOTAL_MIDGAME_VALUE - nMidgameValue) * nMidgameValue / TOTAL_MIDGAME_VALUE; __ASSERT_BOUND(0, nMidgameValue, TOTAL_MIDGAME_VALUE); PreEval.vlAdvanced = (TOTAL_ADVANCED_VALUE * nMidgameValue + TOTAL_ADVANCED_VALUE / 2) / TOTAL_MIDGAME_VALUE; __ASSERT_BOUND(0, PreEval.vlAdvanced, TOTAL_ADVANCED_VALUE); for (sq = 0; sq < 256; sq ++) { if (IN_BOARD(sq)) { PreEval.ucvlWhitePieces[0][sq] = PreEval.ucvlBlackPieces[0][SQUARE_FLIP(sq)] = (uint8_t) ((cucvlKingPawnMidgameAttacking[sq] * nMidgameValue + cucvlKingPawnEndgameAttacking[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); PreEval.ucvlWhitePieces[3][sq] = PreEval.ucvlBlackPieces[3][SQUARE_FLIP(sq)] = (uint8_t) ((cucvlKnightMidgame[sq] * nMidgameValue + cucvlKnightEndgame[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); PreEval.ucvlWhitePieces[4][sq] = PreEval.ucvlBlackPieces[4][SQUARE_FLIP(sq)] = (uint8_t) ((cucvlRookMidgame[sq] * nMidgameValue + cucvlRookEndgame[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); PreEval.ucvlWhitePieces[5][sq] = PreEval.ucvlBlackPieces[5][SQUARE_FLIP(sq)] = (uint8_t) ((cucvlCannonMidgame[sq] * nMidgameValue + cucvlCannonEndgame[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); ucvlPawnPiecesAttacking[sq] = PreEval.ucvlWhitePieces[0][sq]; ucvlPawnPiecesAttackless[sq] = (uint8_t) ((cucvlKingPawnMidgameAttackless[sq] * nMidgameValue + cucvlKingPawnEndgameAttackless[sq] * (TOTAL_MIDGAME_VALUE - nMidgameValue)) / TOTAL_MIDGAME_VALUE); } } for (i = 0; i < 16; i ++) { PreEvalEx.vlHollowThreat[i] = cvlHollowThreat[i] * (nMidgameValue + TOTAL_MIDGAME_VALUE) / (TOTAL_MIDGAME_VALUE * 2); __ASSERT_BOUND(0, PreEvalEx.vlHollowThreat[i], cvlHollowThreat[i]); PreEvalEx.vlCentralThreat[i] = cvlCentralThreat[i]; } // 然后判断各方是否处于进攻状态,方法是计算各种过河棋子的数量,按照车马2炮兵1相加。 nWhiteAttacks = nBlackAttacks = 0; for (i = SIDE_TAG(0) + KNIGHT_FROM; i <= SIDE_TAG(0) + ROOK_TO; i ++) { if (this->ucsqPieces[i] != 0 && BLACK_HALF(this->ucsqPieces[i])) { nWhiteAttacks += 2; } } for (i = SIDE_TAG(0) + CANNON_FROM; i <= SIDE_TAG(0) + PAWN_TO; i ++) { if (this->ucsqPieces[i] != 0 && BLACK_HALF(this->ucsqPieces[i])) { nWhiteAttacks ++; } } for (i = SIDE_TAG(1) + KNIGHT_FROM; i <= SIDE_TAG(1) + ROOK_TO; i ++) { if (this->ucsqPieces[i] != 0 && WHITE_HALF(this->ucsqPieces[i])) { nBlackAttacks += 2; } } for (i = SIDE_TAG(1) + CANNON_FROM; i <= SIDE_TAG(1) + PAWN_TO; i ++) { if (this->ucsqPieces[i] != 0 && WHITE_HALF(this->ucsqPieces[i])) { nBlackAttacks ++; } } // 如果本方轻子数比对方多,那么每多一个轻子(车算2个轻子)威胁值加2。威胁值最多不超过8。 nWhiteSimpleValue = PopCnt16(this->wBitPiece[0] & ROOK_BITPIECE) * 2 + PopCnt16(this->wBitPiece[0] & (KNIGHT_BITPIECE | CANNON_BITPIECE)); nBlackSimpleValue = PopCnt16(this->wBitPiece[1] & ROOK_BITPIECE) * 2 + PopCnt16(this->wBitPiece[1] & (KNIGHT_BITPIECE | CANNON_BITPIECE)); if (nWhiteSimpleValue > nBlackSimpleValue) { nWhiteAttacks += (nWhiteSimpleValue - nBlackSimpleValue) * 2; } else { nBlackAttacks += (nBlackSimpleValue - nWhiteSimpleValue) * 2; } nWhiteAttacks = MIN(nWhiteAttacks, TOTAL_ATTACK_VALUE); nBlackAttacks = MIN(nBlackAttacks, TOTAL_ATTACK_VALUE); PreEvalEx.vlBlackAdvisorLeakage = TOTAL_ADVISOR_LEAKAGE * nWhiteAttacks / TOTAL_ATTACK_VALUE; PreEvalEx.vlWhiteAdvisorLeakage = TOTAL_ADVISOR_LEAKAGE * nBlackAttacks / TOTAL_ATTACK_VALUE; __ASSERT_BOUND(0, nWhiteAttacks, TOTAL_ATTACK_VALUE); __ASSERT_BOUND(0, nBlackAttacks, TOTAL_ATTACK_VALUE); __ASSERT_BOUND(0, PreEvalEx.vlBlackAdvisorLeakage, TOTAL_ADVISOR_LEAKAGE); __ASSERT_BOUND(0, PreEvalEx.vlBlackAdvisorLeakage, TOTAL_ADVISOR_LEAKAGE); for (sq = 0; sq < 256; sq ++) { if (IN_BOARD(sq)) { PreEval.ucvlWhitePieces[1][sq] = PreEval.ucvlWhitePieces[2][sq] = (uint8_t) ((cucvlAdvisorBishopThreatened[sq] * nBlackAttacks + (PreEval.bPromotion ? cucvlAdvisorBishopPromotionThreatless[sq] : cucvlAdvisorBishopThreatless[sq]) * (TOTAL_ATTACK_VALUE - nBlackAttacks)) / TOTAL_ATTACK_VALUE); PreEval.ucvlBlackPieces[1][sq] = PreEval.ucvlBlackPieces[2][sq] = (uint8_t) ((cucvlAdvisorBishopThreatened[SQUARE_FLIP(sq)] * nWhiteAttacks + (PreEval.bPromotion ? cucvlAdvisorBishopPromotionThreatless[SQUARE_FLIP(sq)] : cucvlAdvisorBishopThreatless[SQUARE_FLIP(sq)]) * (TOTAL_ATTACK_VALUE - nWhiteAttacks)) / TOTAL_ATTACK_VALUE); PreEval.ucvlWhitePieces[6][sq] = (uint8_t) ((ucvlPawnPiecesAttacking[sq] * nWhiteAttacks + ucvlPawnPiecesAttackless[sq] * (TOTAL_ATTACK_VALUE - nWhiteAttacks)) / TOTAL_ATTACK_VALUE); PreEval.ucvlBlackPieces[6][sq] = (uint8_t) ((ucvlPawnPiecesAttacking[SQUARE_FLIP(sq)] * nBlackAttacks + ucvlPawnPiecesAttackless[SQUARE_FLIP(sq)] * (TOTAL_ATTACK_VALUE - nBlackAttacks)) / TOTAL_ATTACK_VALUE); } } for (i = 0; i < 16; i ++) { PreEvalEx.vlWhiteBottomThreat[i] = cvlBottomThreat[i] * nBlackAttacks / TOTAL_ATTACK_VALUE; PreEvalEx.vlBlackBottomThreat[i] = cvlBottomThreat[i] * nWhiteAttacks / TOTAL_ATTACK_VALUE; } // 检查预评价是否对称 #ifndef NDEBUG for (sq = 0; sq < 256; sq ++) { if (IN_BOARD(sq)) { for (i = 0; i < 7; i ++) { __ASSERT(PreEval.ucvlWhitePieces[i][sq] == PreEval.ucvlWhitePieces[i][SQUARE_MIRROR(sq)]); __ASSERT(PreEval.ucvlBlackPieces[i][sq] == PreEval.ucvlBlackPieces[i][SQUARE_MIRROR(sq)]); } } } for (i = FILE_LEFT; i <= FILE_RIGHT; i ++) { __ASSERT(PreEvalEx.vlWhiteBottomThreat[i] == PreEvalEx.vlWhiteBottomThreat[FILE_FLIP(i)]); __ASSERT(PreEvalEx.vlBlackBottomThreat[i] == PreEvalEx.vlBlackBottomThreat[FILE_FLIP(i)]); } #endif // 调整不受威胁方少掉的仕(士)相(象)分值 this->vlWhite = ADVISOR_BISHOP_ATTACKLESS_VALUE * (TOTAL_ATTACK_VALUE - nBlackAttacks) / TOTAL_ATTACK_VALUE; this->vlBlack = ADVISOR_BISHOP_ATTACKLESS_VALUE * (TOTAL_ATTACK_VALUE - nWhiteAttacks) / TOTAL_ATTACK_VALUE; // 如果允许升变,那么不受威胁的仕(士)相(象)分值就少了一半 if (PreEval.bPromotion) { this->vlWhite /= 2; this->vlBlack /= 2; } // 最后重新计算子力位置分 for (i = 16; i < 32; i ++) { sq = this->ucsqPieces[i]; if (sq != 0) { __ASSERT_SQUARE(sq); this->vlWhite += PreEval.ucvlWhitePieces[PIECE_TYPE(i)][sq]; } } for (i = 32; i < 48; i ++) { sq = this->ucsqPieces[i]; if (sq != 0) { __ASSERT_SQUARE(sq); this->vlBlack += PreEval.ucvlBlackPieces[PIECE_TYPE(i)][sq]; } } }