//------------------------------------------------------------------------------ //-------------------------------MCTS Methods----------------------------------- //------------------------------------------------------------------------------ void playRandomGame(Player p, Board &b) { int movesPlayed = 1; int i = 0; Move last = MOVE_PASS; // Regenerate the movelist up to 4 times while (movesPlayed > 0 && i < 4) { movesPlayed = 0; i++; MoveList legalMoves = b.getLegalMoves(p); int koCount = 0; // While we still have legal moves remaining while (legalMoves.size() > 0) { // Check if the last move put its own chain into atari // Do not do this twice in a row to prevent infinite ko recapture if (koCount == 0) { Move cap = b.getPotentialCapture(last); if (cap != MOVE_PASS) { int ci = legalMoves.find(cap); if (ci != -1) legalMoves.removeFast(ci); b.doMove(p, cap); last = cap; p = otherPlayer(p); movesPlayed++; koCount++; continue; } } // Otherwise, pick a move at random std::uniform_int_distribution<int> distribution(0, legalMoves.size()-1); int index = distribution(rng); Move m = legalMoves.get(index); // Only play moves that are not into own eyes and not suicides if (!b.isEye(p, m) && b.isMoveValid(p, m)) { b.doMove(p, m); last = m; p = otherPlayer(p); movesPlayed++; koCount = 0; } legalMoves.removeFast(index); } } }
int UltimateTicTacToeMontecarloAI::expand(int leafIndex, Nodes& nodes, int const player) const { Node& node = nodes[leafIndex]; node.children.reserve(maxChildren); Moves options = movementOptions(node.board, node.previousMove); int turn = node.previousMove > 0 ? otherPlayer(node.board.grids.at(node.previousMove)) : player; int mostPromisingChildIndex = -1; int mostPromisingChildScore = 0; while(node.children.size() < maxChildren && !options.empty()) { Move move = options.takeAt(qrand() % options.size()); int childIndex = nodes.size(); node.children.append(childIndex); Board newBoard(node.board); nodes.append( Node {0, 1, playMove(newBoard, move, turn), move, leafIndex, Node::Children()}); int score = scoreBoard(nodes.last().board, player); if(score > mostPromisingChildScore || mostPromisingChildIndex < 0) { mostPromisingChildIndex = childIndex; mostPromisingChildScore = score; } } return mostPromisingChildIndex; }
void nextMove(char player, char board[3][3]){ int iboard[25]; int i,j,move; pair best; initializeBoard(iboard, board); US = player; THEM = otherPlayer(US); timesCalled=0; //printboard(iboard); best = chooseMoveAB(iboard,player,INT_MIN,INT_MAX); convert1dTo2d(best.move); printf("chooseMoveAB timesCalled: %d bestMove:%d\n", timesCalled,best.move); timesCalled=0; //printboard(iboard); best = chooseMove(iboard,player); convert1dTo2d(best.move); printf("chooseMove timesCalled: %d bestMove:%d\n", timesCalled,best.move); timesCalled=0; //printboard(iboard); best = chooseMoveEvaluate(iboard,player); convert1dTo2d(best.move); printf("chooseMoveEvaluate timesCalled: %d bestMove:%d\n", timesCalled,best.move); timesCalled=0; //printboard(iboard); best = chooseMoveABDepth(iboard,player,INT_MIN,INT_MAX); convert1dTo2d(best.move); printf("chooseMoveAB timesCalled: %d bestMove:%d\n", timesCalled,best.move); }
void PlayerJoinEvent::Execute(vector<PlayerThread*>& rvPlayers,PlayerPool* pPlayerPool) { if (rvPlayers.empty()) { return; } //Write join message to chat pPlayerPool->sendMessageToAll( _pSourcePlayer->getUsername() + " joined game" ); EntityPlayer NewPlayer(_pSourcePlayer); for (int x=0; x<=rvPlayers.size()-1; x++) { if (!(rvPlayers[x]->isAssigned() && rvPlayers[x]->isSpawned())) { continue; /* Not spawned */ } if (rvPlayers[x] == _pSourcePlayer) { continue; /* dont spawn yourself to yourself */ } rvPlayers[x]->PlayerInfoList(true,_pSourcePlayer->getUsername()); /* spawn name to playerlist */ if (MathHelper::distance2D(rvPlayers[x]->getCoordinates(),_pSourcePlayer->getCoordinates()) > 100.0) { continue; /* You're too distant */ } rvPlayers[x]->spawnEntity(&NewPlayer); /* Spawn entity */ /* Spawn other players to new player */ EntityPlayer otherPlayer(rvPlayers[x]); _pSourcePlayer->spawnEntity(&otherPlayer); } }
bool Board::isMoveValid(Player p, Move m) { if (m == MOVE_PASS) return true; int x = getX(m); int y = getY(m); assert(pieces[index(x, y)] == EMPTY); // First check if the move makes a capture, since if so then // it cannot possibly be a suicide Player victim = otherPlayer(p); int eastID = (pieces[index(x+1, y)] == victim) * chainID[index(x+1, y)]; int westID = (pieces[index(x-1, y)] == victim) * chainID[index(x-1, y)]; int northID = (pieces[index(x, y+1)] == victim) * chainID[index(x, y+1)]; int southID = (pieces[index(x, y-1)] == victim) * chainID[index(x, y-1)]; if (eastID) { Chain *node = nullptr; searchChainsByID(node, eastID); if (node->liberties == 1) return true; } if (westID) { Chain *node = nullptr; searchChainsByID(node, westID); if (node->liberties == 1) return true; } if (northID) { Chain *node = nullptr; searchChainsByID(node, northID); if (node->liberties == 1) return true; } if (southID) { Chain *node = nullptr; searchChainsByID(node, southID); if (node->liberties == 1) return true; } if (isEye(victim, m)) return false; pieces[index(x, y)] = p; // Suicides are illegal if (pieces[index(x+1, y)] && pieces[index(x-1, y)] && pieces[index(x, y+1)] && pieces[index(x, y-1)]) { if (doCaptures<false>(p, coordToMove(x, y))) { pieces[index(x, y)] = EMPTY; return false; } } pieces[index(x, y)] = EMPTY; return true; }
void testApp::fire(int player) { Player& me = players[player]; //cout << "players (0: " << players[0].life << "," << players[0].lastId << "," << players[0].position << ")" << endl; //cout << "players (1: " << players[1].life << "," << players[1].lastId << "," << players[1].position << ")" << endl; if(me.life > 0) { Player& you = players[otherPlayer(player)]; ofVec2f position = me.position; ofVec2f velocity = you.position - me.position; projectiles.push_back(Projectile(player, position, velocity)); } }
int UltimateTicTacToeMontecarloAI::simulate(Board board, int const previousMove, int const player) const { int turn = previousMove > 0 ? otherPlayer(board.grids.at(previousMove)) : player; GameState state = gameState(board, player); Move prev = previousMove; while(state == GameState::UNRESOLVED) { Moves options = movementOptions(board, prev); Move option = options.at(qrand() % options.size()); playMove(board, option, turn); turn = otherPlayer(turn); state = gameState(board, player); prev = option; } switch(state) { case GameState::WIN: return 1; case GameState::LOSE: return -1; default: return 0; } }
void scoreGame(Player p, Board &b, float &myScore, float &oppScore) { int whiteTerritory = 0, blackTerritory = 0; b.countTerritory(whiteTerritory, blackTerritory); myScore = b.getCapturedStones(p) + ((p == BLACK) ? blackTerritory : whiteTerritory); oppScore = b.getCapturedStones(otherPlayer(p)) + ((p == BLACK) ? whiteTerritory : blackTerritory); if (p == WHITE) myScore += komi; else oppScore += komi; }
int testApp::getId(int realid) { // converts realid to 0 or 1 int id; bool exists = false; for(int i = 0; i < players.size(); i++) { if(players[i].lastId == realid) { id = i; exists = true; } } if(!exists) { id = curPlayer; players[id].lastId = realid; curPlayer = otherPlayer(curPlayer); } return id; }
GameState& GameState::move(Direction direction) { lastMoveCode_ = getMoveCode(direction); getField_(currentPosition_).setOccupied_(direction); currentPosition_ = currentPosition_.getNeighbor(direction); getField_(currentPosition_).setOccupied_(reverseDirection(direction)); if(!canRebound() && !isBlocked()) { currentPlayer_ = otherPlayer(currentPlayer_); if(currentPlayer_ == PLAYER_1) ++turnNumber_; } ++moveNumber_; return *this; }
GameState& GameState::undo(Direction direction) { lastMoveCode_ = -1; if(!canRebound() && !isBlocked()) { if(currentPlayer_ == PLAYER_1) --turnNumber_; currentPlayer_ = otherPlayer(currentPlayer_); } direction = reverseDirection(direction); getField_(currentPosition_).setOccupied_(direction, false); currentPosition_ = currentPosition_.getNeighbor(direction); getField_(currentPosition_).setOccupied_(reverseDirection(direction), false); --moveNumber_; return *this; }
UltimateTicTacToeMontecarloAI::GameState UltimateTicTacToeMontecarloAI::gameState(Board const& board, int player) const { int winner = gridWinner(board.bigGrid); if(winner == player) { return GameState::WIN; } else if(winner == otherPlayer(player)) { return GameState::LOSE; } else if(boardFull(board)) { return GameState::TIE; } else { return GameState::UNRESOLVED; } }
void PlayerMoveEvent::Execute(vector<PlayerThread*>& rvPlayers,PlayerPool* pPlayerPool) { if (rvPlayers.empty()) { return; } EntityPlayer Player(_pSourcePlayer); for (int x=0; x<=rvPlayers.size()-1; x++) { if (!(rvPlayers[x]->isAssigned() && rvPlayers[x]->isSpawned())) { continue; /* not spawned */ } if (rvPlayers[x] == _pSourcePlayer) { continue; /* filter yourself */ } if (MathHelper::distance2D(rvPlayers[x]->getCoordinates(),_newCoordinates) > FC_PLAYERSPAWNRADIUS) { /* Too distant from other player*/ if (rvPlayers[x]->isEntitySpawned(_pSourcePlayer->getEntityID())) { /* despawn me*/ rvPlayers[x]->despawnEntity(_pSourcePlayer->getEntityID()); } continue; } else { /* Spawn other players into my view circle */ if (!_pSourcePlayer->isEntitySpawned(rvPlayers[x]->getEntityID())) { /* other is not spawned to me */ EntityPlayer otherPlayer(rvPlayers[x]); _pSourcePlayer->spawnEntity(&otherPlayer); continue; } } /* Update my position to other players */ if (!rvPlayers[x]->isEntitySpawned(_pSourcePlayer->getEntityID())) { /* if I'm not spawned -> spawn me */ rvPlayers[x]->spawnEntity(&Player); } else { //Already spawned -> update position rvPlayers[x]->updateEntityPosition(&Player); } } }
/* * Updates the board with a move. Assumes that the move is legal. */ void Board::doMove(Player p, Move m) { if (m == MOVE_PASS) return; int x = getX(m); int y = getY(m); assert(pieces[index(x, y)] == EMPTY); assert(chainID[index(x, y)] == 0); pieces[index(x, y)] = p; zobristKey ^= zobristTable[zobristIndex(p, x, y)]; Player victim = otherPlayer(p); Stone east = pieces[index(x+1, y)]; Stone west = pieces[index(x-1, y)]; Stone north = pieces[index(x, y+1)]; Stone south = pieces[index(x, y-1)]; int connectionCount = (east == p) + (west == p) + (north == p) + (south == p); // If the stone placed is a new chain if (connectionCount == 0) { // Record which chain this square is a part of chainID[index(x, y)] = nextID; // Add this chain to the list of chains Chain *cargo = new Chain(p, nextID); cargo->add(m); cargo->liberties = 0; if (east == EMPTY) cargo->addLiberty(coordToMove(x+1, y)); if (west == EMPTY) cargo->addLiberty(coordToMove(x-1, y)); if (north == EMPTY) cargo->addLiberty(coordToMove(x, y+1)); if (south == EMPTY) cargo->addLiberty(coordToMove(x, y-1)); chainList.add(cargo); nextID++; } // If the stone placed is added to an existing chain else if (connectionCount == 1) { // Find the ID of the chain we are adding this stone to int thisID; if (east == p) thisID = chainID[index(x+1, y)]; else if (west == p) thisID = chainID[index(x-1, y)]; else if (north == p) thisID = chainID[index(x, y+1)]; else thisID = chainID[index(x, y-1)]; chainID[index(x, y)] = thisID; Chain *node = nullptr; searchChainsByID(node, thisID); node->add(m); // The new stone occupies a previous liberty, but adds on however many // liberties it itself has node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); } // If the stone possibly connects two existing chains else { int eastID = (east == p) * chainID[index(x+1, y)]; int westID = (west == p) * chainID[index(x-1, y)]; int northID = (north == p) * chainID[index(x, y+1)]; int southID = (south == p) * chainID[index(x, y-1)]; Chain *node = nullptr; bool added = false; if (eastID) { chainID[index(x, y)] = eastID; searchChainsByID(node, eastID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } if (westID) { if (added) { // If two stones from the same chain are adjacent, do nothing // If they are from different chains, we need to combine... if (westID != eastID) mergeChains(node, westID, m); } else { chainID[index(x, y)] = westID; searchChainsByID(node, westID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } } if (northID) { if (added) { if (northID != eastID && northID != westID) mergeChains(node, northID, m); } else { chainID[index(x, y)] = northID; searchChainsByID(node, northID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } } if (southID) { if (added) { if (southID != eastID && southID != westID && southID != northID) mergeChains(node, southID, m); } else { chainID[index(x, y)] = southID; searchChainsByID(node, southID); node->add(m); node->removeLiberty(node->findLiberty(m)); updateLiberty(node, x, y); added = true; } } } // Update opponent liberties int eastID = (east == victim) * chainID[index(x+1, y)]; int westID = (west == victim) * chainID[index(x-1, y)]; int northID = (north == victim) * chainID[index(x, y+1)]; int southID = (south == victim) * chainID[index(x, y-1)]; if (eastID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, eastID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } if (westID && westID != eastID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, westID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } if (northID && northID != eastID && northID != westID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, northID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } if (southID && southID != eastID && southID != westID && southID != northID) { Chain *node = nullptr; int nodeIndex = searchChainsByID(node, southID); node->removeLiberty(node->findLiberty(m)); if (node->liberties == 0) captureChain(node, nodeIndex); } // Check for a suicide int selfID = chainID[index(x, y)]; Chain *node = nullptr; int nodeIndex = searchChainsByID(node, selfID); if (node->liberties == 0) captureChain(node, nodeIndex); // A debugging check assert(!checkChains()); // Check if p captured any of the other player's stones with move m /* doCaptures<true>(victim, coordToMove(x+1, y)); doCaptures<true>(victim, coordToMove(x-1, y)); doCaptures<true>(victim, coordToMove(x, y+1)); doCaptures<true>(victim, coordToMove(x, y-1)); // Check if p suicided with move m doCaptures<true>(p, coordToMove(x, y)); */ }
Player GameState::whoWon() const { return currentPosition_.y == height_ / 2 ? PLAYER_1 : currentPosition_.y == -height_ / 2 ? PLAYER_2 : isBlocked() ? otherPlayer(currentPlayer_) : NO_PLAYER; }
pair chooseMoveEvaluate (int* iboard, int player) { ASSERT(player == XPL || player == OPL); pair myBest = {0,-1}; pair reply; int i, move; int moveCount=0, moveList[9]; //default score if(player == US){ myBest.score = INT_MIN; } else { myBest.score = INT_MAX; } timesCalled++; pair temp; temp.score = 0; temp.move = -1; if(didHeWin(iboard,US)) { //printf("%c WON\n",US); //printf("=====returning{1}====================\n"); //printboard(iboard); //temp.score = 10+timesCalled; temp.score = evaluateBoard(iboard); return temp; } else if(didHeWin(iboard,THEM)) { //printf("%c WON\n",THEM); //printf("=====returning{-1}====================\n"); //printboard(iboard); //temp.score = -10-timesCalled; temp.score = evaluateBoard(iboard); return temp; } else if(isBoardFull(iboard)) { //printf("FULL\n"); //printf("=====returning{0}====================\n"); //temp.score = 0; return temp; } // get legal moves for(i = 0; i < 9; i++) { if( iboard[convert9To25[cellScoreOrdered[i]]] == EMPTY) { moveList[moveCount++] = convert9To25[cellScoreOrdered[i]]; } } // for(i = 0; i < 9; i++) { // if( iboard[convert9To25[i]] == EMPTY) { // moveList[moveCount++] = convert9To25[i]; // } // } // any legal move //myBest.move = pickValuedMove(moveList,moveCount); //printf("Move by %c;*********************************************\n",player); //for(i=0; i < moveCount; i++) { //printf("%d ", moveList[i]); //} //printf("\n"); for(i=0; i < moveCount; i++) { move = moveList[i]; iboard[move] = player; //printf("%c(to move(%d/%d) %d);best:%d ;reply:%d \n", // player,i+1,moveCount,move,myBest.score,reply.score); //printboard(iboard); reply = chooseMove(iboard, otherPlayer(player)); iboard[move] = EMPTY; //printf("RESET %c(to move(%d/%d) %d);best:%d ;reply:%d \n", // player,i+1,moveCount,move,myBest.score,reply.score); if( // maximizinf by selecting opponents less score (player == US && myBest.score < reply.score) || // minimizing by letting them select high score (player == THEM && myBest.score > reply.score)){ // printf("%c SCORE CHANGED (score:%d, best move:%d)!!\n", // player,reply.score,move); myBest.score = reply.score; myBest.move = move; } } return myBest; }
GameResult RunAIGame::play(Depth depth, BoardWidth size, RulesType rules, bool contenderFirst) { for (int i=0; i<=1; i++) { PenteGame *p = _players[i]; p->restartGame(); p->setColour(i+1); p->setNormalDepth(depth); p->setBoardSize(size); p->setRules(rules); } int toMove = P1; AlphaBeta ab_games[2] = { AlphaBeta(*_players[0]), AlphaBeta(*_players[1]) }; GameResult res = GameResult(); res._depth = std::to_string((int)depth); res._size = std::to_string((int)size); res._rules = rules; res._contenderP = (contenderFirst ? "P1" : "P2"); Colour winner = EMPTY; while (winner == EMPTY) { Timer tmr; Loc bestMove = ab_games[toMove-1].getBestMove(); if (!_silent) { std::cout << bestMove << std::endl; } res._times[toMove-1] += tmr.elapsed(); assert(_players[0]->isLegalMove(bestMove)); _players[0]->makeMove(bestMove, toMove); _players[1]->makeMove(bestMove, toMove); toMove = otherPlayer(toMove); winner = _players[1]->getWonBy(); if (!_silent) { // TODO: Use another flag for games _players[0]->print(); #if 0 const PriorityLevel &p1Threes = _players[0]->_posStats.getPriorityLevel(P1, Line3); const PriorityLevel &p2Threes = _players[0]->_posStats.getPriorityLevel(P2, Line3); const PriorityLevel &p1Fours = _players[0]->_posStats.getPriorityLevel(P1, Line4); const PriorityLevel &p2Fours = _players[0]->_posStats.getPriorityLevel(P2, Line4); cout << "P1 3s: " << p1Threes.getNumCands() << "; P2 3s: " << p2Threes.getNumCands() << "; P1 4s: " << p1Fours.getNumCands() << "; P2 4s: " << p2Fours.getNumCands() << endl; #endif } } res._winnerWasContender = ((winner==P2) xor contenderFirst); return res; }
Move generateMove(Player p, Move lastMove) { MoveList legalMoves = game.getLegalMoves(p); MoveList localMoves = game.getLocalMoves(lastMove); // Pass if every move is either into your own eye, a suicide, or places // a chain into atari bool playPass = true; for (unsigned int n = 0; n < legalMoves.size(); n++) { Board copy = Board(game); Move m = legalMoves.get(n); if (!copy.isMoveValid(otherPlayer(p), m) && copy.isEye(p, m)) continue; if (!copy.isMoveValid(p, m)) continue; copy.doMove(p, m); if (copy.isInAtari(m)) continue; playPass = false; break; } if (playPass) return MOVE_PASS; MCTree searchTree; float komiAdjustment = 0.0; Move captureLastStone = game.getPotentialCapture(lastMove); Move potentialEscape = game.getPotentialEscape(p, lastMove); // Add all first-level moves for (unsigned int n = 0; n < legalMoves.size(); n++) { Board copy = Board(game); Player genPlayer = p; Move next = legalMoves.get(n); // Check legality of moves (suicide) if (!copy.isMoveValid(genPlayer, next)) continue; copy.doMove(genPlayer, next); // Never place own chain in atari if (copy.isInAtari(next)) continue; // Check for ko rule violation bool koViolation = false; if (next != MOVE_PASS) { uint64_t newKey = copy.getZobristKey(); for (int i = keyStackSize-1; i >= 0; i--) { if (newKey == keyStack[i]) { koViolation = true; break; } } } if (koViolation) continue; // First level moves are added to the root MCNode *leaf = searchTree.root; MCNode *addition = new MCNode(); addition->parent = leaf; addition->m = next; // Play out a random game. The final board state will be stored in copy. playRandomGame(otherPlayer(genPlayer), copy); // Score the game float myScore = 0.0, oppScore = 0.0; scoreGame(genPlayer, copy, myScore, oppScore); if (myScore > oppScore) addition->numerator++; addition->scoreDiff = ((int) myScore) - ((int) oppScore); komiAdjustment += myScore - oppScore; // Add the new node to the tree leaf->children[leaf->size] = addition; leaf->size++; // Backpropagate the results searchTree.backPropagate(addition); // Do priors, if any // Own eye and opening priors inspired by Pachi, // written by Petr Baudis and Jean-loup Gailly int basePrior = boardSize * boardSize / 8; // Discourage playing into own eyes if (game.isEye(genPlayer, next)) { addition->denominator += basePrior; // If this eye is not ko-related we almost certainly should not play // in it if (!game.isMoveValid(otherPlayer(genPlayer), next)) { addition->denominator += 10 * basePrior; addition->scoreDiff -= 10 * 360; } } // Discourage playing onto edges and encourage playing onto the 4th line // in 13x13 and 19x19 openings unsigned int openingMoves = boardSize * boardSize - boardSize; if ((boardSize == 13 || boardSize == 19) && legalMoves.size() > openingMoves) { int x = getX(next); int y = getY(next); if (x == 1 || x == 19 || y == 1 || y == 19) { addition->denominator += 2 * basePrior; } else { int taperedPrior = basePrior * (legalMoves.size() - openingMoves) / boardSize; if (x == 4 || x == boardSize-3) { addition->numerator += 2 * taperedPrior; addition->denominator += 2 * taperedPrior; } if (y == 4 || y == boardSize-3) { addition->numerator += 2 * taperedPrior; addition->denominator += 2 * taperedPrior; } if (x == 3 || x == boardSize-2 || y == 3 || y == boardSize-2) { addition->numerator += taperedPrior; addition->denominator += taperedPrior; } } } // And the same for 9x9 else if (boardSize == 9 && legalMoves.size() > openingMoves) { int x = getX(next); int y = getY(next); if (x == 1 || x == boardSize || y == 1 || y == boardSize) { addition->denominator += 2 * basePrior; } else { int taperedPrior = basePrior * (legalMoves.size() - openingMoves) / boardSize; if (x == 3 || x == boardSize-2) { addition->numerator += 2 * taperedPrior; addition->denominator += 2 * taperedPrior; } if (y == 3 || y == boardSize-2) { addition->numerator += 2 * taperedPrior; addition->denominator += 2 * taperedPrior; } } } // Add a bonus for capturing a chain that the opponent placed // into atari on the previous move if (next == captureLastStone) { addition->numerator += 5 * basePrior; addition->denominator += 5 * basePrior; } // Add a bonus for escaping when the opponent's last move // placed our chain into atari if (next == potentialEscape) { addition->numerator += 5 * basePrior; addition->denominator += 5 * basePrior; } // Add a bonus to local moves if (legalMoves.size() < openingMoves) { int li = localMoves.find(next); if (li != -1) { localMoves.removeFast(li); } else { addition->numerator += basePrior; addition->denominator += 2 * basePrior; } } } // If we have no moves that are ko-legal, pass. if (searchTree.root->size == 0) return MOVE_PASS; // Calculate an estimate of a komi adjustment komiAdjustment /= legalMoves.size(); // Expand the MC tree iteratively for (int n = 0; n < playouts; n++) { Board copy = Board(game); Player genPlayer = p; // Find a node in the tree to add a child to int depth = -1; MCNode *leaf = searchTree.findLeaf(genPlayer, copy, depth); MCNode *addition = new MCNode(); addition->parent = leaf; MoveList candidates = copy.getLegalMoves(genPlayer); candidates.add(MOVE_PASS); // Set up a permutation matrix int *permutation = new int[candidates.size()]; // Fisher-Yates shuffle for (unsigned int i = 0; i < candidates.size(); i++) { std::uniform_int_distribution<int> distribution(0, i); int j = distribution(rng); permutation[i] = permutation[j]; permutation[j] = i; } // Find a random move that has not been explored yet Move next = 0; for (unsigned int i = 0; i < candidates.size(); i++) { next = candidates.get(permutation[i]); bool used = false; for (int j = 0; j < leaf->size; j++) { if (next == leaf->children[j]->m) { used = true; break; } } if (!used && copy.isMoveValid(genPlayer, next)) break; } delete[] permutation; addition->m = next; copy.doMove(genPlayer, next); // Play out a random game. The final board state will be stored in copy. playRandomGame(otherPlayer(genPlayer), copy); // Score the game... somehow... float myScore = 0.0, oppScore = 0.0; scoreGame(genPlayer, copy, myScore, oppScore); myScore += (genPlayer == p) ? -komiAdjustment : komiAdjustment; if (myScore > oppScore) { addition->numerator++; // If the node is not a child of root if (depth) raveTable.inc(next, depth); } else { if (depth) raveTable.dec(next, depth); } addition->scoreDiff = ((int) myScore) - ((int) oppScore); // Add the new node to the tree leaf->children[leaf->size] = addition; leaf->size++; // Backpropagate the results searchTree.backPropagate(addition); } // Find the highest scoring move Move bestMove = searchTree.root->children[0]->m; double bestScore = 0.0; int64_t diff = -(1 << 30); int maxRAVE = raveTable.max(); for (int i = 0; i < searchTree.root->size; i++) { double candidateScore = (double) searchTree.root->children[i]->numerator / (double) searchTree.root->children[i]->denominator; // + (double) raveTable.score(searchTree.root->children[i]->m) // / ((double) maxRAVE) // / (16 + std::sqrt(searchTree.root->children[i]->denominator)) // + (double) searchTree.root->children[i]->scoreDiff // / (double) (360 * 32); // if (candidateScore > bestScore) { // bestScore = candidateScore; // bestMove = searchTree.root->children[i]->m; // } if (debugOutput) { std::cerr << "(" << getX(searchTree.root->children[i]->m) << ", " << getY(searchTree.root->children[i]->m) << "): " << searchTree.root->children[i]->numerator << " / " << searchTree.root->children[i]->denominator << std::endl; } if (candidateScore > bestScore || (candidateScore == bestScore && searchTree.root->children[i]->scoreDiff > diff)) { bestScore = candidateScore; bestMove = searchTree.root->children[i]->m; diff = searchTree.root->children[i]->scoreDiff; } } raveTable.age(); return bestMove; }
// Counts the territory each side owns void Board::countTerritory(int &whiteTerritory, int &blackTerritory) { whiteTerritory = 0; blackTerritory = 0; Stone *visited = new Stone[arraySize*arraySize]; Stone *territory = new Stone[arraySize*arraySize]; Stone *region = new Stone[arraySize*arraySize]; // Count territory for both sides for (Player p = BLACK; p <= WHITE; p++) { // Reset the visited array for (int i = 0; i < arraySize*arraySize; i++) visited[i] = 0; // Main loop for (int j = 1; j <= boardSize; j++) { for (int i = 1; i <= boardSize; i++) { // Don't recount territory if (visited[index(i, j)]) continue; // Only use empty squares as seeds if (pieces[index(i, j)]) continue; if (isEye(p, coordToMove(i, j))) { visited[index(i, j)] = 1; if (p == BLACK) blackTerritory++; else whiteTerritory++; continue; } for (int k = 0; k < arraySize*arraySize; k++) territory[k] = 0; int territorySize = 0; int boundarySize = 0; getTerritory(p, i, j, visited, territory, territorySize, boundarySize); // Check if territory was actually sectioned off if (territorySize + boundarySize == boardSize*boardSize) continue; // Detect life/death of internal stones // Initialize region to 0 if territory is 1, and vice versa // This acts as our "visited" array, so that we only explore areas // inside the territory // bool isContested = false; // for (int n = 1; n <= boardSize; n++) { // for (int m = 1; m <= boardSize; m++) { // if (!territory[index(m, n)]) // continue; // if (pieces[index(m, n)] == otherPlayer(p)) { // isContested = true; // break; // } // } // } // if (!isContested) { // if (p == BLACK) // blackTerritory += territorySize; // else // whiteTerritory += territorySize; // } for (int k = 0; k < arraySize*arraySize; k++) region[k] = territory[k] ^ 1; int internalRegions = 0; for (int n = 1; n <= boardSize; n++) { for (int m = 1; m <= boardSize; m++) { if (region[index(m, n)]) continue; if (pieces[index(m, n)]) continue; MoveList eye; if (isSurrounded(EMPTY, p, m, n, region, eye)) internalRegions++; } } int territoryCount = 0; if (internalRegions == 0) { territoryCount += territorySize; // Score dead stones for (int k = 0; k < arraySize*arraySize; k++) if (territory[k] && pieces[k] == otherPlayer(p)) territoryCount++; } if (p == BLACK) blackTerritory += territoryCount; else whiteTerritory += territoryCount; } } } delete[] visited; delete[] territory; delete[] region; }
pair chooseMoveABDepth (int* iboard, int player, int alpha, int beta) { ASSERT(player == XPL || player == OPL); pair myBest = {0,-1}; pair reply; int i,j, move; int moveCount=0, moveList[9]; //default score if(player == US){ myBest.score = alpha; } else { myBest.score = beta; } timesCalled++; pair temp; //temp.score = evaluateBoard(iboard); temp.move = -1; // if(level == 0){ // temp.score = evaluateBoard(iboard); // return temp; // } if(didHeWin(iboard,US)) { //printf("%c WON\n",US); //printboard(iboard); //temp.score = evaluateBoard(iboard); temp.score = evaluatePosition(iboard,US); return temp; } else if(didHeWin(iboard,THEM)) { //printf("%c WON\n",THEM); //printboard(iboard); //temp.score = evaluateBoard(iboard); temp.score = evaluatePosition(iboard,US); return temp; } else if(isBoardFull(iboard)) { //printf("FULL\n"); //printboard(iboard); temp.score = evaluatePosition(iboard,US); return temp; } // get legal moves in25 for(i = 0; i < 9; i++) { if( iboard[convert9To25[i]] == EMPTY) { moveList[moveCount++] = convert9To25[i]; } } // for(i = 0; i < 9; i++) { // if( iboard[convert9To25[cellScoreOrdered[i]]] == EMPTY) { // moveList[moveCount++] = convert9To25[cellScoreOrdered[i]]; // } // } myBest.move = moveList[0]; for(i=0; i < moveCount; i++) { move = moveList[i]; iboard[move] = player; reply = chooseMoveAB(iboard, otherPlayer(player),alpha,beta); iboard[move] = EMPTY; // maximum lower bound of possible solutions if(player == US && myBest.score < reply.score){ alpha = reply.score; myBest.score = evaluatePosition(iboard,US); myBest.move = move; } // minimum upper bound of possible solutions else if (player == THEM && myBest.score > reply.score){ beta = reply.score; myBest.score = reply.score; myBest.move = move; } if(alpha >= beta){ return myBest; } } return myBest; }