📄 xqwl05.cpp
字号:
mv = 0;
return -MATE_VALUE;
}
mv = hsh.wmv;
bMate = FALSE;
if (hsh.svl > WIN_VALUE) {
hsh.svl -= pos.nDistance;
bMate = TRUE;
} else if (hsh.svl < -WIN_VALUE) {
hsh.svl += pos.nDistance;
bMate = TRUE;
}
if (hsh.ucDepth >= nDepth || bMate) {
if (hsh.ucFlag == HASH_BETA) {
return (hsh.svl >= vlBeta ? hsh.svl : -MATE_VALUE);
} else if (hsh.ucFlag == HASH_ALPHA) {
return (hsh.svl <= vlAlpha ? hsh.svl : -MATE_VALUE);
}
return hsh.svl;
}
return -MATE_VALUE;
};
// 保存置换表项
static void RecordHash(int nFlag, int vl, int nDepth, int mv) {
HashItem hsh;
hsh = Search.HashTable[pos.zobr.dwKey & (HASH_SIZE - 1)];
if (hsh.ucDepth > nDepth) {
return;
}
hsh.ucFlag = nFlag;
hsh.ucDepth = nDepth;
if (vl > WIN_VALUE) {
hsh.svl = vl + pos.nDistance;
} else if (vl < -WIN_VALUE) {
hsh.svl = vl - pos.nDistance;
} else {
hsh.svl = vl;
}
hsh.wmv = mv;
hsh.dwLock0 = pos.zobr.dwLock0;
hsh.dwLock1 = pos.zobr.dwLock1;
Search.HashTable[pos.zobr.dwKey & (HASH_SIZE - 1)] = hsh;
};
// MVV/LVA每种子力的价值
static BYTE cucMvvLva[24] = {
0, 0, 0, 0, 0, 0, 0, 0,
5, 1, 1, 3, 4, 3, 2, 0,
5, 1, 1, 3, 4, 3, 2, 0
};
// 求MVV/LVA值
inline int MvvLva(int mv) {
return (cucMvvLva[pos.ucpcSquares[DST(mv)]] << 3) - cucMvvLva[pos.ucpcSquares[SRC(mv)]];
}
// "qsort"按MVV/LVA值排序的比较函数
static int CompareMvvLva(const void *lpmv1, const void *lpmv2) {
return MvvLva(*(int *) lpmv2) - MvvLva(*(int *) lpmv1);
}
// "qsort"按历史表排序的比较函数
static int CompareHistory(const void *lpmv1, const void *lpmv2) {
return Search.nHistoryTable[*(int *) lpmv2] - Search.nHistoryTable[*(int *) lpmv1];
}
// 走法排序阶段
const int PHASE_HASH = 0;
const int PHASE_KILLER_1 = 1;
const int PHASE_KILLER_2 = 2;
const int PHASE_GEN_MOVES = 3;
const int PHASE_REST = 4;
// 走法排序结构
struct SortStruct {
int mvHash, mvKiller1, mvKiller2; // 置换表走法和两个杀手走法
int nPhase, nIndex, nGenMoves; // 当前阶段,当前采用第几个走法,总共有几个走法
int mvs[MAX_GEN_MOVES]; // 所有的走法
void Init(int mvHash_) { // 初始化,设定置换表走法和两个杀手走法
mvHash = mvHash_;
mvKiller1 = Search.mvKillers[pos.nDistance][0];
mvKiller2 = Search.mvKillers[pos.nDistance][1];
nPhase = PHASE_HASH;
}
int Next(void); // 得到下一个走法
};
// 得到下一个走法
int SortStruct::Next(void) {
int mv;
switch (nPhase) {
// "nPhase"表示着法启发的若干阶段,依次为:
// 0. 置换表着法启发,完成后立即进入下一阶段;
case PHASE_HASH:
nPhase = PHASE_KILLER_1;
if (mvHash != 0) {
return mvHash;
}
// 技巧:这里没有"break",表示"switch"的上一个"case"执行完后紧接着做下一个"case",下同
// 1. 杀手着法启发(第一个杀手着法),完成后立即进入下一阶段;
case PHASE_KILLER_1:
nPhase = PHASE_KILLER_2;
if (mvKiller1 != mvHash && mvKiller1 != 0 && pos.LegalMove(mvKiller1)) {
return mvKiller1;
}
// 2. 杀手着法启发(第二个杀手着法),完成后立即进入下一阶段;
case PHASE_KILLER_2:
nPhase = PHASE_GEN_MOVES;
if (mvKiller2 != mvHash && mvKiller2 != 0 && pos.LegalMove(mvKiller2)) {
return mvKiller2;
}
// 3. 生成所有着法,完成后立即进入下一阶段;
case PHASE_GEN_MOVES:
nPhase = PHASE_REST;
nGenMoves = pos.GenerateMoves(mvs);
qsort(mvs, nGenMoves, sizeof(int), CompareHistory);
nIndex = 0;
// 4. 对剩余着法做历史表启发;
case PHASE_REST:
while (nIndex < nGenMoves) {
mv = mvs[nIndex];
nIndex ++;
if (mv != mvHash && mv != mvKiller1 && mv != mvKiller2) {
return mv;
}
}
// 5. 没有着法了,返回零。
default:
return 0;
}
}
// 对最佳走法的处理
inline void SetBestMove(int mv, int nDepth) {
int *lpmvKillers;
Search.nHistoryTable[mv] += nDepth * nDepth;
lpmvKillers = Search.mvKillers[pos.nDistance];
if (lpmvKillers[0] != mv) {
lpmvKillers[1] = lpmvKillers[0];
lpmvKillers[0] = mv;
}
}
// 静态(Quiescence)搜索过程
static int SearchQuiesc(int vlAlpha, int vlBeta) {
int i, nGenMoves;
int vl, vlBest;
int mvs[MAX_GEN_MOVES];
// 一个静态搜索分为以下几个阶段
// 1. 检查重复局面
vl = pos.RepStatus();
if (vl != 0) {
return pos.RepValue(vl);
}
// 2. 到达极限深度就返回局面评价
if (pos.nDistance == LIMIT_DEPTH) {
return pos.Evaluate();
}
// 3. 初始化最佳值
vlBest = -MATE_VALUE; // 这样可以知道,是否一个走法都没走过(杀棋)
if (pos.InCheck()) {
// 4. 如果被将军,则生成全部走法
nGenMoves = pos.GenerateMoves(mvs);
qsort(mvs, nGenMoves, sizeof(int), CompareHistory);
} else {
// 5. 如果不被将军,先做局面评价
vl = pos.Evaluate();
if (vl > vlBest) {
vlBest = vl;
if (vl >= vlBeta) {
return vl;
}
if (vl > vlAlpha) {
vlAlpha = vl;
}
}
// 6. 如果局面评价没有截断,再生成吃子走法
nGenMoves = pos.GenerateMoves(mvs, GEN_CAPTURE);
qsort(mvs, nGenMoves, sizeof(int), CompareMvvLva);
}
// 7. 逐一走这些走法,并进行递归
for (i = 0; i < nGenMoves; i ++) {
if (pos.MakeMove(mvs[i])) {
vl = -SearchQuiesc(-vlBeta, -vlAlpha);
pos.UndoMakeMove();
// 8. 进行Alpha-Beta大小判断和截断
if (vl > vlBest) { // 找到最佳值(但不能确定是Alpha、PV还是Beta走法)
vlBest = vl; // "vlBest"就是目前要返回的最佳值,可能超出Alpha-Beta边界
if (vl >= vlBeta) { // 找到一个Beta走法
return vl; // Beta截断
}
if (vl > vlAlpha) { // 找到一个PV走法
vlAlpha = vl; // 缩小Alpha-Beta边界
}
}
}
}
// 9. 所有走法都搜索完了,返回最佳值
return vlBest == -MATE_VALUE ? pos.nDistance - MATE_VALUE : vlBest;
}
// "SearchFull"的参数
const BOOL NO_NULL = TRUE;
// 超出边界(Fail-Soft)的Alpha-Beta搜索过程
static int SearchFull(int vlAlpha, int vlBeta, int nDepth, BOOL bNoNull = FALSE) {
int nHashFlag, vl, vlBest;
int mv, mvBest, mvHash;
SortStruct Sort;
// 一个Alpha-Beta完全搜索分为以下几个阶段
if (pos.nDistance > 0) {
// 1. 到达水平线,则调用静态搜索(注意:由于空步裁剪,深度可能小于零)
if (nDepth <= 0) {
return SearchQuiesc(vlAlpha, vlBeta);
}
// 1-1. 检查重复局面(注意:不要在根节点检查,否则就没有走法了)
vl = pos.RepStatus();
if (vl != 0) {
return pos.RepValue(vl);
}
// 1-2. 到达极限深度就返回局面评价
if (pos.nDistance == LIMIT_DEPTH) {
return pos.Evaluate();
}
// 1-3. 尝试置换表裁剪,并得到置换表走法
vl = ProbeHash(vlAlpha, vlBeta, nDepth, mvHash);
if (vl > -MATE_VALUE) {
return vl;
}
// 1-4. 尝试空步裁剪(根节点的Beta值是"MATE_VALUE",所以不可能发生空步裁剪)
if (!bNoNull && !pos.InCheck() && pos.NullOkay()) {
pos.NullMove();
vl = -SearchFull(-vlBeta, 1 - vlBeta, nDepth - NULL_DEPTH - 1, NO_NULL);
pos.UndoNullMove();
if (vl >= vlBeta) {
return vl;
}
}
} else {
mvHash = 0;
}
// 2. 初始化最佳值和最佳走法
nHashFlag = HASH_ALPHA;
vlBest = -MATE_VALUE; // 这样可以知道,是否一个走法都没走过(杀棋)
mvBest = 0; // 这样可以知道,是否搜索到了Beta走法或PV走法,以便保存到历史表
// 3. 初始化走法排序结构
Sort.Init(mvHash);
// 4. 逐一走这些走法,并进行递归
while ((mv = Sort.Next()) != 0) {
if (pos.MakeMove(mv)) {
// 将军延伸
vl = -SearchFull(-vlBeta, -vlAlpha, pos.InCheck() ? nDepth : nDepth - 1);
pos.UndoMakeMove();
// 5. 进行Alpha-Beta大小判断和截断
if (vl > vlBest) { // 找到最佳值(但不能确定是Alpha、PV还是Beta走法)
vlBest = vl; // "vlBest"就是目前要返回的最佳值,可能超出Alpha-Beta边界
if (vl >= vlBeta) { // 找到一个Beta走法
nHashFlag = HASH_BETA;
mvBest = mv; // Beta走法要保存到历史表
break; // Beta截断
}
if (vl > vlAlpha) { // 找到一个PV走法
nHashFlag = HASH_PV;
mvBest = mv; // PV走法要保存到历史表
vlAlpha = vl; // 缩小Alpha-Beta边界
}
}
}
}
// 5. 所有走法都搜索完了,把最佳走法(不能是Alpha走法)保存到历史表,返回最佳值
if (vlBest == -MATE_VALUE) {
// 如果是杀棋,就根据杀棋步数给出评价
return pos.nDistance - MATE_VALUE;
}
// 记录到置换表
RecordHash(nHashFlag, vlBest, nDepth, mvBest);
if (mvBest != 0) {
// 如果不是Alpha走法,就将最佳走法保存到历史表
SetBestMove(mvBest, nDepth);
if (pos.nDistance == 0) {
// 搜索根节点时,总是有一个最佳走法(因为全窗口搜索不会超出边界),将这个走法保存下来
Search.mvResult = mvBest;
}
}
return vlBest;
}
// 迭代加深搜索过程
static void SearchMain(void) {
int i, t, vl;
// 初始化
memset(Search.nHistoryTable, 0, 65536 * sizeof(int)); // 清空历史表
memset(Search.mvKillers, 0, LIMIT_DEPTH * 2 * sizeof(int)); // 清空杀手走法表
memset(Search.HashTable, 0, HASH_SIZE * sizeof(HashItem)); // 清空置换表
t = clock(); // 初始化定时器
pos.nDistance = 0; // 初始步数
// 迭代加深过程
for (i = 1; i <= LIMIT_DEPTH; i ++) {
vl = SearchFull(-MATE_VALUE, MATE_VALUE, i);
// 搜索到杀棋,就终止搜索
if (vl > WIN_VALUE || vl < -WIN_VALUE) {
break;
}
// 超过一秒,就终止搜索
if (clock() - t > CLOCKS_PER_SEC) {
break;
}
}
}
// TransparentBlt 的替代函数,用来修正原函数在 Windows 98 下资源泄漏的问题
static void TransparentBlt2(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, UINT crTransparent) {
HDC hImageDC, hMaskDC;
HBITMAP hOldImageBMP, hImageBMP, hOldMaskBMP, hMaskBMP;
hImageBMP = CreateCompatibleBitmap(hdcDest, nWidthDest, nHeightDest);
hMaskBMP = CreateBitmap(nWidthDest, nHeightDest, 1, 1, NULL);
hImageDC = CreateCompatibleDC(hdcDest);
hMaskDC = CreateCompatibleDC(hdcDest);
hOldImageBMP = (HBITMAP) SelectObject(hImageDC, hImageBMP);
hOldMaskBMP = (HBITMAP) SelectObject(hMaskDC, hMaskBMP);
if (nWidthDest == nWidthSrc && nHeightDest == nHeightSrc) {
BitBlt(hImageDC, 0, 0, nWidthDest, nHeightDest,
hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
} else {
StretchBlt(hImageDC, 0, 0, nWidthDest, nHeightDest,
hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, SRCCOPY);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -