double Player::alphabeta(Board *b, int depth, double alpha, double beta, bool max_player) { if(depth == 0 || b->isDone()) return b->score(side); double v; if(max_player) { v = -INF; vector<Move *> moves = b->possibleMoves(side); for(auto &m : moves) { Board *child = b->copy(); child->doMove(m, side); v = max(v, alphabeta(child, depth - 1, alpha, beta, false)); alpha = max(alpha, v); if(beta <= alpha) break; } return v; } else { v = INF; vector<Move *> moves = b->possibleMoves(opp_side); for(auto &m : moves) { Board *child = b->copy(); child->doMove(m, opp_side); v = min(v, alphabeta(child, depth - 1, alpha, beta, true)); beta = min(beta, v); if(beta <= alpha) break; } return v; } }
/** *\brief Fonction where_play, utilisée pour savoir où l'IA doit jouer. *\details Cette fonction permet à l'IA (modulo sa profondeur) de savoir dans quelle colonne jouer, en se basant sur les fonctions alphabeta et pond. *\return a de type \a int, c'est le numéro de colonne (entre 1 et 7 donc) dans lequel l'IA doit jouer. */ int where_play(grid g,int depth,token t) { int i; int BestMove=1000 ; int tmp=1000 ; /* On initialise buf, mais Valgrind râle quand même, cf ligne 105 */ char buf[7] = { 0 } ; int j=0 ; int z ; for(i=1;i<8;i++) { if(g->heights[i-1] != LINE_NB) { /* On pose un jeton */ put_token(g,i,t) ; /* Si on est RED ... */ if(t==RED) { /* ... On lance alphabeta en indiquant que le prochain joueur à joueur joue les YELLOW */ tmp=alphabeta(g,-1000,1000,depth,YELLOW) ; } /* Si on est YELLOW ... */ if(t==YELLOW) { /* ... On lance alphabeta en indiquant que le prochain joueur à joueur joue les RED */ tmp=alphabeta(g,-1000,1000,depth,RED) ; } /* En cas d'égalité, on stocke à la suite dans un tableau, en vue de décider du choix aléatoirement : permet de gérer le cas où quel que soit la colonne dans laquelle on joue " le cas est le même " */ if(tmp==BestMove) { buf[j]=i ; j = j+1 ; } /* Si une valeur renvoyée est < BestMove, alors on remet les indices à 0, et on écrase le tableau, pour favoriser les pondérations " faibles " */ if(tmp<BestMove) { j=0 ; for(z=0;z<7;z++) { buf[z] = 0 ; } BestMove = tmp ; buf[j]=i ; j = j + 1 ; } /* On enlève le jeton : parcours destructif de l'arbre */ erase_token(g,i) ; } } int k=0 ; int l=0 ; /* On compte le nombre de valeurs non nulles dans le tableau. */ while(k<7&&buf[k]!=0) { l = l + 1 ; k = k + 1 ; } /* On balance une fonction aléatoire, avec une seed horaire */ srand (time (NULL)); /* On module modulo le nombre de valeurs non nulle qu'il faut considérer dans buf[] -> permet de choisir aléatoirement en cas d'égalité !*/ int a = rand()%l; return(buf[a]) ; }
void makeComputerMove(struct game *state) { printf("makeComputerMove: Making computer move on board '%s' with player %c\n",state->board,state->player); /* Get the next possible states and choose the one with the least minimax value */ struct gamelist *nextstates = gennextstates(state); /* Set the minstatevalue to 2, larger than the minimax function will return, so that the nextgame variable is definitely set */ int minstatevalue = 2; int minimaxvalue; struct game *nextgame; puts("looping through next states checking minimax"); while (nextstates != NULL) { minimaxvalue = alphabeta(nextstates->state,-2,2); if (minimaxvalue < minstatevalue) { minstatevalue = minimaxvalue; nextgame = nextstates->state; } printf("Minimax value of board '%s' = %i\n",nextstates->state->board,minimaxvalue); nextstates = nextstates->next; } strcpy(state->board,nextgame->board); state->player = nextgame->player; }
void BOARD::iterativeDeepening() { int bestscore=-INFINITE; int bestmove =NOMOVE; int numPv=0; clearForSearch(); for(int i=1;i<=info.depth;i++) { bestscore=alphabeta(i,-INFINITE,INFINITE,true); if(info.stopped) break; numPv=getPvLine(i); bestmove=PVARRAY[0]; printf("info score cp %d depth %d nodes %lld time %d ",bestscore,i,info.nodes,getTime()-info.starttime); printf("pv"); for(int j=0;j<numPv;j++) { printf(" "); printMove(PVARRAY[j]); } printf("\n"); //printf("Ordering : %0.2f\n",info.fhf/info.fh); } printf("bestmove ");printMove(bestmove); printf("\n"); }
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; }
/* * Compute the next move given the opponent's last move. Your AI is * expected to keep track of the board on its own. If this is the first move, * or if the opponent passed on the last move, then opponentsMove will be NULL. * * msLeft represents the time your AI has left for the total game, in * milliseconds. doMove() must take no longer than msLeft, or your AI will * be disqualified! An msLeft value of -1 indicates no time limit. * * The move returned must be legal; if there are no valid moves for your side, * return NULL. */ Move *Player::doMove(Move *opponentsMove, int msLeft) { /* * TODO: Implement how moves your AI should play here. You should first * process the opponent's opponents move before calculating your own move */ board->doMove(opponentsMove, opp_side); vector<Move *> moves = board->possibleMoves(side); if(moves.empty()) return NULL; double max_score, best_move = 0; for(int i = 0; i < moves.size(); i++) { Board *next = board->copy(); next->doMove(moves[i], side); int s = alphabeta(next, 6, -INF, INF, false); if(i == 0) max_score = s; else if(s > max_score) { max_score = s; best_move = i; } } board->doMove(moves[best_move], side); return moves[best_move]; }
float alphabeta(BNTBoard board, int depth, float alpha, float beta, BNTPlayer player) { if (depth == 0 || IsGameEnd(board)) return EvaluateBoard(board, player) * (player == BNTPlayerX ? 1 : -1); if (player == BNTPlayerX) { for (int x = 0; x < BOARD_SIZE; ++x) { for (int y = 0; y < BOARD_SIZE; ++y) { if (board[x][y] == BNTPlayerNone) { board[x][y] = player; alpha = DEPTH_PENALTY * MAX(alpha, alphabeta(board, depth-1, alpha, beta, OppositePlayer(player))); board[x][y] = BNTPlayerNone; if (beta <= alpha) { break;/* Beta cut-off */ } } } } return alpha; } else { for (int x = 0; x < BOARD_SIZE; ++x) { for (int y = 0; y < BOARD_SIZE; ++y) { if (board[x][y] == BNTPlayerNone) { board[x][y] = player; beta = DEPTH_PENALTY * MIN(beta, alphabeta(board, depth-1, alpha, beta, OppositePlayer(player))); board[x][y] = BNTPlayerNone; if (beta <= alpha) { break;/* Alpha cut-off */ } } } } return beta; } }
void CAlphaBetaEngine::SearchAGoodMove(BYTE position[10][9]) { memcpy(CurPosition, position, 90); m_nMaxDepth = m_nSearchDepth; alphabeta(m_nMaxDepth, -20000, 20000); MakeMove(&m_cmBestMove); memcpy(position, CurPosition, 90); }
bool Board_AB::alphabeta_helper(num i, bool maximize, num depth, char& score, num& loc, char& alpha, char& beta, bool& firstChild ){ if (i >= n || grid[i] != '.') { return false; } if (depth > 0 && closestDist(i) > bound) { return false; } grid[i] = (maximize?'R':'B'); Bitstring z = (maximize?assignmentZ[i].first:assignmentZ[i].second); zobrist ^= z; Bitstring g = (maximize?assignmentG[i].first:assignmentG[i].second); gamestate |= g; char curScore = 0; //PVS if (firstChild) { curScore = -alphabeta(!maximize, -beta, -alpha, depth+1, i).first; firstChild = false; } else { curScore = -alphabeta(!maximize, -alpha - 1, -alpha, depth+1, i).first; if (alpha < curScore && curScore < beta) { curScore = -alphabeta(!maximize, -beta, -curScore, depth+1, i).first; } } if (curScore > score) { loc = i; score = curScore; alpha = (alpha<score?score:alpha); } grid[i] = '.'; zobrist ^= z; gamestate ^= g; return alpha >= beta; }
std::pair<int, const typename boost::graph_traits<Graph>::vertex_descriptor> alphabeta(const Graph &g, const typename boost::graph_traits<Graph>::vertex_descriptor vd, int depth, int alpha, int beta, bool player1, Heuristic heuristic) { typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex; typename boost::graph_traits<Graph>::out_edge_iterator out_i, out_end; dout<<vd; if(depth == 0 || out_degree(vd, g) == 0) { dout<<std::endl; return std::make_pair(heuristic(vd), vd); } Vertex vert; if(player1) { int v = INT_MIN; for(boost::tie(out_i, out_end) = boost::out_edges(vd, g); out_i != out_end; ++out_i) { dout<<" children "<<out_end - out_i; dout<<" true"; dout<<" calling "<<target(*out_i, g); dout<<std::endl; vert = target(*out_i, g); v = std::max(v, alphabeta(g, vert, depth-1, alpha, beta, !player1, heuristic).first); alpha = std::max(alpha, v); if(beta <= alpha) break;//beta cut-off } return std::make_pair(v, vert); } else { int v = INT_MAX; for(boost::tie(out_i, out_end) = boost::out_edges(vd, g); out_i != out_end; ++out_i) { dout<<" children "<<out_end - out_i; dout<<" false"; dout<<" calling "<<target(*out_i, g); dout<<std::endl; vert = target(*out_i, g); v = std::min(v, alphabeta(g, vert, depth-1, alpha, beta, !player1, heuristic).first); beta = std::min(beta, v); if(beta<=alpha) break;//alpha cut-off } return std::make_pair(v, vert); } }
int printMoveValue(char* command){ int exitcode; int depth; int bestOffset = 0; if (command[10] == 'b'){ depth = computeBestDepth(); bestOffset = 3; } else{ if (sscanf(command, "get_score %d", &depth) < 0){ return -1; } } if (!strstr(command, "move") && !strstr(command, "castle")){ return -1; } if (strstr(command, "move")){ PossibleMove* move = readMove(command + 12 + bestOffset, &exitcode); if (exitcode != 0){ // illegal input or illegal move return exitcode; } else{ int score = alphabeta(move, depth, !turn, INT_MIN, INT_MAX); printf("%d\n", score); PossibleMove_free(move); } } // castling move else{ int rookX, rookY; exitcode = readTile(command + 19, &rookX, &rookY); if (exitcode == 0){ PossibleMove* castlingMove = PossibleMove_new(rookX, rookY, 0, 0, 0, &board); int score = alphabeta(castlingMove, depth, !turn, INT_MIN, INT_MAX); printf("%d\n", score); PossibleMove_free(castlingMove); } } return exitcode; }
int main2() { printf("\n\nSize of node is %zd bytes\n\n", sizeof(Node)); int randSeed = time(NULL); printf("Random Seed: %d\n", randSeed); srand(randSeed); // 3, and 4 are good. 6,7,8 are very good // 10 is excellent srand(10); // generate a random tree printf ("generating random tree of depth %d\n", g_depth); Node root = {0}; START_TIMER genTree(&root, g_depth); STOP_TIMER printf("random tree generated, total nodes: %d, leaf nodes: %d, time: %g ms\n", gTotalNodes, gLeafNodes, gTime); float bestVal = 0; // search the best move using min-max search printf("searching the tree using min-max\n"); START_TIMER bestVal = negaMax(&root, g_depth, g_depth); STOP_TIMER printf ("best move %d, score: %f, time: %g\n", root.bestChild, root.nodeVal, gTime); // search the best move using alpha-beta search printf("searching the tree using alpha-beta\n"); START_TIMER bestVal = alphabeta(&root, g_depth, g_depth, -INF, INF); STOP_TIMER printf ("best move %d, score: %f\nnodes visited (leaves/interior/total): %d/%d/%d\n", root.bestChild, root.nodeVal, gLeafNodesVisited, gInteriorNodesVisited, gLeafNodesVisited + gInteriorNodesVisited); printf("time taken: %g\n", gTime); START_TIMER exploreTree(&root, g_depth); STOP_TIMER printf("time taken: %g\n", gTime); float val; START_TIMER val = SSS_star(&root, g_depth); STOP_TIMER printf("SSS* best node: %d, score: %f, nodes explored: %d, time taken: %g\n", root.bestChild, val, g_sssNodes, gTime); freeTree(&root); getchar(); return 0; }
int depthSearch(SearchThread* self, ChessHNode* start, int depth, move_t* line, int* lineLength){ int real_depth = DEPTH_ORDERS[depth]; int quiecense = QUIECENSE_ORDERS[depth]; int deep_quiecense = DEEP_QUIECENSE_ORDERS[depth]; self->table->minHalfMoveNumber = start->halfMoveNumber; int eval = alphabeta( self, start, real_depth, quiecense, deep_quiecense, INT_MIN, INT_MAX, line, lineLength ); return eval; }
/** * Builds a game tree and searches for a good movme in it using * the minmax algorithm combined with alpha-beta pruning. * * @param board current state of the game * @param treeDepth depth of the game tree we build * @param score_out the score of the best move is returned here * * @return cmove move chosen by the AI */ int findMove(chessBoard &board, int treeDepth, cmove &aimove) { moveTree mt; clock_t start = clock(); mt.buildTree(board, mt.gameTree, treeDepth); clock_t end = clock(); printf("Built move tree with depth %d in %f seconds.\n", treeDepth, ((float)end - (float)start) / CLOCKS_PER_SEC); // printf("current player is %d\n", board.currentPlayer); linkedList< tree<moveNode*>* > *ch = mt.gameTree->children; linkedList< cmove > *moves = mt.moveList; if (moves->size != ch->size ) { cout << "ERROR: move numbers do not match" << endl; } // printf("moves depth 1: %d\n", sumMoves(mt.gameTree, 0)); // printf("moves depth 2: %d\n", sumMoves(mt.gameTree, 1)); // printf("moves depth 3: %d\n", sumMoves(mt.gameTree, 2)); int bestValue = INT_MIN; start = clock(); for (int i = 0; i < ch->size; i++) { tree<moveNode*> *n = (*ch)[i]; // start with min value (bmax = false) because in the situation we // are evaluating it is the opponent's turn! int value = alphabeta(n, treeDepth-1, INT_MIN, INT_MAX, false); // int value = minimax(n, treeDepth-1, false); #if 0 cmove move = (*ch)[i]->item->prev_move; printf("Move %c%d %c%d, score %d\n", move.from.x + 'a', move.from.y + 1, move.to.x + 'a', move.to.y + 1, value); #endif if (value > bestValue) { // aimove = (*ch)[i]->item->prev_move; aimove = (*moves)[i]; bestValue = value; } } end = clock(); printf("Minimax search with alpha-beta took %f seconds.\n", ((float)end - (float)start) / CLOCKS_PER_SEC); return bestValue; }
int main() { for (int i = 0; i < 1000; i++) { int randSeed = i + time(NULL); gTotalNodes = 0; gLeafNodes = 0; gLeafNodesVisited = 0; gInteriorNodesVisited = 0; printf("Random Seed: %d\n", randSeed); srand(randSeed); printf("generating random tree of depth %d\n", g_depth); Node root = { 0 }; START_TIMER genTree(&root, g_depth); STOP_TIMER printf("random tree generated, total nodes: %d, leaf nodes: %d, time: %g ms\n", gTotalNodes, gLeafNodes, gTime); float bestValAB = 0; float bestValET = 0; // search the best move using alpha-beta search printf("searching the tree using alpha-beta\n"); START_TIMER bestValAB = alphabeta(&root, g_depth, g_depth, -INF, INF); STOP_TIMER printf("best move %d, score: %f\nnodes visited (leaves/interior/total): %d/%d/%d\n", root.bestChild, root.nodeVal, gLeafNodesVisited, gInteriorNodesVisited, gLeafNodesVisited + gInteriorNodesVisited); printf("time taken: %g\n", gTime); START_TIMER bestValET = exploreTree(&root, g_depth); STOP_TIMER printf("time taken: %g\n\n\n\n", gTime); if (bestValET != bestValAB) { printf("\n*Mismatch found!*\n"); getchar(); } freeTree(&root); //getchar(); } getchar(); return 0; }
int printBestMoves(char* command){ int depth; if (command[15] == 'b'){ depth = computeBestDepth(); } else{ if (sscanf(command, "get_best_moves %d", &depth) != 1){ return -1; } } LinkedList* allPossibleMoves = Board_getPossibleMoves(&board, turn); if (!allPossibleMoves){ return 1; } int bestScore = INT_MIN; Iterator iterator; Iterator_init(&iterator, allPossibleMoves); LinkedList* bestMoves = PossibleMoveList_new(); if (!bestMoves){ PossibleMoveList_free(allPossibleMoves); return 1; } while(Iterator_hasNext(&iterator)){ PossibleMove* currentMove = (PossibleMove*)Iterator_next(&iterator); int score = alphabeta(currentMove, depth, !turn, INT_MIN, INT_MAX); if (score > bestScore) { LinkedList_removeAll(bestMoves); if(LinkedList_add(bestMoves, currentMove)){ LinkedList_removeAll(bestMoves); LinkedList_free(bestMoves); PossibleMoveList_free(allPossibleMoves); return 1; } bestScore = score; } else if (score == bestScore){ if(LinkedList_add(bestMoves, currentMove)){ LinkedList_removeAll(bestMoves); LinkedList_free(bestMoves); PossibleMoveList_free(allPossibleMoves); return 1; } } } PossibleMoveList_print(bestMoves); LinkedList_removeAll(bestMoves); //removes the nodes LinkedList_free(bestMoves); //frees the struct LinkedList_free(allPossibleMoves); //frees the moves return 0; }
/** *\brief Fonction alphabeta, qui est l'implémentation de l'algorithme alpha/beta. *\details Cette fonction permet de générer l'arbre (détruit au fur et à mesure) de l'ensemble des coups et des grilles possibles. Note pour l'utilisation pratique : Il faut alpha < beta, typiquement alpha = -INFINI et beta = INFINI. *\return best de type \a int : c'est la valeur de la pondération affectée à une grille. */ int alphabeta(grid g, int alpha, int beta, int depth, token t) { if(winner(g)==1||depth==1) { return(pond(g)) ; } else { int best ; int column ; best = -INFINI ; for(column=1; column<=COLUMN_NB; column++) { if(g->heights[column-1]!=LINE_NB) { int val ; /* On joue le coup */ put_token(g, column, t) ; if(t==RED) { /* On alterne les coups */ val = - alphabeta(g, -beta, -alpha, depth-1, YELLOW) ; } if(t==YELLOW) { /* On alterne les coups */ val = - alphabeta(g, -beta, -alpha, depth-1, RED) ; } /* On annule le coup -> Parcours destructif de l'arbre */ erase_token(g, column) ; /* Coupures Alpha et Beta */ if(val > best) { best = val ; if(best > alpha) { alpha = best ; if(alpha >= beta) { return(best) ; } } } } } return(best) ; } }
double OmokAI::alphabeta(std::shared_ptr<OmokNode> node, double alpha, double beta, size_t depth) { size_t size = _game->getBoardSize(); if (depth == _depth || node->state->isWin()) { return node->state->getScore(); /*if (depth % 2 == 1) { return node->state->getBlackScore(); } else { return -node->state->getWhiteScore(); }*/ } else { if (node->children.size() == 0) { buildChildren(node, depth); } if (depth % 2 == 0) { //maximizing for (auto &node : node->children) { double a = alphabeta(node.second, alpha, beta, depth + 1); alpha = a > alpha? a : alpha; if (beta <= alpha) { // beta cut-off break; } } return alpha; } else { // minimizing for (auto &node : node->children) { double b = alphabeta(node.second, alpha, beta, depth + 1); beta = b < beta ? b : beta; if (beta <= alpha) { // beta cut-off break; } } return beta; } } }
int Board::alphabeta(int ply, int depth, int alpha, int beta) { // Negascout int i, j, val; triangularLength[ply] = ply; if (depth == 0) return board.eval(); moveBufLen[ply+1] = movegen(moveBufLen[ply]); for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++) { makeMove(moveBuffer[i]); { if (!isOtherKingAttacked()) { inodes++; if (!ply) displaySearchStats(3, ply, i); val = -alphabeta(ply+1, depth-1, -beta, -alpha); unmakeMove(moveBuffer[i]); if (val >= beta) { return beta; } if (val > alpha) { alpha = val; // both sides want to maximize from *their* perspective triangularArray[ply][ply] = moveBuffer[i]; // save this move for (j = ply + 1; j < triangularLength[ply + 1]; j++) { triangularArray[ply][j] = triangularArray[ply+1][j]; // and append the latest best PV from deeper plies } triangularLength[ply] = triangularLength[ply + 1]; if (!ply) { msStop = timer.getms(); displaySearchStats(2, depth, val); } } } else unmakeMove(moveBuffer[i]); } } return alpha; }
int minvalue(struct gamelist *nextstates, int alpha, int beta) { int minstatevalue = 2; int minimaxvalue; while (nextstates != NULL) { minimaxvalue = alphabeta(nextstates->state,alpha,beta); if (minimaxvalue < minstatevalue) minstatevalue = minimaxvalue; if (minstatevalue <= alpha) return minstatevalue; if (beta > minstatevalue) beta = minstatevalue; nextstates = nextstates->next; } return minstatevalue; }
int maxvalue(struct gamelist *nextstates,int alpha, int beta) { int maxstatevalue = -2; int minimaxvalue; while (nextstates != NULL) { minimaxvalue = alphabeta(nextstates->state,alpha,beta); if (minimaxvalue > maxstatevalue) maxstatevalue = minimaxvalue; if (maxstatevalue >= beta) return maxstatevalue; if (alpha < maxstatevalue) alpha = maxstatevalue; nextstates = nextstates->next; } return maxstatevalue; }
/*A problem found in alphabeta pruning is that once I get a 0 value for a certain MAX node (guaranteed draw) if any of the next nodes has a value of -1 alphabeta allocates it as a 0 value node. Although these children don't really affect the true value of father's alphabeta (as max node if there's a 0, it can't get less) it can happen that I have to count the number of winnning leaves and naturally I can't choose a path that seems 0 (draw) while it is indeed -1 (lose) so that I developed this function that returns the TRUE value of a certain node that normally is allocated as 0 but in reality can also be -1*/ static int TrueValue(NODE *T) { NODE *tmpSubTree; //temporarily to this function int valore; if ((tmpSubTree = (NODE*)malloc(sizeof(NODE))) == NULL) exit(0); tmpSubTree->depth = T->depth; tmpSubTree->i = T->i; tmpSubTree->j = T->j; tmpSubTree->leftchild = NULL; tmpSubTree->rightbrothers = NULL; tmpSubTree->player = T->player; tmpSubTree->value == (T->player == MAX) ? (-2) : (2); tmpSubTree->transboard = copyv(T->transboard); valore = alphabeta(&tmpSubTree, tmpSubTree->depth, -1, 1, tmpSubTree->player); //simply I recreate a sub-tree with the technique used to create the whole tree so that I get the TRUE value and I store it in the real tree DeleteTree(&tmpSubTree); //eventually I free tmpSubTree return (valore); //returns the TRUE value of that child }
PossibleMove* getBestMove(){ LinkedList* allPossibleMoves = Board_getPossibleMoves(&board, turn); if (!allPossibleMoves){ return NULL; } int depth = getDepth(); int bestScore = INT_MIN; PossibleMove* bestMove; Iterator iterator; Iterator_init(&iterator, allPossibleMoves); while(Iterator_hasNext(&iterator)){ PossibleMove* currentMove = (PossibleMove*)Iterator_next(&iterator); int score = alphabeta(currentMove, depth, !turn, INT_MIN, INT_MAX); if (score > bestScore || (score == bestScore && rand()%2)) { bestScore = score; bestMove = currentMove; } } LinkedList_freeAllButOne(allPossibleMoves, bestMove); return bestMove; }
void OmokAI::normalSearch() { Position w = _game->getPreviousWhite(); auto iter = _root->children.find(w); if (iter == _root->children.cend()) { _root->state->set(Piece::createWhite(w)); _root->children.clear(); } else { _root = iter->second; } double alpha = -std::numeric_limits<double>::infinity(); double beta = std::numeric_limits<double>::infinity(); std::shared_ptr<OmokNode> maxOmokNode; Position maxMove(-1, -1); if (!_root->state->isWin()) { if (_root->children.size() == 0) { buildChildren(_root, 0); } for (auto &node : _root->children) { double a = alphabeta(node.second, alpha, beta, 1); if (a > alpha) { alpha = a; maxOmokNode = node.second; maxMove = node.first; } if (beta <= alpha) { // beta cut-off break; } } assert(_game->isBlackTurn()); _game->put(maxMove); assert(maxOmokNode); _root = maxOmokNode; } }
float alphabeta(Node *node, int depth, int origDepth, float alpha, float beta) { if (depth == 0) { gLeafNodesVisited++; // eval for even depths, -eval for odd depths if (origDepth % 2 == 0) return node->nodeVal; else return -node->nodeVal; } gInteriorNodesVisited++; // choose the best child int bestChild = 0; for (int i = 0; i < node->nChildren; i++) { float curScore = -alphabeta(&node->children[i], depth - 1, origDepth, -beta, -alpha); if (curScore >= beta) { return beta; } if (curScore > alpha) { alpha = curScore; bestChild = i; } } node->nodeVal = alpha; node->bestChild = bestChild; return alpha; }
BNTField NextBestMove(BNTBoard board, BNTPlayer player, int lookahead) { float score = player == BNTPlayerX ? -INFINITY : +INFINITY; float bestX = 0, bestY = 0; int movesCount = 0; BNTField moves[BOARD_SIZE * BOARD_SIZE]; for (int x = 0; x < BOARD_SIZE; ++x) { for (int y = 0; y < BOARD_SIZE; ++y) { if (board[x][y] == BNTPlayerNone) { moves[movesCount] = BNTFieldMake(x, y); ++movesCount; } } } //shuffle legal moves shuffle(moves, movesCount); for (int i = 0; i < movesCount; ++i) { BNTField f = moves[i]; board[f.x][f.y] = player; float v = DEPTH_PENALTY * alphabeta(board, lookahead, -INFINITY, +INFINITY, OppositePlayer(player)); board[f.x][f.y] = BNTPlayerNone; if ((player == BNTPlayerX ? v > score : v < score)) { score = v, bestX = f.x, bestY = f.y; } } return BNTFieldMake(bestX, bestY); }
/** * Entry point for search * * Does iterative deepening and alpha/beta width handling, and * calls alpha/beta search */ void ABIDStrategy::searchBestMove() { int alpha = -15000, beta = 15000; int nalpha, nbeta, currentValue = 0; _pv.clear(_maxDepth); _currentBestMove.type = Move::none; _currentMaxDepth=1; /* iterative deepening loop */ do { /* searches on same level with different alpha/beta windows */ while(1) { nalpha = alpha, nbeta = beta; _inPV = (_pv[0].type != Move::none); if (_sc && _sc->verbose()) { char tmp[100]; sprintf(tmp, "Alpha/Beta [%d;%d] with max depth %d", alpha, beta, _currentMaxDepth); _sc->substart(tmp); } currentValue = alphabeta(0, alpha, beta); /* stop searching if a win position is found */ if (currentValue > 14900 || currentValue < -14900) _stopSearch = true; /* Don't break out if we haven't found a move */ if (_currentBestMove.type == Move::none) _stopSearch = false; if (_stopSearch) break; /* if result is outside of current alpha/beta window, * the search has to be rerun with widened alpha/beta */ if (currentValue <= nalpha) { alpha = -15000; if (beta<15000) beta = currentValue+1; continue; } if (currentValue >= nbeta) { if (alpha > -15000) alpha = currentValue-1; beta=15000; continue; } break; } /* Window in both directions cause of deepening */ alpha = currentValue - 200, beta = currentValue + 200; if (_stopSearch) break; _currentMaxDepth++; } while(_currentMaxDepth <= _maxDepth); _bestMove = _currentBestMove; }
/* * Alpha/Beta search * * - first, start with principal variation * - depending on depth, we only do depth search for some move types */ int ABIDStrategy::alphabeta(int depth, int alpha, int beta) { int currentValue = -14999+depth, value; Move m; Move bestMove; MoveList list; bool depthPhase, doDepthSearch; int i=0; int movecounter =-1; int flag=0; /* We make a depth search for the following move types... */ int maxType = (depth < _currentMaxDepth-1) ? Move::maxMoveType : (depth < _currentMaxDepth) ? Move::maxPushType : Move::maxOutType; _board->generateMoves(list); if (_sc && _sc->verbose()) { char tmp[100]; sprintf(tmp, "Alpha/Beta [%d;%d], %d moves (%d depth)", alpha, beta, list.count(Move::none), list.count(maxType)); _sc->startedNode(depth, tmp); } /* check for an old best move in principal variation */ if (_inPV) { m = _pv[depth]; if ((m.type != Move::none) && (!list.isElement(m, 0, true))) m.type = Move::none; if (m.type == Move::none) _inPV = false; } // first, play all moves with depth search depthPhase = true; while (1) { pretty_print("movecounter", movecounter); movecounter++; // get next move if (m.type == Move::none) { if (depthPhase) depthPhase = list.getNext(m, maxType); if (!depthPhase) if (!list.getNext(m, Move::none)) break; } if((thread_rank == movecounter% num_threads) || (depth > 0))// we could start with a non-depth move from principal variation { doDepthSearch = depthPhase && (m.type <= maxType); _board->playMove(m); /* check for a win position first */ if (!_board->isValid()) { /* Shorter path to win position is better */ value = 14999-depth; } else { if (doDepthSearch) { /* opponent searches for its maximum; but we want the * minimum: so change sign (for alpha/beta window too!) */ value = -alphabeta(depth+1, -beta, -alpha); } else { value = evaluate(); } } _board->takeBack(); /* best move so far? */ if (value > currentValue) { currentValue = value; _pv.update(depth, m); if (_sc) _sc->foundBestMove(depth, m, currentValue); if (depth == 0) _currentBestMove = m; /* alpha/beta cut off or win position ... */ if (currentValue>14900 || currentValue >= beta) { if (_sc) _sc->finishedNode(depth, _pv.chain(depth)); flag = 1; break; //return currentValue; } /* maximize alpha */ if (currentValue > alpha) alpha = currentValue; } } if (_stopSearch) break; // depthPhase=false; m.type = Move::none; } if(depth == 0){ //all threads send value to thread 0 if(thread_rank == 0) { Move *moves=NULL; Variation *PVs=NULL; int *eval_results; PVs=(Variation*)malloc((num_threads -1)*sizeof(Variation)); moves=(Move*)malloc((num_threads -1)*sizeof(Move)); eval_results=(int*)malloc((num_threads - 1)*sizeof(int)); for(i=0;i<num_threads-1;i++) { MPI_Status status; MPI_Recv (&moves[i], sizeof(Move), MPI_BYTE, i+1 ,10, MPI_COMM_WORLD, &status); MPI_Recv (&PVs[i], sizeof(Variation), MPI_BYTE, i+1 ,10, MPI_COMM_WORLD, &status); MPI_Recv (&eval_results[i], 1, MPI_INT, i+1 ,10, MPI_COMM_WORLD, &status); } for(i=0;i<num_threads-1;i++) { if(eval_results[i] > currentValue) { currentValue = eval_results[i]; _pv=PVs[i]; _currentBestMove = moves [i]; } } for(i=0;i<num_threads-1;i++) { MPI_Send(&_currentBestMove, sizeof(Move), MPI_BYTE, i+1 ,10, MPI_COMM_WORLD); MPI_Send(&_pv, sizeof(Variation), MPI_BYTE, i+1 ,10, MPI_COMM_WORLD); MPI_Send(¤tValue, 1, MPI_INT, i+1 ,10, MPI_COMM_WORLD); } pretty_print("thread_rank", thread_rank); free(PVs); free(moves); free(eval_results); } else { MPI_Status status; MPI_Send(&_currentBestMove, sizeof(Move), MPI_BYTE, 0 ,10, MPI_COMM_WORLD); MPI_Send(&_pv, sizeof(Variation), MPI_BYTE, 0 ,10, MPI_COMM_WORLD); MPI_Send(¤tValue, 1, MPI_INT, 0 ,10, MPI_COMM_WORLD); MPI_Recv(&_currentBestMove, sizeof(Move), MPI_BYTE, 0 ,10, MPI_COMM_WORLD, &status); MPI_Recv(&_pv, sizeof(Variation), MPI_BYTE, 0 ,10, MPI_COMM_WORLD, &status); MPI_Recv(¤tValue, 1, MPI_INT, 0 ,10, MPI_COMM_WORLD, &status); } } if(!flag) if (_sc) _sc->finishedNode(depth, _pv.chain(depth)); return currentValue; }
void test_game2() { int nlegmov; char bestply2[10]; float tmpres; MOVE_LIST movelist; string_execute_move("a2a4", w, b); string_execute_move("b8c6", w, b); string_execute_move("e2e3", w, b); string_execute_move("a8b8", w, b); string_execute_move("g1h3", w, b); string_execute_move("g8h6", w, b); string_execute_move("h3g5", w, b); string_execute_move("g7g6", w, b); string_execute_move("b2b3", w, b); string_execute_move("a7a6", w, b); string_execute_move("h2h3", w, b); string_execute_move("f7f6", w, b); string_execute_move("g5f3", w, b); string_execute_move("c6a7", w, b); string_execute_move("f3d4", w, b); string_execute_move("c7c5", w, b); string_execute_move("d4f3", w, b); string_execute_move("h8g8", w, b); string_execute_move("c2c4", w, b); string_execute_move("g6g5", w, b); string_execute_move("f3h2", w, b); string_execute_move("g5g4", w, b); string_execute_move("h2g4", w, b); string_execute_move("f6f5", w, b); string_execute_move("g4h6", w, b); string_execute_move("f8h6", w, b); string_execute_move("d1h5", w, b); string_execute_move("g8g6", w, b); string_execute_move("h5f5", w, b); string_execute_move("h6g7", w, b); string_execute_move("d2d4", w, b); string_execute_move("d7d6", w, b); string_execute_move("f5h5", w, b); string_execute_move("e8f8", w, b); string_execute_move("h5h7", w, b); string_execute_move("g6h6", w, b); string_execute_move("h7e4", w, b); string_execute_move("g7f6", w, b); string_execute_move("f1d3", w, b); string_execute_move("b8a8", w, b); string_execute_move("h1h2", w, b); string_execute_move("e7e6", w, b); string_execute_move("b1c3", w, b); string_execute_move("d8a5", w, b); string_execute_move("b3b4", w, b); string_execute_move("a5b4", w, b); string_execute_move("e1d2", w, b); string_execute_move("c5d4", w, b); white_leg_mov(w, b); crt_list(&movelist); nlegmov = push_leg_mov(w, &movelist); depthmax = STARTDPTH; tmpres = alphabeta(depthmax, -200, 200, -1, bestply2); printf("%s\n", bestply2); }
void test_game() { int nlegmov; char bestply2[10]; float tmpres; MOVE_LIST movelist; string_execute_move("d2d4", w, b); string_execute_move("e7e6", w, b); string_execute_move("c2c3", w, b); string_execute_move("d7d5", w, b); string_execute_move("g1h3", w, b); string_execute_move("d8f6", w, b); string_execute_move("c1g5", w, b); string_execute_move("f6f5", w, b); string_execute_move("d1d3", w, b); string_execute_move("f5d3", w, b); string_execute_move("e2d3", w, b); string_execute_move("f8d6", w, b); string_execute_move("b1d2", w, b); string_execute_move("f7f6", w, b); string_execute_move("g5e3", w, b); string_execute_move("c7c6", w, b); string_execute_move("h3g1", w, b); string_execute_move("b7b5", w, b); string_execute_move("b2b3", w, b); string_execute_move("c8d7", w, b); string_execute_move("h2h4", w, b); string_execute_move("g8e7", w, b); string_execute_move("f1e2", w, b); string_execute_move("e7f5", w, b); string_execute_move("e2g4", w, b); string_execute_move("f5e3", w, b); string_execute_move("f2e3", w, b); string_execute_move("h7h5", w, b); string_execute_move("g4h3", w, b); string_execute_move("d6g3", w, b); string_execute_move("e1e2", w, b); string_execute_move("g3h4", w, b); string_execute_move("h3e6", w, b); string_execute_move("d7e6", w, b); string_execute_move("h1h4", w, b); string_execute_move("g7g5", w, b); string_execute_move("h4h2", w, b); string_execute_move("h8h7", w, b); string_execute_move("a1f1", w, b); string_execute_move("h7f7", w, b); string_execute_move("h2h1", w, b); string_execute_move("e8f8", w, b); string_execute_move("b3b4", w, b); string_execute_move("a7a6", w, b); string_execute_move("e2f3", w, b); string_execute_move("b8d7", w, b); string_execute_move("h1h5", w, b); string_execute_move("a8a7", w, b); string_execute_move("a2a3", w, b); string_execute_move("e6f5", w, b); string_execute_move("f3g3", w, b); string_execute_move("f5d3", w, b); string_execute_move("f1f3", w, b); string_execute_move("g5g4", w, b); string_execute_move("g3g4", w, b); string_execute_move("f6f5", w, b); string_execute_move("g4h4", w, b); string_execute_move("d7f6", w, b); string_execute_move("h5h8", w, b); string_execute_move("f8g7", w, b); string_execute_move("h8d8", w, b); string_execute_move("f6e4", w, b); string_execute_move("d2e4", w, b); string_execute_move("f5e4", w, b); string_execute_move("f3f7", w, b); string_execute_move("a7f7", w, b); string_execute_move("d8e8", w, b); string_execute_move("g7f6", w, b); string_execute_move("e8h8", w, b); string_execute_move("f7g7", w, b); string_execute_move("h8c8", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); string_execute_move("g4h4", w, b); string_execute_move("g7h7", w, b); string_execute_move("h4g4", w, b); string_execute_move("h7g7", w, b); /*ok */ string_execute_move("g4f4", w, b); string_execute_move("g7f7", w, b); string_execute_move("c8c6", w, b); string_execute_move("f6g7", w, b); string_execute_move("f4g5", w, b); string_execute_move("a6a5", w, b); string_execute_move("c6g6", w, b); string_execute_move("g7h8", w, b); string_execute_move("b4a5", w, b); string_execute_move("h8h7", w, b); string_execute_move("a5a6", w, b); string_execute_move("h7h8", w, b); string_execute_move("g2g4", w, b); string_execute_move("h8h7", w, b); string_execute_move("g6h6", w, b); string_execute_move("h7g8", w, b); string_execute_move("h6g6", w, b); string_execute_move("g8h8", w, b); string_execute_move("g5h5", w, b); string_execute_move("d3c4", w, b); string_execute_move("g4g5", w, b); string_execute_move("c4d3", w, b); string_execute_move("g6h6", w, b); string_execute_move("h8g8", w, b); string_execute_move("h6g6", w, b); string_execute_move("g8h8", w, b); string_execute_move("h5h4", w, b); string_execute_move("d3c4", w, b); string_execute_move("g6h6", w, b); string_execute_move("f7h7", w, b); string_execute_move("a6a7", w, b); black_leg_mov(w, b); crt_list(&movelist); nlegmov = push_leg_mov(b, &movelist); depthmax = STARTDPTH; tmpres = alphabeta(depthmax, -200, 200, 1, bestply2); printf("%s\n", bestply2); }