int NegamaxAlphaBetaSearcher::EvaluateNegaMax(GameState& state, int max_player_id) { if(state.GetCurrentPlayer() == max_player_id) return state.Evaluate(max_player_id); else return -state.Evaluate(max_player_id); }
int NegamaxAlphaBetaSearcher::SearchBestPlay(const GameState& state, int depth) { std::vector<int> bestCell; int bestValue = -INFINITY; int bestPos = 0; #ifdef _DEBUG _searcherCounter = 0; #endif ResetTranspositionTable(); GameState tryState = state; int player_id = state.GetCurrentPlayer(); std::vector<MOVES_LIST> moves; int mc = tryState.FindMoves(player_id, moves); if(mc == 0) //遇到无路可走的情况,比如被对方逼着走禁手,可放弃一步 { return -1; } SortMoves(moves); for(int i = 0; i < mc; i++) { tryState.DoPutChess(moves[i].cell, player_id); int value = NegaMax(tryState, depth - 1, -INFINITY, INFINITY, player_id); tryState.UndoPutChess(moves[i].cell); if(value > bestValue) { bestValue = value; bestCell.clear(); bestCell.push_back(moves[i].cell); } else if(value == bestValue) { bestCell.push_back(moves[i].cell); } } if(bestCell.size() > 0) bestPos = rand() % bestCell.size(); #ifdef _DEBUG std::cout << "NegamaxSearcher " << _searcherCounter << " (with Alpha-Beta)" << std::endl; #endif return bestCell[bestPos]; }
int NegamaxAlphaBetaSearcher::NegaMax(GameState& state, int depth, int alpha, int beta, int max_player_id) { int alphaOrig = alpha; #if 0 unsigned int state_hash = state.GetHash(); //查询置换表 TT_ENTRY ttEntry = { 0 }; if(LookupTranspositionTable(state_hash, ttEntry) && (ttEntry.depth >= depth)) { if(ttEntry.flag == TT_FLAG_EXACT) return ttEntry.value; else if(ttEntry.flag == TT_FLAG_LOWERBOUND) alpha = std::max(alpha, ttEntry.value); else// if(ttEntry.flag == TT_FLAG_UPPERBOUND) beta = std::min(beta, ttEntry.value); if(beta <= alpha) return ttEntry.value; } #endif if(state.IsGameOver() || (depth == 0)) { #ifdef _DEBUG _searcherCounter++; #endif return EvaluateNegaMax(state, max_player_id); } state.SwitchPlayer(); int score = -INFINITY; int player_id = state.GetCurrentPlayer(); std::vector<MOVES_LIST> moves; int mc = state.FindMoves(player_id, moves); SortMoves(moves); for(int i = 0; i < mc; i++) { state.DoPutChess(moves[i].cell, player_id); int value = -NegaMax(state, depth - 1, -beta, -alpha, max_player_id); state.UndoPutChess(moves[i].cell); score = std::max(score, value); alpha = std::max(alpha, value); if(beta <= alpha) break; } state.SwitchPlayer(); #if 0 //写入置换表 ttEntry.value = score; if(score <= alphaOrig) ttEntry.flag = TT_FLAG_UPPERBOUND; else if(score >= beta) ttEntry.flag = TT_FLAG_LOWERBOUND; else ttEntry.flag = TT_FLAG_EXACT; ttEntry.depth = depth; StoreTranspositionTable(state_hash, ttEntry); #endif return score; }
//Get Best Move - //recursive min/max algorithm explores game states and //returns a move and its score, for a specific player MoveScore GetBestMove( const GameState& theGame, const int player_index, int abPrune, const int recursive=1) { const Change& player_change = theGame.GetPlayerChange( theGame.GetCurrentPlayer() ); const Change& pool = theGame.GetGameChange(); int bestScore = (player_index == theGame.GetCurrentPlayer())? std::numeric_limits<int>::min() : std::numeric_limits<int>::max(); Coin bestCoin; Change bestChange; Change::List* someChange; if (recycledChangeLists.empty()){ someChange = new Change::List; } else{ someChange = recycledChangeLists.back(); recycledChangeLists.pop_back(); } //for each coin that can be played for (const Coin *ci=&COINLIST[0];ci!=&COINLIST[COIN_COUNT];++ci){ if (player_change.GetCount(*ci) > 0){ //get all possible sets of change into array GetAllPossibleChange( pool, *ci, *someChange); //for each possible set of change //(reversed iteration improves AB prune by as much as 70%) const Change::List::reverse_iterator end = someChange->rend(); for(Change::List::reverse_iterator i = someChange->rbegin();i!=end;++i){ //create "move" from coin to give, and change to take const Move myMove(*ci,*i); //create gamestate once the move has been played GameState newGame = theGame.PlayMove( myMove ); //get "score" for this move int score; if (recursive>0 && newGame.GetActivePlayers()>1){ //recursivly examine move, get its score score = GetBestMove( newGame, player_index, bestScore, recursive-1).score; } else{ //leaf node - just get heuristic value score = GameHeuristic( newGame, player_index, myHeuristic ); } //Min/Max search if ((player_index == theGame.GetCurrentPlayer() && score > bestScore) || (player_index != theGame.GetCurrentPlayer() && score < bestScore)) { bestScore = score; bestCoin = *ci; bestChange = *i; //Alpha/Beta pruning if ((player_index == theGame.GetCurrentPlayer() && score > abPrune) || (player_index != theGame.GetCurrentPlayer() && score < abPrune)){ goto exit; } } } } } exit: recycledChangeLists.push_back( someChange ); return MoveScore( Move( bestCoin, bestChange ), bestScore ); }