/* 因为没有传入side参数,因此只适合计算黑子的策略 */ int NegaMaxEngine::NegaMax(int depth) { int bestScore = -INFINITY; int side = (m_nSearchDepth - depth)%2; int childNodeCount, score; if(depth <= 0) { return m_pEval->Evaluate(CurPosition, side); } childNodeCount = m_pMG->CreatePossibleMove(CurPosition, depth , side); if(childNodeCount <= 0) return NOMORE_MOVE; for(int i=0; i<childNodeCount; ++i) { MakeMove(&m_pMG->m_MoveList[depth][i], side); score = -NegaMax(depth -1); UnMakeMove(&m_pMG->m_MoveList[depth][i]); if(score > bestScore) { bestScore = score; if(depth == m_nSearchDepth) { m_cmBestMove = m_pMG->m_MoveList[depth][i]; } } } return bestScore; }
/* Search game tree by alpha-beta algorith */ short AlphaBeta(short alpha, short beta, short depth) { short i, value, best; if (!depth) return Eval(); Gen(); best = -INFINITY; for (i=gen_begin[ply]; i<gen_end[ply] && best<beta; i++) { if (best > alpha) alpha = best; if (MakeMove(gen_dat[i].m)) value = 1000-ply; else value = -AlphaBeta(-beta, -alpha, depth-1); UnMakeMove(); if (value > best) { best = value; if (!ply) newmove = gen_dat[i].m; } } return best; }
int CAlphaBetaEngine::alphabeta(int depth, int alpha, int beta) { int score; int Count,i; BYTE type; i = IsGameOver(CurPosition, depth); if (i != 0) return i; if (depth <= 0) //叶子节点取估值 return m_pEval->Eveluate(CurPosition, (m_nMaxDepth-depth)%2); Count = m_pMG->CreatePossibleMove(CurPosition, depth, (m_nMaxDepth-depth)%2); for (i=0;i<Count;i++) { type = MakeMove(&m_pMG->m_MoveList[depth][i]); score = -alphabeta(depth - 1, -beta, -alpha); UnMakeMove(&m_pMG->m_MoveList[depth][i],type); if (score > alpha) { alpha = score; if(depth == m_nMaxDepth) m_cmBestMove = m_pMG->m_MoveList[depth][i]; } if (alpha >= beta) break; } return alpha; }
int CAlphabeta_HHEngine::AlphaBeta(int nDepth, int alpha,int beta) { int score; int Count,i; BYTE type; i=IsGameOver(CurPosition, nDepth); if(i!=0) return i; if(nDepth<=0)//叶子节点取估值 return m_pEval->Eveluate(CurPosition,(m_nMaxDepth-nDepth)%2,m_nUserChessColor); Count=m_pMG->CreatePossibleMove(CurPosition,nDepth,(m_nMaxDepth-nDepth)%2,m_nUserChessColor); if(nDepth==m_nMaxDepth) { //在根节点设定进度条 m_pThinkProgress->SetRange(0,Count); m_pThinkProgress->SetStep(1); } //取所有走法的历史得分 for(i=0;i<Count;i++) m_pMG->m_MoveList[nDepth][i].Score=GetHistoryScore(&m_pMG->m_MoveList[nDepth][i]); MergeSort(m_pMG->m_MoveList[nDepth],Count,0);//对Count种走法按历史得分大小排序 int bestmove=-1;//记录最佳走法的变量 for(i=0;i<Count;i++) { if(nDepth==m_nMaxDepth) m_pThinkProgress->StepIt();//走进度条 type=MakeMove(&m_pMG->m_MoveList[nDepth][i]); score=-AlphaBeta(nDepth-1,-beta,-alpha); UnMakeMove(&m_pMG->m_MoveList[nDepth][i],type); if(score>alpha) { alpha=score; if(nDepth==m_nMaxDepth) m_cmBestMove=m_pMG->m_MoveList[nDepth][i]; bestmove=i; } if(alpha>=beta) { bestmove=i; break; } } if(bestmove!=-1) EnterHistoryScore(&m_pMG->m_MoveList[nDepth][bestmove],nDepth);//将最佳走法汇入历史记录表 return alpha; }
int AlphaBetaEngine::AlphaBeta(int depth, int alpha, int beta) { int side = (m_nSearchDepth - depth)%2; int childNodeCount, score; if(depth <= 0) { return m_pEval->Evaluate(CurPosition, side); } childNodeCount = m_pMG->CreatePossibleMove(CurPosition, depth , side); #ifdef _DEBUG_NUM branchNum += childNodeCount; #endif for (int i=0; i<childNodeCount; i++) { m_pMG->m_MoveList[depth][i].Score = 7 -abs(m_pMG->m_MoveList[depth][i].StonePos.x-m_pMG->m_MoveList[depth][i].StonePos.y); } MergeSort(m_pMG->m_MoveList[depth], childNodeCount); for(int i=0; i<childNodeCount; ++i) { MakeMove(&m_pMG->m_MoveList[depth][i], side); score = -AlphaBeta(depth -1, -beta, -alpha); UnMakeMove(&m_pMG->m_MoveList[depth][i]); if(score >alpha) { alpha = score; if(depth == m_nSearchDepth) { m_cmBestMove = m_pMG->m_MoveList[depth][i]; } if(alpha > beta) { #ifdef _DEBUG_NUM cutoffNum += (childNodeCount -i); #endif break;//cutoff } } } return alpha; }
int CPVS_Engine::PrincipalVariation(int nDepth, int alpha, int beta) { int score; int Count,i; BYTE type; int best; i=IsGameOver(CurPosition, nDepth); if(i!=0) return i; //叶子节点取估值 if(nDepth<=0) return m_pEval->Eveluate(CurPosition,(m_nMaxDepth-nDepth)%2,m_nUserChessColor); Count=m_pMG->CreatePossibleMove(CurPosition,nDepth,(m_nMaxDepth-nDepth)%2,m_nUserChessColor); if(nDepth==m_nMaxDepth) { //在根节点设定进度条 m_pThinkProgress->SetRange(0,Count); m_pThinkProgress->SetStep(1); } type=MakeMove(&m_pMG->m_MoveList[nDepth][0]); //产生第一个节点 best=-PrincipalVariation(nDepth-1,-beta,-alpha);//使用全窗口搜索第一个节点 UnMakeMove(&m_pMG->m_MoveList[nDepth][0],type); //撤销第一个节点 //靠近根节点保存最佳走法 if(nDepth==m_nMaxDepth) m_cmBestMove=m_pMG->m_MoveList[nDepth][0]; //从第二个节点起,对每一节点 for(i=1;i<Count;i++) { if(nDepth==m_nMaxDepth) m_pThinkProgress->StepIt();//走进度条 if(best<beta)//如果不能Beta剪枝 { if(best>alpha) alpha=best; type=MakeMove(&m_pMG->m_MoveList[nDepth][i]); //产生子个节点 score=-PrincipalVariation(nDepth-1,-alpha-1,-alpha);//使用极窄窗搜索 if(score>alpha && score<beta) { best=-PrincipalVariation(nDepth-1,-beta,-score);//failhigh,重新搜索 //靠近根节点保存最佳走法 if(nDepth==m_nMaxDepth) m_cmBestMove=m_pMG->m_MoveList[nDepth][i]; } else if(score>best) { best=score;//窄窗搜索命中 if(nDepth==m_nMaxDepth) m_cmBestMove=m_pMG->m_MoveList[nDepth][i]; } UnMakeMove(&m_pMG->m_MoveList[nDepth][i],type);//撤销子节点 } } return best;//返回最佳值 }
/* ******************************************************************************** * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. then, make this move and * * do a search from the resulting position. while pondering, one of three * * things can happen: (1) a move is entered, and it matches the predicted * * move. we then switch from pondering to thinking and search as normal; * * (2) a move is entered, but it does not match the predicted move. we then * * abort the search, unmake the pondered move, and then restart with the move * * entered. (3) a command is entered. if it is a simple command, it can be * * done without aborting the search or losing time. if not, we abort the * * search, execute the command, and then attempt to restart pondering if the * * command didn't make that impossible. * * * ******************************************************************************** */ int Ponder(int wtm) { int dummy=0, i, *n_ponder_moves, *mv; /* ---------------------------------------------------------- | | | first, let's check to see if pondering is allowed, or | | if we should avoid pondering on this move since it is | | the first move of a game, or if the game is over, or | | "force" mode is active, or there is input in the queue | | that needs to be read and processed. | | | ---------------------------------------------------------- */ if (!ponder || force || over || CheckInput()) return(0); /* ---------------------------------------------------------- | | | if we don't have a predicted move to ponder, try two | | sources: (1) look up the current position in the | | transposition table and see if it has a suggested best | | move; (2) do a short tree search to calculate a move | | that we should ponder. | | | ---------------------------------------------------------- */ strcpy(hint,"none"); if (!ponder_move) { (void) LookUp(0,0,wtm,&dummy,&dummy); if (hash_move[0]) ponder_move=hash_move[0]; } if (!ponder_move) { TimeSet(puzzle); if (time_limit < 3) return(0); puzzling=1; position[1]=position[0]; printf(" puzzling over a move to ponder.\n"); last_pv.path_length=0; last_pv.path_iteration_depth=0; for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } (void) Iterate(wtm,puzzle,0); for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } puzzling=0; if (pv[0].path_length) ponder_move=pv[0].path[1]; if (!ponder_move) return(0); for (i=1;i<(int) pv[0].path_length-1;i++) last_pv.path[i]=pv[0].path[i+1]; last_pv.path_length=pv[0].path_length-1; last_pv.path_iteration_depth=0; if (!ValidMove(1,wtm,ponder_move)) { ponder_move=0; return(0); } } /* ---------------------------------------------------------- | | | display the move we are going to "ponder". | | | ---------------------------------------------------------- */ if (wtm) printf("White(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); else printf("Black(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); sprintf(hint,"%s",OutputMove(&ponder_move,0,wtm)); if (post) printf("Hint: %s\n",hint); /* ---------------------------------------------------------- | | | set the ponder move list and eliminate illegal moves. | | | ---------------------------------------------------------- */ n_ponder_moves=GenerateCaptures(0, wtm, ponder_moves); num_ponder_moves=GenerateNonCaptures(0, wtm, n_ponder_moves)-ponder_moves; for (mv=ponder_moves;mv<ponder_moves+num_ponder_moves;mv++) { MakeMove(0, *mv, wtm); if (Check(wtm)) { UnMakeMove(0, *mv, wtm); *mv=0; } else UnMakeMove(0, *mv, wtm); } /* ---------------------------------------------------------- | | | now, perform an iterated search, but with the special | | "pondering" flag set which changes the time controls | | since there is no need to stop searching until the | | opponent makes a move. | | | ---------------------------------------------------------- */ MakeMove(0,ponder_move,wtm); if (Rule50Moves(1)==90 || Rule50Moves(1)==91) ClearHashTables(); last_opponent_move=ponder_move; if (ChangeSide(wtm)) *rephead_w++=HashKey; else *rephead_b++=HashKey; if (RepetitionDraw(wtm)) printf("game is a draw by repetition\n"); if (whisper) strcpy(whisper_text,"n/a"); thinking=0; pondering=1; (void) Iterate(ChangeSide(wtm),think,0); pondering=0; thinking=0; if (ChangeSide(wtm)) rephead_w--; else rephead_b--; last_opponent_move=0; UnMakeMove(0,ponder_move,wtm); /* ---------------------------------------------------------- | | | search completed. the possible return values are: | | | | (0) no pondering was done, period. | | | | (1) pondering was done, opponent made the predicted | | move, and we searched until time ran out in a | | normal manner. | | | | (2) pondering was done, but the ponder search | | terminated due to either finding a mate, or the | | maximum search depth was reached. the result of | | this ponder search are valid, but only if the | | opponent makes the correct (predicted) move. | | | | (3) pondering was done, but the opponent either made | | a different move, or entered a command that has to | | interrupt the pondering search before the command | | (or move) can be processed. this forces Main() to | | avoid reading in a move/command since one has been | | read into the command buffer already. | | | ---------------------------------------------------------- */ if (made_predicted_move) return(1); if (abort_search) return(3); return(2); }
/* ******************************************************************************** * * * Search() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Search() is * * called whenever there is "depth" remaining so that all moves are subject * * to searching, or when the side to move is in check, to make sure that this * * side isn't mated. Search() recursively calls itself until depth is ex- * * hausted, at which time it calls Quiesce() instead. * * * ******************************************************************************** */ int Search(int alpha, int beta, int wtm, int depth, int ply, int do_null) { register int first_move=1; register BITBOARD save_hash_key; register int initial_alpha, value; register int extensions; /* ---------------------------------------------------------- | | | check to see if we have searched enough nodes that it | | is time to peek at how much time has been used, or if | | is time to check for operator keyboard input. this is | | usually enough nodes to force a time/input check about | | once per second, except when the target time per move | | is very small, in which case we try to check the time | | at least 10 times during the search. | | | ---------------------------------------------------------- */ if (ply >= MAXPLY-2) return(beta); nodes_searched++; if (--next_time_check <= 0) { next_time_check=nodes_between_time_checks; if (CheckInput()) Interrupt(ply); time_abort+=TimeCheck(0); if (time_abort) { abort_search=1; return(0); } } /* ---------------------------------------------------------- | | | check for draw by repetition. | | | ---------------------------------------------------------- */ if (RepetitionCheck(ply,wtm)) { value=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if (value < beta) SavePV(ply,value,0); #if !defined(FAST) if(ply <= trace_level) printf("draw by repetition detected, ply=%d.\n",ply); #endif return(value); } /* ---------------------------------------------------------- | | | now call LookUp() to see if this position has been | | searched before. if so, we may get a real score, | | produce a cutoff, or get nothing more than a good move | | to try first. there are four cases to handle: | | | | 1. LookUp() returned "EXACT_SCORE" if this score is | | greater than beta, return beta. otherwise, return the | | score. In either case, no further searching is needed | | from this position. note that lookup verified that | | the table position has sufficient "draft" to meet the | | requirements of the current search depth remaining. | | | | 2. LookUp() returned "LOWER_BOUND" which means that | | when this position was searched previously, every move | | was "refuted" by one of its descendents. as a result, | | when the search was completed, we returned alpha at | | that point. we simply return alpha here as well. | | | | 3. LookUp() returned "UPPER_BOUND" which means that | | when we encountered this position before, we searched | | one branch (probably) which promptly refuted the move | | at the previous ply. | | | | 4. LookUp() returned "AVOID_NULL_MOVE" which means | | the hashed score/bound was no good, but it indicated | | that trying a null-move in this position will be a | | waste of time. | | | ---------------------------------------------------------- */ switch (LookUp(ply,depth,wtm,&alpha,beta)) { case EXACT_SCORE: if(alpha >= beta) return(beta); else { SavePV(ply,alpha,1); return(alpha); } case LOWER_BOUND: return(alpha); case UPPER_BOUND: return(beta); case AVOID_NULL_MOVE: do_null=0; } /* ---------------------------------------------------------- | | | now it's time to try a probe into the endgame table- | | base files. this is done if (a) the previous move was | | a capture or promotion, unless we are at very shallow | | plies (<4) in the search; (b) there are less than 5 | | pieces left (currently all interesting 4 piece endings | | are available.) | | | ---------------------------------------------------------- */ #if defined(TABLEBASES) if (TotalPieces < 5) do { register int wpawn, bpawn; int tb_value; if (TotalWhitePawns && TotalBlackPawns) { wpawn=FirstOne(WhitePawns); bpawn=FirstOne(BlackPawns); if (FileDistance(wpawn,bpawn) == 1) { if(((Rank(wpawn)==1) && (Rank(bpawn)>2)) || ((Rank(bpawn)==6) && (Rank(wpawn)<5)) || EnPassant(ply)) break; } } tb_probes++; if (EGTBScore(ply, wtm, &tb_value)) { tb_probes_successful++; alpha=tb_value; if (abs(alpha) > MATE-100) alpha+=(alpha > 0) ? -(ply-1) : +(ply-1); else if (alpha == 0) alpha=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if(alpha >= beta) return(beta); else { SavePV(ply,alpha,2); return(alpha); } } } while(0); # endif /* ---------------------------------------------------------- | | | initialize. | | | ---------------------------------------------------------- */ in_check[ply+1]=0; extended_reason[ply+1]=no_extension; initial_alpha=alpha; last[ply]=last[ply-1]; killer_count1[ply+1]=0; killer_count2[ply+1]=0; /* ---------------------------------------------------------- | | | first, we try a null move to see if we can get a quick | | cutoff with only a little work. this operates as | | follows. instead of making a legal move, the side on | | move 'passes' and does nothing. the resulting position | | is searched to a shallower depth than normal (usually | | one ply less but settable by the operator) this should | | result in a cutoff or at least should set the lower | | bound better since anything should be better than not | | doing anything. | | | | this is skipped for any of the following reasons: | | | | 1. the side on move is in check. the null move | | results in an illegal position. | | 2. no more than one null move can appear in succession | | or else the search will degenerate into nothing. | | 3. the side on move has little material left making | | zugzwang positions more likely. | | | ---------------------------------------------------------- */ # if defined(NULL_MOVE_DEPTH) if (do_null && !in_check[ply] && ((wtm) ? TotalWhitePieces : TotalBlackPieces)>2) { current_move[ply]=0; current_phase[ply]=NULL_MOVE; #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,depth,wtm,alpha,beta,"Search",0); #endif position[ply+1]=position[ply]; Rule50Moves(ply+1)++; save_hash_key=HashKey; if (EnPassant(ply)) { HashEP(EnPassant(ply+1),HashKey); EnPassant(ply+1)=0; } if ((depth-NULL_MOVE_DEPTH-INCREMENT_PLY) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth-NULL_MOVE_DEPTH-INCREMENT_PLY,ply+1,NO_NULL); else value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); HashKey=save_hash_key; if (abort_search) return(0); if (value >= beta) { StoreRefutation(ply,depth,wtm,beta); return(beta); } } # endif /* ---------------------------------------------------------- | | | if there is no best move from the hash table, and this | | is a PV node, then we need a good move to search | | first. while killers and history moves are good, they | | are not "good enough". the simplest action is to try | | a shallow search (depth-2) to get a move. note that | | when we call Search() with depth-2, it, too, will | | not have a hash move, and will therefore recursively | | continue this process, hence the name "internal | | iterative deepening." | | | ---------------------------------------------------------- */ next_status[ply].phase=FIRST_PHASE; if (hash_move[ply]==0 && (depth > 2*INCREMENT_PLY) && (((ply & 1) && alpha == root_alpha && beta == root_beta) || (!(ply & 1) && alpha == -root_beta && beta == -root_alpha))) { current_move[ply]=0; value=Search(alpha,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL); if (abort_search) return(0); if (value <= alpha) { value=Search(-MATE,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL); if (abort_search) return(0); } else if (value < beta) { if ((int) pv[ply-1].path_length >= ply) hash_move[ply]=pv[ply-1].path[ply]; } else hash_move[ply]=current_move[ply]; last[ply]=last[ply-1]; next_status[ply].phase=FIRST_PHASE; } /* ---------------------------------------------------------- | | | now iterate through the move list and search the | | resulting positions. note that Search() culls any | | move that is not legal by using Check(). the special | | case is that we must find one legal move to search to | | confirm that it's not a mate or draw. | | | ---------------------------------------------------------- */ while ((current_phase[ply]=(in_check[ply]) ? NextEvasion(ply,wtm) : NextMove(depth,ply,wtm))) { extended_reason[ply]&=check_extension; #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,depth,wtm,alpha,beta,"Search",current_phase[ply]); #endif /* ---------------------------------------------------------- | | | if two successive moves are capture / re-capture so | | that the material score is restored, extend the search | | by one ply on the re-capture since it is pretty much | | forced and easy to analyze. | | | ---------------------------------------------------------- */ extensions=-INCREMENT_PLY; if (Captured(current_move[ply]) && Captured(current_move[ply-1]) && To(current_move[ply-1]) == To(current_move[ply]) && (p_values[Captured(current_move[ply-1])+7] == p_values[Captured(current_move[ply])+7] || Promote(current_move[ply-1])) && !(extended_reason[ply-1]&recapture_extension)) { extended_reason[ply]|=recapture_extension; recapture_extensions_done++; extensions+=RECAPTURE; } /* ---------------------------------------------------------- | | | if we push a passed pawn, we need to look deeper to | | see if it is a legitimate threat. | | | ---------------------------------------------------------- */ if (Piece(current_move[ply])==pawn && !FutileAhead(wtm) && ((wtm && To(current_move[ply])>H5 && TotalBlackPieces<16 && !And(mask_pawn_passed_w[To(current_move[ply])],BlackPawns)) || (!wtm && To(current_move[ply])<A4 && TotalWhitePieces<16 && !And(mask_pawn_passed_b[To(current_move[ply])],WhitePawns)) || push_extensions[To(current_move[ply])]) && Swap(From(current_move[ply]),To(current_move[ply]),wtm) >= 0) { extended_reason[ply]|=passed_pawn_extension; passed_pawn_extensions_done++; extensions+=PASSED_PAWN_PUSH; } /* ---------------------------------------------------------- | | | now make the move and search the resulting position. | | if we are in check, the current move must be legal | | since NextEvasion ensures this, otherwise we have to | | make sure the side-on-move is not in check after the | | move to weed out illegal moves and save time. | | | ---------------------------------------------------------- */ MakeMove(ply,current_move[ply],wtm); if (in_check[ply] || !Check(wtm)) { /* ---------------------------------------------------------- | | | if the move to be made checks the opponent, then we | | need to remember that he's in check and also extend | | the depth by one ply for him to get out. | | | ---------------------------------------------------------- */ if (Check(ChangeSide(wtm))) { in_check[ply+1]=1; extended_reason[ply+1]=check_extension; check_extensions_done++; extensions+=IN_CHECK; } else { in_check[ply+1]=0; extended_reason[ply+1]=no_extension; } /* ---------------------------------------------------------- | | | now we toss in the "razoring" trick, which simply says | | if we are doing fairly badly, we can reduce the depth | | an additional ply, if there was nothing at the current | | ply that caused an extension. | | | ---------------------------------------------------------- */ if (depth < 3*INCREMENT_PLY && !in_check[ply] && extensions == -INCREMENT_PLY) { register int val=(wtm) ? Material : -Material; if (val+1500 < alpha) extensions-=INCREMENT_PLY; } /* ---------------------------------------------------------- | | | if there's only one legal move, extend the search one | | additional ply since this node is very easy to search. | | | ---------------------------------------------------------- */ if (first_move) { if (last[ply]-last[ply-1] == 1) { extended_reason[ply]|=one_reply_extension; one_reply_extensions_done++; extensions+=ONE_REPLY_TO_CHECK; } if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else { value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); } if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } first_move=0; } else { if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-alpha-1,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else { value=-Quiesce(-alpha-1,-alpha,ChangeSide(wtm),ply+1); } if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } if (value>alpha && value<beta) { if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } } } if (value > alpha) { if(value >= beta) { HistoryRefutation(ply,depth,wtm); UnMakeMove(ply,current_move[ply],wtm); StoreRefutation(ply,depth,wtm,beta); return(beta); } alpha=value; } } UnMakeMove(ply,current_move[ply],wtm); } /* ---------------------------------------------------------- | | | all moves have been searched. if none were legal, | | return either MATE or DRAW depending on whether the | | side to move is in check or not. | | | ---------------------------------------------------------- */ if (first_move == 1) { value=(Check(wtm)) ? -(MATE-ply) : ((wtm==root_wtm) ? DrawScore() : -DrawScore()); if(value > beta) value=beta; else if (value < alpha) value=alpha; if (value >=alpha && value <beta) { SavePV(ply,value,0); #if !defined(FAST) if (ply <= trace_level) printf("Search() no moves! ply=%d\n",ply); #endif } return(value); } else { if (alpha != initial_alpha) { memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply],(pv[ply].path_length-ply+1)*4); memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3); pv[ply-1].path[ply-1]=current_move[ply-1]; HistoryBest(ply,depth,wtm); } StoreBest(ply,depth,wtm,alpha,initial_alpha); /* ---------------------------------------------------------- | | | if the 50-move rule is drawing close, then adjust the | | score to reflect the impending draw. | | | ---------------------------------------------------------- */ if (Rule50Moves(ply) > 99) { value=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if (value < beta) SavePV(ply,value,0); #if !defined(FAST) if(ply <= trace_level) printf("draw by 50-move rule detected, ply=%d.\n",ply); #endif return(value); } return(alpha); } }
int CAlphaBeta_TTEngine::AlphaBeta(int nDepth, int alpha, int beta) { int score; int Count,i; BYTE type; int side; i=IsGameOver(CurPosition,nDepth); if(i!=0) return i; //察看当前节点是否在置换表中有有效数据 side=(m_nMaxDepth-nDepth)%2; score=LookUpHashTable(alpha,beta,nDepth,side); if(score!=66666) return score;//命中,直接返回表中的值 //叶子节点取估值 if(nDepth<=0) { score=m_pEval->Eveluate(CurPosition,side,m_nUserChessColor); EnterHashTable(exact,score,nDepth,side);//将求得的估值放进置换表 return score; } Count=m_pMG->CreatePossibleMove(CurPosition,nDepth,side,m_nUserChessColor); if(nDepth==m_nMaxDepth) { //在根节点设定进度条 progress->setTotalSteps(Count); progress->setProgress(1); } int eval_is_exact=0;//数据类型标志 for(i=0;i<Count;i++)//对当前节点的下一步每一可能的走法 { if(nDepth==m_nMaxDepth) progress->nextStep(); Hash_MakeMove(&m_pMG->m_MoveList[nDepth][i],CurPosition);//产生该走法所对应子节点的哈希值 type=MakeMove(&m_pMG->m_MoveList[nDepth][i]); //产生子节点 score=-AlphaBeta(nDepth-1,-beta,-alpha);//递归搜索子节点 Hash_UnMakeMove(&m_pMG->m_MoveList[nDepth][i],type,CurPosition);//恢复当前节点的哈希值 UnMakeMove(&m_pMG->m_MoveList[nDepth][i],type); //撤销子节点 if(score>=beta)//beta剪枝 { EnterHashTable(lower_bound,score,nDepth,side);//将节点下边界存入置换表 return score;//返回下边界 } if(score>alpha) { alpha=score; //取最大值 eval_is_exact=1;//设定确切值标志 if(nDepth==m_nMaxDepth) m_cmBestMove=m_pMG->m_MoveList[nDepth][i]; } } //将搜索结果放进置换表 if(eval_is_exact) EnterHashTable(exact,alpha,nDepth,side); //确切值 else EnterHashTable(upper_bound,alpha,nDepth,side);//上边界 return alpha;//返回最佳值/上界 }
/* ******************************************************************************** * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. then, make this move and * * do a search from the resulting position. while pondering, one of three * * things can happen: (1) a move is entered, and it matches the predicted * * move. we then switch from pondering to thinking and search as normal; * * (2) a move is entered, but it does not match the predicted move. we then * * abort the search, unmake the pondered move, and then restart with the move * * entered. (3) a command is entered. if it is a simple command, it can be * * done without aborting the search or losing time. if not, we abort the * * search, execute the command, and then attempt to restart pondering if the * * command didn't make that impossible. * * * ******************************************************************************** */ int Ponder(int wtm) { int dummy=0, i, *n_ponder_moves; /* ---------------------------------------------------------- | | | if we don't have a predicted move to ponder, try two | | sources: (1) look up the current position in the | | transposition table and see if it has a suggested best | | move; (2) do a short tree search to calculate a move | | that we should ponder. | | | ---------------------------------------------------------- */ ponder_completed=0; if (!ponder_move) { (void) LookUp(0,0,wtm,&dummy,dummy); if (hash_move[0]) ponder_move=hash_move[0]; } if (!ponder_move) { TimeSet(puzzle); if (time_limit < 3) return(0); puzzling=1; position[1]=position[0]; Print(2," puzzling over a move to ponder.\n"); last_pv.path_length=0; last_pv.path_iteration_depth=0; for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } (void) Iterate(wtm,puzzle); for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } puzzling=0; if (pv[0].path_length) ponder_move=pv[0].path[1]; if (!ponder_move) return(0); for (i=1;i<(int) pv[0].path_length-1;i++) last_pv.path[i]=pv[0].path[i+1]; last_pv.path_length=pv[0].path_length-1; last_pv.path_iteration_depth=0; if (!ValidMove(1,wtm,ponder_move)) { printf("puzzle returned an illegal move!\n"); DisplayChessMove("move= ",ponder_move); ponder_move=0; return(0); } } if (!ponder_move) { strcpy(hint,"none"); return(0); } /* ---------------------------------------------------------- | | | display the move we are going to "ponder". | | | ---------------------------------------------------------- */ if (wtm) Print(2,"White(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); else Print(2,"Black(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); sprintf(hint,"%s",OutputMove(&ponder_move,0,wtm)); n_ponder_moves=GenerateCaptures(0, wtm, ponder_moves); num_ponder_moves=GenerateNonCaptures(0, wtm, n_ponder_moves)-ponder_moves; /* ---------------------------------------------------------- | | | now, perform an iterated search, but with the special | | "pondering" flag set which changes the time controls | | since there is no need to stop searching until the | | opponent makes a move. | | | ---------------------------------------------------------- */ MakeMove(0,ponder_move,wtm); if (Rule50Moves(1)==90 || Rule50Moves(1)==91) ClearHashTables(); last_opponent_move=ponder_move; if (ChangeSide(wtm)) *rephead_w++=HashKey; else *rephead_b++=HashKey; if (RepetitionDraw(wtm)) Print(0,"game is a draw by repetition\n"); if (whisper) strcpy(whisper_text,"n/a"); pondering=1; (void) Iterate(ChangeSide(wtm),think); pondering=0; if (!abort_search) ponder_completed=1; if (ChangeSide(wtm)) rephead_w--; else rephead_b--; last_opponent_move=0; UnMakeMove(0,ponder_move,wtm); /* ---------------------------------------------------------- | | | search completed. there are two possible reasons for | | us to get here. (1) the opponent made the predicted | | move and we have used enough time on the search, or; | | the operator typed in a command (or not-predicted | | move) that requires the search to abort and restart. | | | ---------------------------------------------------------- */ if (made_predicted_move) return(1); return(0); }
/* ******************************************************************************** * * * Quiesce() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Quiesce() is * * called whenever there is no "depth" remaining so that only capture moves * * are searched deeper. * * * ******************************************************************************** */ int Quiesce(int alpha, int beta, int wtm, int ply) { register int initial_alpha, value, delta; register int *next_move; register int *goodmv, *movep, moves=0, *sortv, temp; /* ---------------------------------------------------------- | | | initialize. | | | ---------------------------------------------------------- */ if (ply >= MAXPLY-2) return(beta); nodes_searched++; next_time_check--; last[ply]=last[ply-1]; initial_alpha=alpha; /* ---------------------------------------------------------- | | | now call Evaluate() to produce the "stand-pat" score | | that will be returned if no capture is acceptable. | | if this score is > alpha, then we also have to save | | the "path" to this node as it is the PV that leads | | to this score. | | | ---------------------------------------------------------- */ value=Evaluate(ply,wtm,alpha,beta); if (value > alpha) { if (value >= beta) return(value); alpha=value; pv[ply].path_length=ply-1; pv[ply].path_hashed=0; pv[ply].path_iteration_depth=iteration_depth; } /* ---------------------------------------------------------- | | | generate captures and sort them based on (a) the value | | of the captured piece - the value of the capturing | | piece if this is > 0; or, (b) the value returned by | | Swap(). if the value of the captured piece won't | | bring the material score back up to near alpha, that | | capture is discarded as "futile." | | | ---------------------------------------------------------- */ last[ply]=GenerateCaptures(ply, wtm, last[ply-1]); delta=alpha-500-(wtm?Material:-Material); goodmv=last[ply-1]; sortv=sort_value; for (movep=last[ply-1];movep<last[ply];movep++) if (p_values[Captured(*movep)+7]+p_values[Promote(*movep)+7] >= delta) { if (Captured(*movep) == king) return(beta); if (p_values[Piece(*movep)+7] < p_values[Captured(*movep)+7] || (p_values[Piece(*movep)+7] <= p_values[Captured(*movep)+7] && delta<=0)) { *goodmv++=*movep; *sortv++=p_values[Captured(*movep)+7]; moves++; } else { temp=Swap(From(*movep),To(*movep),wtm); if (temp >= 0) { *sortv++=temp; *goodmv++=*movep; moves++; } } } /* ---------------------------------------------------------- | | | don't disdain the lowly bubble sort here. the list of | | captures is always short, and experiments with other | | algorithms are always slightly slower. | | | ---------------------------------------------------------- */ if (moves > 1) { register int done; register int *end=last[ply-1]+moves-1; do { done=1; sortv=sort_value; movep=last[ply-1]; for (;movep<end;movep++,sortv++) if (*sortv < *(sortv+1)) { temp=*sortv; *sortv=*(sortv+1); *(sortv+1)=temp; temp=*movep; *movep=*(movep+1); *(movep+1)=temp; done=0; } } while(!done); } next_move=last[ply-1]; /* ---------------------------------------------------------- | | | now iterate through the move list and search the | | resulting positions. | | | ---------------------------------------------------------- */ while (moves--) { current_move[ply]=*(next_move++); #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,0,wtm,alpha,beta,"quiesce",CAPTURE_MOVES); #endif MakeMove(ply,current_move[ply],wtm); value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); UnMakeMove(ply,current_move[ply],wtm); if (value > alpha) { if(value >= beta) return(value); alpha=value; } } /* ---------------------------------------------------------- | | | all moves have been searched. return the search | | result that was found. if the result is not the | | original alpha score, then we need to return the PV | | that is associated with this score. | | | ---------------------------------------------------------- */ if (alpha != initial_alpha) { memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply], (pv[ply].path_length-ply+1)*sizeof(int)); memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3); pv[ply-1].path[ply-1]=current_move[ply-1]; } return(alpha); }
int CNegaScout_TT_HH::NegaScout(int depth, int alpha, int beta) { bool bIsSure = false; int Count,i; int a,b,t; int side; int score; int mtype = (m_nMaxDepth%2 == depth%2) ? (-1) : (1); CPublicToMakeMove ptmm; i = IsGameOver(CurPosition, depth,mtype); if (i != 0) return i; side = (m_nMaxDepth-depth)%2; score = LookUpHashTable(alpha, beta, depth, side); if (score != 6666666) { G_nCountTTHH++; return score; } if (depth <= 0) //叶子节点取估值 { int Now1 = GetTickCount(); score = m_pEval->Eveluate(CurPosition,mtype,(m_nMaxDepth-depth)%2); ETime += GetTickCount() - Now1; EnterHashTable(exact, score, depth, side ); return score; } int Now2 = GetTickCount(); Count = m_pMG->CreatePossibleMove(CurPosition, depth, mtype); GTime += GetTickCount() - Now2; if(1 == Count && depth == m_nMaxDepth) { m_cmBestMove = m_pMG->m_nMoveList[depth][0]; return 0; } for (i=0;i<Count;i++) { m_pMG->m_nMoveList[depth][i].Score = GetHistoryScore(&m_pMG->m_nMoveList[depth][i]); } MergeSort(m_pMG->m_nMoveList[depth], Count, 0); int bestmove=-1; a = alpha; b = beta; int eval_is_exact = 0; for ( i = 0; i < Count; i++ ) { Hash_MakeMove(&m_pMG->m_nMoveList[depth][i], CurPosition); MakeMove(&m_pMG->m_nMoveList[depth][i],ptmm); t = -NegaScout(depth-1 , -b, -a); if (t > a && t < beta && i > 0) { a = -NegaScout (depth-1, -beta, -t); /* re-search */ eval_is_exact = 1; if(depth == m_nMaxDepth) { bIsSure = true; Hash_UnMakeMove(&m_pMG->m_nMoveList[depth][i],CurPosition); UnMakeMove(&m_pMG->m_nMoveList[depth][i],ptmm); m_cmBestMove = m_pMG->m_nMoveList[depth][i]; } bestmove = i; } if(bIsSure == false) { Hash_UnMakeMove(&m_pMG->m_nMoveList[depth][i],CurPosition); UnMakeMove(&m_pMG->m_nMoveList[depth][i],ptmm); } else { bIsSure = false; } if (a < t) { eval_is_exact = 1; a=t; if(depth == m_nMaxDepth) m_cmBestMove = m_pMG->m_nMoveList[depth][i]; } if ( a >= beta ) { EnterHashTable(lower_bound, a, depth,side); EnterHistoryScore(&m_pMG->m_nMoveList[depth][i], depth); return a; } b = a + 1; /* set new null window */ } if (bestmove != -1) EnterHistoryScore(&m_pMG->m_nMoveList[depth][bestmove], depth); if (eval_is_exact) EnterHashTable(exact, a, depth,side); else EnterHashTable(upper_bound, a, depth,side); return a; }
/* ******************************************************************************** * * * "annotate" command is used to search through the game in the "history" file * * (often set by the "read" command which reads moves in, skipping non-move * * information such as move numbers, times, etc.) * * * * the normal output of this command is a file, in PGN format, that contains * * the moves of the game, along with analysis when Crafty does not think that * * move was the best choice. the definition of "best choice" is somewhat * * vague, because if the move played is "close" to the best move available, * * Crafty will not comment on the move. "close" is defined by the <margin> * * option explained below. this basic type of annotation works by first * * using the normal tree search algorithm to find the best move. if this * * move was the move played, no output is produced. if a different move is * * considered best, then the actual move played is searched to the same depth * * and if the best move and actual move scores are within <margin> of each * * other, no comment is produced, otherwise crafty inserts the evaluation for * * the move played, followed by the eval and PV for the best continuation it * * found. * * * * the format of the command is as follows: * * * * annotate filename b|w|bw moves margin time [n] * * * * filename is the input file where Crafty will obtain the moves to annotate, * * and output will be written to file "filename.ann". * * * * where b/w/bw indicates whether to annotate only the white side (w), the * * black side (b) or both (bw). * * * * moves indicates the move or moves to annotate. it can be a single move, * * which indicates the starting move number to annotate, or it can be a range, * * which indicates a range of move (1-999 gets the whole game.) * * * * margin is the difference between Crafty's evaluation for the move actually * * played and for the move Crafty thinks is best, before crafty will generate * * a comment in the annotation file. 1.0 is a pawn, and will only generate * * comments if the move played is 1.000 (1 pawn) worse than the best move * * found by doing a complete search. * * * * time is time per move to search, in seconds. * * * * [n] is optional and tells Crafty to produce the PV/score for the "n" best * * moves. Crafty stops when the best move reaches the move played in the game * * or after displaying n moves, whichever comes first. if you use -n, then it * * will display n moves regardless of where the game move ranks. * * * ******************************************************************************** */ void Annotate() { FILE *annotate_in, *annotate_out; char command[80], text[128], colors[32], next; int annotate_margin, annotate_score[100], player_score, best_moves; int annotate_search_time_limit, search_player; int twtm, path_len, analysis_printed=0, *mv; int wtm, move_number, move_num, line1, line2, move, suggested, i; int temp_draw_score_is_zero, searches_done; CHESS_PATH temp[100], player_pv; /* ---------------------------------------------------------- | | | first, quiz the user for the options needed to | | successfully annotate a game. | | | ---------------------------------------------------------- */ Print(0,"\nannotate filename: "); fscanf(input_stream,"%s",text); annotate_in = fopen(text,"r"); if (annotate_in == NULL) { Print(0,"unable to open %s for input\n", text); return; } sprintf(command,"read=%s",text); Option(command); strcpy(text+strlen(text),".can"); annotate_out = fopen(text,"w"); if (annotate_out == NULL) { Print(0,"unable to open %s for output\n", text); return; } Print(0,"\ncolor(s): "); fscanf(input_stream,"%s",colors); line1=1; line2=999; Print(0,"\nstarting move number or range: "); fscanf(input_stream,"%s",text); if(strchr(text,'-')) sscanf(text,"%d-%d",&line1,&line2); else { sscanf(text,"%d",&line1); line2=999; } Print(0,"\nannotation margin: "); fscanf(input_stream,"%s",text); annotate_margin=atof(text)*PAWN_VALUE; Print(0,"\ntime per move: "); fscanf(input_stream,"%s",text); annotate_search_time_limit=atoi(text)*100; next=getc(input_stream); best_moves=1; if (next == ' ') { fscanf(input_stream,"%s",text); best_moves=atoi(text); } /* ---------------------------------------------------------- | | | reset the game to "square 0" to start the annotation | | procedure. then we read moves from the input file, | | make them on the game board, and annotate if the move | | is for the correct side. if we haven't yet reached | | the starting move to annotate, we skip the Search() | | stuff and read another move. | | | ---------------------------------------------------------- */ annotate_mode=1; temp_draw_score_is_zero=draw_score_is_zero; draw_score_is_zero=1; ponder_completed=0; ponder_move=0; last_pv.path_iteration_depth=0; last_pv.path_length=0; InitializeChessBoard(&position[0]); wtm=1; move_number=1; fprintf(annotate_out,"[Event \"%s\"]\n",pgn_event); fprintf(annotate_out,"[Site \"%s\"]\n",pgn_site); fprintf(annotate_out,"[Date \"%s\"]\n",pgn_date); fprintf(annotate_out,"[Round \"%s\"]\n",pgn_round); fprintf(annotate_out,"[White \"%s\"]\n",pgn_white); fprintf(annotate_out,"[WhiteElo \"%s\"]\n",pgn_white_elo); fprintf(annotate_out,"[Black \"%s\"]\n",pgn_black); fprintf(annotate_out,"[BlackElo \"%s\"]\n",pgn_black_elo); fprintf(annotate_out,"[Result \"%s\"]\n",pgn_result); fprintf(annotate_out,"[Annotator \"Crafty v%s\"]\n",version); if (!strcmp(colors,"bw") || !strcmp(colors,"wb")) fprintf(annotate_out,"{annotating both black and white moves.}\n"); else if (strchr(colors,'b')) fprintf(annotate_out,"{annotating only black moves.}\n"); else if (strchr(colors,'w')) fprintf(annotate_out,"{annotating only white moves.}\n"); fprintf(annotate_out,"{using a scoring margin of %s pawns.}\n", DisplayEvaluationWhisper(annotate_margin)); fprintf(annotate_out,"{search time limit is %s}\n\n", DisplayTimeWhisper(annotate_search_time_limit)); do { fflush(annotate_out); move=ReadChessMove(annotate_in,wtm,0); if (move <= 0) break; strcpy(text,OutputMove(&move,0,wtm)); fseek(history_file,((move_number-1)*2+1-wtm)*10,SEEK_SET); fprintf(history_file,"%10s ",text); if (wtm) Print(0,"White(%d): %s\n",move_number,text); else Print(0,"Black(%d): %s\n",move_number,text); if (analysis_printed) fprintf(annotate_out,"%3d.%s%6s\n",move_number,(wtm?"":" ..."),text); else { if (wtm) fprintf(annotate_out,"%3d.%6s",move_number,text); else fprintf(annotate_out,"%6s\n",text); } analysis_printed=0; if (move_number >= line1) { if ((!wtm && strchr(colors,'b')) | ( wtm && strchr(colors,'w'))) { last_pv.path_iteration_depth=0; last_pv.path_length=0; thinking=1; RootMoveList(wtm); /* ---------------------------------------------------------- | | | now search the position to see if the move played is | | the best move possible. if not, then search just the | | move played to get a score for it as well, so we can | | determine if annotated output is appropriate. | | | ---------------------------------------------------------- */ search_time_limit=annotate_search_time_limit; search_depth=0; player_score=-999999; search_player=1; for (searches_done=0;searches_done<abs(best_moves);searches_done++) { if (searches_done > 0) { search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; } Print(0,"\n Searching all legal moves."); Print(0,"----------------------------------\n"); position[1]=position[0]; InitializeHashTables(); annotate_score[searches_done]=Iterate(wtm,annotate); if (pv[0].path[1] == move) { player_score=annotate_score[searches_done]; player_pv=pv[0]; search_player=0; } temp[searches_done]=pv[0]; for (mv=last[0];mv<last[1];mv++) { if (*mv == pv[0].path[1]) { for (;mv<last[1]-1;mv++) *mv=*(mv+1); last[1]--; break; } } if (last[1] < last[0] || (player_score+annotate_margin>annotate_score[searches_done] && best_moves>0)) break; } if (search_player) { Print(0,"\n Searching only the move played in game."); Print(0,"--------------------\n"); position[1]=position[0]; search_move=move; search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; if (search_depth==0) search_time_limit=annotate_search_time_limit; InitializeHashTables(); player_score=Iterate(wtm,annotate); player_pv=pv[0]; search_depth=0; search_time_limit=annotate_search_time_limit; search_move=0; } /* ---------------------------------------------------------- | | | output the score/pv for the move played unless it | | matches what Crafty would have played. if it doesn't | | then output the pv for what Crafty thinks is best. | | | ---------------------------------------------------------- */ thinking=0; if (player_pv.path_iteration_depth>1 && player_pv.path_length>=1 && player_score+annotate_margin<annotate_score[0] && (temp[0].path[1]!=player_pv.path[1] || annotate_margin<0.0 || best_moves!=1)) { if (wtm) { analysis_printed=1; fprintf(annotate_out,"\n"); } twtm = wtm; fprintf(annotate_out," {%d:%s", player_pv.path_iteration_depth, DisplayEvaluationWhisper(player_score)); path_len=player_pv.path_length; for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s", OutputMove(&player_pv.path[i],i,twtm)); MakeMove(i,player_pv.path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,player_pv.path[i],twtm); } fprintf(annotate_out,"}\n"); for (move_num=0;move_num<searches_done;move_num++) { twtm = wtm; if (move != temp[move_num].path[1]) { fprintf(annotate_out," {%d:%s", temp[move_num].path_iteration_depth, DisplayEvaluationWhisper(annotate_score[move_num])); path_len=temp[move_num].path_length; for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s", OutputMove(&temp[move_num].path[i],i,twtm)); MakeMove(i,temp[move_num].path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,temp[move_num].path[i],twtm); } fprintf(annotate_out,"}\n"); } } } } } /* ---------------------------------------------------------- | | | before going on to the next move, see if the user has | | included a set of other moves that require a search. | | if so, search them one at a time and produce the ana- | | lysis for each one. | | | ---------------------------------------------------------- */ do { next=getc(annotate_in); } while (next==' ' || next=='\n'); ungetc(next,annotate_in); if (next == EOF) break; if (next == '{') do { do { next=getc(annotate_in); } while (next==' '); ungetc(next,annotate_in); if (next == EOF || next == '}') break; suggested=ReadChessMove(annotate_in,wtm,1); if (suggested < 0) break; if (suggested > 0) { thinking=1; Print(0,"\n Searching only the move suggested."); Print(0,"--------------------\n"); position[1]=position[0]; search_move=suggested; search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; InitializeHashTables(); annotate_score[0]=Iterate(wtm,annotate); search_depth=0; search_time_limit=annotate_search_time_limit; search_move=0; thinking=0; twtm = wtm; path_len = pv[0].path_length; if (pv[0].path_iteration_depth > 1 && path_len >= 1) { if (wtm && !analysis_printed) { analysis_printed=1; fprintf(annotate_out,"\n"); } fprintf(annotate_out," {suggested %d:%s", pv[0].path_iteration_depth, DisplayEvaluationWhisper(annotate_score[0])); for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s",OutputMove(&pv[0].path[i],i,twtm)); MakeMove(i,pv[0].path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,pv[0].path[i],twtm); } fprintf(annotate_out,"}\n"); } } } while(1); MakeMoveRoot(move,wtm); wtm=ChangeSide(wtm); if (wtm) move_number++; if (move_number > line2) break; } while (1); fprintf(annotate_out,"\n"); if (annotate_out) fclose(annotate_out); if (annotate_in) fclose(annotate_in); search_time_limit=0; draw_score_is_zero=temp_draw_score_is_zero; annotate_mode=0; }
int CNegaScout::NegaScout(int depth, int alpha, int beta) { int Count,i; BYTE type; int a,b,t; int side; int score; i = IsGameOver(CurPosition, depth); if (i != 0) return i; side = (m_nMaxDepth-depth)%2; score = LookUpHashTable(alpha, beta, depth, side); if (score != 66666) return score; if (depth <= 0) //叶子节点取估值 { score = m_pEval->Eveluate(CurPosition, side ); EnterHashTable(exact, score, depth, side ); return score; } Count = m_pMG->CreatePossibleMove(CurPosition, depth, side); for (i=0;i<Count;i++) { m_pMG->m_MoveList[depth][i].Score = GetHistoryScore(&m_pMG->m_MoveList[depth][i]); } MergeSort(m_pMG->m_MoveList[depth], Count, 0); int bestmove=0; a = alpha; b = beta; int eval_is_exact = 0; for ( i = 0; i < Count; i++ ) { Hash_MakeMove(&m_pMG->m_MoveList[depth][i], CurPosition); type = MakeMove(&m_pMG->m_MoveList[depth][i]); t = -NegaScout(depth-1 , -b, -a ); if (t > a && t < beta && i > 0) { a = -NegaScout (depth-1, -beta, -t ); /* re-search */ eval_is_exact = 1; if(depth == m_nMaxDepth) m_cmBestMove = m_pMG->m_MoveList[depth][i]; bestmove = i; } Hash_UnMakeMove(&m_pMG->m_MoveList[depth][i],type, CurPosition); UnMakeMove(&m_pMG->m_MoveList[depth][i],type); if (a < t) { eval_is_exact = 1; a=t; if(depth == m_nMaxDepth) m_cmBestMove = m_pMG->m_MoveList[depth][i]; } if ( a >= beta ) { EnterHashTable(lower_bound, a, depth,side); EnterHistoryScore(&m_pMG->m_MoveList[depth][i], depth); return a; } b = a + 1; /* set new null window */ } EnterHistoryScore(&m_pMG->m_MoveList[depth][bestmove], depth); if (eval_is_exact) EnterHashTable(exact, a, depth,side); else EnterHashTable(upper_bound, a, depth,side); return a; }
int CNegaScout_TT_HH::NegaScout(int iDepth, int iAlpha, int iBeta) { int iCount,iGameOver; BYTE byChess; int a,b,t; int iSide; int iScore; int i; iGameOver=IsGameOver(byCurChessBoard, iDepth); if(iGameOver!=0) return iGameOver; iSide=(m_iMaxDepth-iDepth)%2;//计算当前节点的类型,极大0/极小1 iScore=LookUpHashTable(iAlpha,iBeta,iDepth,iSide); if(iScore!=66666) return iScore; if(iDepth<=0)//叶子节点取估值 { iScore=m_pEval->Eveluate(byCurChessBoard,iSide); EnterHashTable(Exact,iScore,iDepth,iSide);//将估值存入置换表 return iScore; } iCount=m_pMG->CreatePossibleMove(byCurChessBoard,iDepth,iSide,m_nUserChessColor); if(iDepth==m_iMaxDepth) { //在根节点设定进度条 m_pThinkProgress->SetRange(0,iCount); m_pThinkProgress->SetStep(1); } for(i=0;i<iCount;i++) m_pMG->m_MoveList[iDepth][i].iScore=GetHistoryScore(&m_pMG->m_MoveList[iDepth][i]); MergeSort(m_pMG->m_MoveList[iDepth],iCount,0); int bestmove=-1; a=iAlpha; b=iBeta; int eval_is_exact=0; for(i=0;i<iCount;i++) { if(iDepth==m_iMaxDepth) m_pThinkProgress->StepIt();//走进度条 Hash_MakeMove(&m_pMG->m_MoveList[iDepth][i],byCurChessBoard); byChess=MakeMove(&m_pMG->m_MoveList[iDepth][i]); t=-NegaScout(iDepth-1,-b,-a); if(t>a && t<iBeta && i>0) { //对于第一个后的节点,如果上面的搜索failhigh a=-NegaScout(iDepth-1,-iBeta,-t);//递归搜索子节点 eval_is_exact=1;//设数据类型为精确值 if(iDepth==m_iMaxDepth) m_cmBestMove=m_pMG->m_MoveList[iDepth][i]; bestmove=i; } Hash_UnMakeMove(&m_pMG->m_MoveList[iDepth][i],byChess,byCurChessBoard); UnMakeMove(&m_pMG->m_MoveList[iDepth][i],byChess); if(a<t) { eval_is_exact=1; a=t; if(iDepth==m_iMaxDepth) m_cmBestMove=m_pMG->m_MoveList[iDepth][i]; } if(a>=iBeta) { EnterHashTable(LowerBound,a,iDepth,iSide); EnterHistoryScore(&m_pMG->m_MoveList[iDepth][i],iDepth); return a; } b=a+1;//set new null window } if(bestmove!=-1) EnterHistoryScore(&m_pMG->m_MoveList[iDepth][bestmove],iDepth); if(eval_is_exact) EnterHashTable(Exact,a,iDepth,iSide); else EnterHashTable(UpperBound,a,iDepth,iSide); return a; }