// 点击格子事件处理 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 DelPiece(int sq, int pc) { ucpcSquares[sq] = 0; if (pc < 16) { vlWhite -= cucvlPiecePos[pc - 8][sq]; zobr.Xor(Zobrist.Table[pc - 8][sq]); } else { vlBlack -= cucvlPiecePos[pc - 16][SQUARE_FLIP(sq)]; zobr.Xor(Zobrist.Table[pc - 9][sq]); } }
// 绘制格子 static void DrawSquare(int sq, BOOL bSelected = FALSE) { int sqFlipped, xx, yy, pc; sqFlipped = Xqwl.bFlipped ? SQUARE_FLIP(sq) : sq; xx = BOARD_EDGE + (FILE_X(sqFlipped) - FILE_LEFT) * SQUARE_SIZE; yy = BOARD_EDGE + (RANK_Y(sqFlipped) - RANK_TOP) * SQUARE_SIZE; SelectObject(Xqwl.hdcTmp, Xqwl.bmpBoard); BitBlt(Xqwl.hdc, xx, yy, SQUARE_SIZE, SQUARE_SIZE, Xqwl.hdcTmp, xx, yy, SRCCOPY); pc = pos.ucpcSquares[sq]; if (pc != 0) { DrawTransBmp(Xqwl.hdc, Xqwl.hdcTmp, xx, yy, Xqwl.bmpPieces[pc]); } if (bSelected) { DrawTransBmp(Xqwl.hdc, Xqwl.hdcTmp, xx, yy, Xqwl.bmpSelected); } }
// 翻转棋盘 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); } } }
// 将内部着法表示转换为纵线符号 uint32_t Move2File(int mv, const PositionStruct &pos) { int i, j, sq, pc, pt, nPieceNum; int xSrc, ySrc, xDst, yDst; int nFileList[9], nPieceList[5]; C4dwStruct Ret; if (SRC(mv) == 0 || DST(mv) == 0) { return 0x20202020; } pc = pos.ucpcSquares[SRC(mv)]; if (pc == 0) { return 0x20202020; } pt = PIECE_TYPE(pc); Ret.c[0] = PIECE_BYTE(pt); if (pos.sdPlayer == 0) { xSrc = FILESQ_FILE_X(SQUARE_FILESQ(SRC(mv))); ySrc = FILESQ_RANK_Y(SQUARE_FILESQ(SRC(mv))); xDst = FILESQ_FILE_X(SQUARE_FILESQ(DST(mv))); yDst = FILESQ_RANK_Y(SQUARE_FILESQ(DST(mv))); } else { xSrc = FILESQ_FILE_X(SQUARE_FILESQ(SQUARE_FLIP(SRC(mv)))); ySrc = FILESQ_RANK_Y(SQUARE_FILESQ(SQUARE_FLIP(SRC(mv)))); xDst = FILESQ_FILE_X(SQUARE_FILESQ(SQUARE_FLIP(DST(mv)))); yDst = FILESQ_RANK_Y(SQUARE_FILESQ(SQUARE_FLIP(DST(mv)))); } if (pt >= KING_TYPE && pt <= BISHOP_TYPE) { Ret.c[1] = Digit2Byte(xSrc); } else { for (i = 0; i < 9; i ++) { nFileList[i] = 0; } j = (pt == PAWN_TYPE ? 5 : 2); for (i = 0; i < j; i ++) { sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); if (sq != -1) { nFileList[FILESQ_FILE_X(sq)] ++; } } // 提示:处理“两条的纵线上有多个兵(卒)”的问题上,可参阅"File2Move()"函数。 if (nFileList[xSrc] > 1) { nPieceNum = 0; for (i = 0; i < j; i ++) { sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); if (sq != -1) { if (nFileList[FILESQ_FILE_X(sq)] > 1) { nPieceList[nPieceNum] = FIRST_PIECE(pt, i); nPieceNum ++; } } } for (i = 0; i < nPieceNum - 1; i ++) { for (j = nPieceNum - 1; j > i; j --) { if (FILESQ_SIDE_PIECE(pos, nPieceList[j - 1]) > FILESQ_SIDE_PIECE(pos, nPieceList[j])) { SWAP(nPieceList[j - 1], nPieceList[j]); } } } sq = FILESQ_COORD_XY(xSrc, ySrc); for (i = 0; i < nPieceNum; i ++) { if (FILESQ_SIDE_PIECE(pos, nPieceList[i]) == sq) { break; } } Ret.c[1] = (nPieceNum == 2 && i == 1 ? ccPos2Byte[2 + DIRECT_TO_POS] : ccPos2Byte[nPieceNum > 3 ? i : i + DIRECT_TO_POS]); } else { Ret.c[1] = Digit2Byte(xSrc); } } if (pt >= ADVISOR_TYPE && pt <= KNIGHT_TYPE) { if (SRC(mv) == DST(mv)) { Ret.c[2] = '='; Ret.c[3] = 'P'; } else { Ret.c[2] = (yDst > ySrc ? '-' : '+'); Ret.c[3] = Digit2Byte(xDst); } } else { Ret.c[2] = (yDst == ySrc ? '.' : yDst > ySrc ? '-' : '+'); Ret.c[3] = (yDst == ySrc ? Digit2Byte(xDst) : Digit2Byte(ABS(ySrc - yDst) - 1)); } return Ret.dw; }
/* "File2Move()"函数将纵线符号表示转换为内部着法表示。 * * 这个函数以及后面的"Move2File()"函数是本模块最难处理的两个函数,特别是在处理“两条的纵线上有多个兵(卒)”的问题上。 * 在棋谱的快速时,允许只使用数字键盘,因此1到7依次代表帅(将)到兵(卒)这七种棋子,"File2Move()"函数也考虑到了这个问题。 */ int File2Move(uint32_t dwFileStr, const PositionStruct &pos) { int i, j, nPos, pt, sq, nPieceNum; int xSrc, ySrc, xDst, yDst; C4dwStruct FileStr; int nFileList[9], nPieceList[5]; // 纵线符号表示转换为内部着法表示,通常分为以下几个步骤: // 1. 检查纵线符号是否是仕(士)相(象)的28种固定纵线表示,在这之前首先必须把数字、小写等不统一的格式转换为统一格式; FileStr.dw = dwFileStr; switch (FileStr.c[0]) { case '2': case 'a': FileStr.c[0] = 'A'; break; case '3': case 'b': case 'E': case 'e': FileStr.c[0] = 'B'; break; default: break; } if (FileStr.c[3] == 'p') { FileStr.c[3] = 'P'; } for (i = 0; i < MAX_FIX_FILE; i ++) { if (FileStr.dw == cdwFixFile[i]) { if (pos.sdPlayer == 0) { return MOVE(cucFixMove[i][0], cucFixMove[i][1]); } else { return MOVE(SQUARE_FLIP(cucFixMove[i][0]), SQUARE_FLIP(cucFixMove[i][1])); } } } // 2. 如果不是这28种固定纵线表示,那么把棋子、位置和纵线序号(列号)解析出来 nPos = Byte2Direct(FileStr.c[0]); if (nPos == MAX_DIRECT) { pt = Byte2Piece(FileStr.c[0]); nPos = Byte2Pos(FileStr.c[1]); } else { pt = Byte2Piece(FileStr.c[1]); nPos += DIRECT_TO_POS; } if (nPos == MAX_POS) { // 3. 如果棋子是用列号表示的,那么可以直接根据纵线来找到棋子序号; xSrc = Byte2Digit(FileStr.c[1]); if (pt == KING_TYPE) { sq = FILESQ_SIDE_PIECE(pos, 0); } else if (pt >= KNIGHT_TYPE && pt <= PAWN_TYPE) { j = (pt == PAWN_TYPE ? 5 : 2); for (i = 0; i < j; i ++) { sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); if (sq != -1) { if (FILESQ_FILE_X(sq) == xSrc) { break; } } } sq = (i == j ? -1 : sq); } else { sq = -1; } } else { // 4. 如果棋子是用位置表示的,那么必须挑选出含有多个该种棋子的所有纵线,这是本函数最难处理的地方; if (pt >= KNIGHT_TYPE && pt <= PAWN_TYPE) { for (i = 0; i < 9; i ++) { nFileList[i] = 0; } j = (pt == PAWN_TYPE ? 5 : 2); for (i = 0; i < j; i ++) { sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); if (sq != -1) { nFileList[FILESQ_FILE_X(sq)] ++; } } nPieceNum = 0; for (i = 0; i < j; i ++) { sq = FILESQ_SIDE_PIECE(pos, FIRST_PIECE(pt, i)); if (sq != -1) { if (nFileList[FILESQ_FILE_X(sq)] > 1) { nPieceList[nPieceNum] = FIRST_PIECE(pt, i); nPieceNum ++; } } } // 5. 找到这些纵线以后,对这些纵线上的棋子进行排序,然后根据位置来确定棋子序号; for (i = 0; i < nPieceNum - 1; i ++) { for (j = nPieceNum - 1; j > i; j --) { if (FILESQ_SIDE_PIECE(pos, nPieceList[j - 1]) > FILESQ_SIDE_PIECE(pos, nPieceList[j])) { SWAP(nPieceList[j - 1], nPieceList[j]); } } } // 提示:如果只有两个棋子,那么“后”表示第二个棋子,如果有多个棋子, // 那么“一二三四五”依次代表第一个到第五个棋子,“前中后”依次代表第一个到第三个棋子。 if (nPieceNum == 2 && nPos == 2 + DIRECT_TO_POS) { sq = FILESQ_SIDE_PIECE(pos, nPieceList[1]); } else { nPos -= (nPos >= DIRECT_TO_POS ? DIRECT_TO_POS : 0); sq = (nPos >= nPieceNum ? -1 : FILESQ_SIDE_PIECE(pos, nPieceList[nPos])); } } else { sq = -1; } } if (sq == -1) { return 0; } // 6. 现在已知了着法的起点,就可以根据纵线表示的后两个符号来确定着法的终点; xSrc = FILESQ_FILE_X(sq); ySrc = FILESQ_RANK_Y(sq); if (pt == KNIGHT_TYPE) { // 提示:马的进退处理比较特殊。 xDst = Byte2Digit(FileStr.c[3]); if (FileStr.c[2] == '+') { yDst = ySrc - 3 + ABS(xDst - xSrc); } else { yDst = ySrc + 3 - ABS(xDst - xSrc); } } else { if (FileStr.c[2] == '+') { xDst = xSrc; yDst = ySrc - Byte2Digit(FileStr.c[3]) - 1; } else if (FileStr.c[2] == '-') { xDst = xSrc; yDst = ySrc + Byte2Digit(FileStr.c[3]) + 1; } else { xDst = Byte2Digit(FileStr.c[3]); yDst = ySrc; } } // 注意:yDst有可能超过范围! if (yDst < 0 || yDst > 9) { return 0; } // 7. 把相对走子方的坐标转换为固定坐标,得到着法的起点和终点。 if (pos.sdPlayer == 0) { return MOVE(FILESQ_SQUARE(FILESQ_COORD_XY(xSrc, ySrc)), FILESQ_SQUARE(FILESQ_COORD_XY(xDst, yDst))); } else { return MOVE(SQUARE_FLIP(FILESQ_SQUARE(FILESQ_COORD_XY(xSrc, ySrc))), SQUARE_FLIP(FILESQ_SQUARE(FILESQ_COORD_XY(xDst, yDst)))); } }
// 获得某个棋子对于本方视角的纵线优先坐标,棋子编号从0到15 inline int FILESQ_SIDE_PIECE(const PositionStruct &pos, int nPieceNum) { int sq; sq = pos.ucsqPieces[SIDE_TAG(pos.sdPlayer) + nPieceNum]; return (sq == 0 ? -1 : pos.sdPlayer == 0 ? SQUARE_FILESQ(sq) : SQUARE_FILESQ(SQUARE_FLIP(sq))); }
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]; } } }