def findSpotToExpand(node, valid_moves): """ Finds a new child(a new gamestate associated with a move) for the current node. Alternatively, if all children are already given, calculate the children for the best child. """ # print("Find spot to expand: ", node, " valid moves: ", valid_moves) # If node is terminal(game finished), return current node if node.isTerminal(): # print("Node terminal in findspottoexpand: ", node) return node if not node.isFullyExpanded(): # Create a new child node with a random valid move and add it to the current node's children new_gamestate = copy.deepcopy(node.game) new_gamestate_move = random.choice(node.valid_moves) valid, win, ngs = gomoku.move(new_gamestate, new_gamestate_move) if type(ngs) != gomoku.GameState and type(ngs) != tuple: print(type(ngs)) print("Error in findspottoexpand!") return node newChild = Node(node, ngs, new_gamestate_move) node.children.append(newChild) # Return the new child # print("Returning newchild: ", newChild) return newChild # print("FSTE > Node is fully expanded. Finding best child and calling that") # If the node is already expanded, find the best child of this node and expand that one newNode = node.findBestChild() # print("FSTE > Best child: ", newNode) return findSpotToExpand(newNode, gomoku.valid_moves( newNode.game)) # Find a new one with the list of valid moves
def move(self, board, last_move, max_time_to_move=1000): """This is the most important method: the agent will get: 1) the current state of the board 2) the last move by the opponent 3) the available moves you can play (this is a special service we provide ;-) ) 4) the maximimum time until the agent is required to make a move in milliseconds [diverging from this will lead to disqualification]. """ valid_moves = gomoku.valid_moves(board) time_deadline = time.time() + ( max_time_to_move * 0.90 / 1000 ) # Deadline is 99% of the time to move in ms in ns. Can be optimized # If only 1 move is possible, do that move if (len(valid_moves) == 1): return valid_moves[0] currentGame = gomoku.gomoku_game(19, copy.deepcopy(board), self.play) # Current game board rootNode = Node(None, currentGame, last_move) # Current Rootnode(active gamestate) while (time.time() < time_deadline): ## Calculate new move newLeaf = findSpotToExpand(rootNode) ## Play until game ends for that move gameresult = rollout(newLeaf) ## Calculate score for that move backupValue(gameresult, newLeaf) # Find best child #-- Met de code hieronder zou die als die een fout maakt, alsnog een slimme keuze moeten maken. Dit was tijdelijke code, maar maakt hem ook dommer? # bestChild = None # while len(rootNode.children) != 0: # bestChild = rootNode.findBestChild() # if not bestChild.last_move in valid_moves: # rootNode.children.remove(bestChild) # print("Current best child move is invalid") # else: # break # if not bestChild.last_move in valid_moves: # print("Chosen best child move is invalid") # return valid_moves[0] # If the best child is invalid for some reason, just use a random valid move bestChild = rootNode.findBestChild() if not bestChild.last_move in valid_moves: print("Chosen best child move is invalid") return valid_moves[0] print("Supplied Valid Moves: {}".format(len(valid_moves))) print("Simulated Valid Moves: {}".format(len( currentGame.valid_moves()))) gomoku.prettyboard(currentGame.board) self.play += 2 return bestChild.last_move
def move(self, state: GameState, last_move: Move, max_time_to_move: int = 1000) -> Move: """This is the most important method: the agent will get: 1) the current state of the game 2) the last move by the opponent 3) the available moves you can play (this is a special service we provide ;-) ) 4) the maximum time until the agent is required to make a move in milliseconds [diverging from this will lead to disqualification]. """ moves = gomoku.valid_moves(state) return random.choice(moves)
def move(self, state, last_move, max_time_to_move=1000): """This is the most important method: the agent will get: 1) the current state of the state 2) the last move by the opponent 3) the available moves you can play (this is a special service we provide ;-) ) 4) the maximimum time until the agent is required to make a move in milliseconds [diverging from this will lead to disqualification]. """ valid_moves = gomoku.valid_moves(state) time_deadline = time.time() + ( max_time_to_move * 1 / 1000 ) # Deadline is 99% of the time to move in ms in ns. Can be optimized board = state[0] ply = state[1] # If only 1 move is possible, do that move if (len(valid_moves) == 1): return valid_moves[0] currentGame = copy.deepcopy(state) # Current game board rootNode = Node(None, currentGame, last_move) # Current Rootnode(active gamestate) cRound = 0 while (time.time() < time_deadline): ## Calculate new move newLeaf = findSpotToExpand(rootNode, valid_moves) # print("FindSpotToExpand Done: ", newLeaf) ## Play until game ends for that move gameresult = rollout(newLeaf) ## Calculate score for that move backupValue(gameresult, newLeaf) # If the best child is invalid for some reason, just use a random valid move # With the new code it should always be valid, but just in case bestChild = rootNode.findBestChild() if not bestChild.last_move in valid_moves: print("Chosen best child move is invalid") return valid_moves[0] self.play += 2 return bestChild.last_move
def getValidMoves(self): """ Returns the current valid moves for the game statea """ self.valid_moves = gomoku.valid_moves(self.game) return self.valid_moves
def play_competition(self, maxtime_per_move=1000, tolerance=0.05): """This method runs the actual competition between the registered players. Each player plays each other player twice: once with black and once with white.""" self.results = [] mtime = maxtime_per_move * ( 1.0 + tolerance) * 1000000 # operational maxtime in nanoseconds for i in range(len(self.players)): self.results.append( [0.0] * len(self.players)) # set the results matrix to all zeroes for i in range(len(self.players)): for j in range(len(self.players)): if (i == j): continue # players do not play themselves self.players[i].new_game(True) # player i is black self.players[j].new_game(False) # player j is white game = gomoku.starting_state( bsize_=self.bsize) # initialise the game previous_move = () over = False currentRound = 1 while not over: print("Current Round: ", currentRound) if game[1] % 2 == 1: # black to move current_player = self.players[i] pid = i pid_other = j else: # white to move current_player = self.players[j] pid = j pid_other = i start_time = time.time_ns() move = current_player.move( game, previous_move, max_time_to_move=maxtime_per_move) stop_time = time.time_ns() currentRound += 1 # print(str((stop_time-start_time)/1000000)+"/"+str(maxtime_per_move*(1+tolerance))) ok, win, game = gomoku.move( game, move ) # perform the move, and obtain whether the move was valid (ok) and whether the move results in a win previous_move = move # Uncomment the follwing two lines if you want to watch the games unfold slowly: # time.sleep(1) # gomoku.pretty_board(game[0]) # print("\n") if (stop_time - start_time) > mtime: # player who made the illegal move should be disqualified. This needs to be done manually. print( "disqualified for exceeding maximum time per move: player " + str(pid)) if not ok: # player who made the illegal move should be disqualified. This needs to be done manually. print("disqualified for illegal move: player " + str(pid)) print("on board: ") gomoku.pretty_board(game[0]) print("trying to play: (" + str(move[0]) + "," + str(move[1]) + ")") if game[1] % 2 == 1: print("as black") else: print("as white") if win: over = True self.results[pid][pid_other] += 1 elif len(gomoku.valid_moves(game)) == 0: # if there are no more valid moves, the board is full and it's a draw over = True self.results[pid][pid_other] += 0.5 self.results[pid_other][pid] += 0.5