def dijkstraEval(self, board_state, maximizer): h = HexBoard(board_state.shape[0]) h.board = board_state red_shortestPath = self.dijkstra(hexboard=h, color=h.RED) blue_shortestPath = self.dijkstra(hexboard=h, color=h.BLUE) freeReds = self.getFreeHexes(hexboard=h, path=red_shortestPath) freeBlues = self.getFreeHexes(hexboard=h, path=blue_shortestPath) heuristic_score = 0 if maximizer == h.RED: heuristic_score = freeBlues - freeReds else: heuristic_score = freeReds - freeBlues return heuristic_score # def iterativeDeepening(self, node, maximizer): # depth = 1 # end_time = datetime.now() + timedelta(seconds=3) # For windows os # while datetime.now() < end_time and not msvcrt.kbhit(): # bestScore = self.alphaBetaSearch(node, depth, -9999999, 9999999, isMaximizer=maximizer) # depth = depth + 1 # return bestScore
def _update_board(board: HexBoard, l_move, is_max: bool) -> HexBoard: board = copy.deepcopy( board ) # I think this was the problem with the minimax core, it was using a reference instead of a deep copy color = board.BLUE if is_max else board.RED board.place(l_move, color) return board
def alphabeta_Id(board: HexBoard, depth: int, alpha: float, beta: float, is_max: bool) -> float: # board.print() print("in alphabeta_Id000000000") try: (hit, g, ttbm) = transposition_table.lookup(board, board.get_board_size(), depth) print("in alphabeta_Id", hit, g, ttbm) except Exception as e: # print('Exception in running lookup function: ' + str(e)) if hit(): return g if depth == 0 or board.is_game_over(): g = dijkstra_eval(board) bm = () legals = board.get_move_list() if legals: if is_max: g: float = -_INF for move in ttbm + legals: updated_board: HexBoard = _update_board( board, move, is_max) # y do we make the move first? gc = alphabeta_Id(updated_board, depth - 1, alpha, beta, is_max) if gc > g: bm = updated_board g = gc alpha = max(alpha, g) if beta <= alpha: break else: # if is_max False g: float = _INF for move in ttbm + legals: updated_board: HexBoard = _update_board(board, move, is_max) gc = alphabeta_Id(updated_board, depth - 1, alpha, beta, is_max) if gc < g: bm = updated_board g = gc beta = min(beta, g) if beta <= alpha: break transposition_table.store(updated_board, updated_board.get_board_size(), g, depth, bm) return g else: print("NO MORE LEGAL MOVES LEFT") return dijkstra_eval(board)
def simple_dijkstra(board: HexBoard, source, is_max): Q = set() V_set = board.get_all_vertices() dist = {} dist_clone = {} # I made a clone of dist to retain the information in dist prev = {} for v in V_set: dist[v] = np.inf dist_clone[v] = np.inf # clone prev[v] = None Q.add(v) dist[source] = 0 dist_clone[source] = dist[source] # clone while len(Q) != 0: u = min(dist, key=dist.get) Q.remove(u) color = board.BLUE if is_max else board.RED ## IGNORE THE CODE BELOW, IT DOESN'T WORK PROPERLY AT THE MOMENT # begin reverse iteration step # this happens when we get to a border of the corresponding color (i.e. we connect the sides) # if board.border(color, u): # S = [] # path_length = 0 # if prev[u] or u == source: # while u: # if not board.is_color(u, color): # path_length += 1 # S.insert(0, u) # u = prev[u] # print(f"S for color {color} IS: {S}") # print(f"Path length when accounting for already marked hexagons: {path_length}") # return path_length, S # You can also optionally return "S" if you also want to store the path # # end reverse iteration step neighbors = board.get_neighbors(u) for v in neighbors: if v in Q: # Only check neighbours that are also in "Q" len_u_v = 0 if board.is_color( v, color) else 1 # this isn't working as intended i think... alt = dist[u] + len_u_v if alt < dist[v]: dist[v] = alt dist_clone[v] = dist[v] prev[v] = u # We pop "u" from the distance dict to ensure that the keys match the ones in "Q" dist.pop( u ) # This is also why we need the clone, or else we'll return an empty dict return dist_clone, prev
def _update_board(board: HexBoard, l_move, is_max: bool) -> HexBoard: """ Makes a deep copy of the board and updates the board state on that copy. This makes it so that we don't need to use an undo move function. The reason for using deepcopy is because python passes objects by reference if you use the "=" operator """ board = copy.deepcopy( board ) # I think this was the problem with the minimax core, it was using a reference instead of a deep copy color = board.BLUE if is_max else board.RED board.place(l_move, color) return board
def alphabeta(board: HexBoard, depth: int, alpha: float, beta: float, is_max: bool) -> float: # board.print() if depth == 0 or board.is_game_over(): return dijkstra_eval(board) legals = board.get_move_list() if legals: if is_max: g: float = -_INF for move in legals: updated_board: HexBoard = _update_board(board, move, is_max) g = max( g, alphabeta(updated_board, depth - 1, alpha, beta, is_max=False)) alpha = max(alpha, g) if beta <= alpha: break else: g: float = _INF for move in legals: updated_board: HexBoard = _update_board(board, move, is_max) g = min( g, alphabeta(updated_board, depth - 1, alpha, beta, is_max=True)) beta = min(beta, g) if beta <= alpha: break return g else: print("NO MORE LEGAL MOVES LEFT") return dijkstra_eval(board)
def alphabeta_move(board: HexBoard, depth: int, is_max: bool, show_AI=False): """ Set is_max to True for BLUE player, and False for RED player. You can set the depth to whatever you want really, just don't go too deep it'll take forever. Set show_AI to True if you want to see it's scoring process """ legal_moves = board.get_move_list() best_score = -np.inf best_move = None for move in legal_moves: sim_board = _update_board(board, move, is_max) if sim_board.check_win( sim_board.BLUE if is_max else sim_board.RED ): # KILLER MOVE: If we find a move in the simulation that wins, make that move no matter what if show_AI: print(f"KILLER MOVE FOUND: {move}") best_move = move best_score = np.inf break score = alphabeta( sim_board, depth=depth, alpha=-np.inf, beta=np.inf, is_max=is_max ) # For some reason performs better if you use is_max=False if show_AI: print(f"CURRENT SCORE: {score} for MOVE: {move}") if score > best_score: best_score = score best_move = move if show_AI: print(f"BEST MOVE: {best_move} with BEST SCORE: {best_score}") return best_move
def Expand(self, node, game_state): """ Function: Adds a random child to node, Updates game_state and node's untriedMoves Returns : The created child """ untriedMoves = node.untriedMoves m = choice(untriedMoves) # node's untriedMoves updated here node.untriedMoves.remove(m) # game_state updated here game_state = HexBoard.makeMove(m, game_state) # Check the child, if it is already appended return it otherwise create that child child = node.getChild(game_state) if child is not None: return child child_type = 'MIN' if node.type == 'MIN': child_type = 'MAX' child = Node(node_type=child_type, board_state=game_state.board, parent=node) # node's children updated here node.children.append(child) return child
def dijkstraEval(self, board_state, maximizer): h = HexBoard(board_state.shape[0]) h.board = board_state red_shortestPath = self.dijkstra(hexboard=h, color=h.RED) blue_shortestPath = self.dijkstra(hexboard=h, color=h.BLUE) freeReds = self.getFreeHexes(hexboard=h, path=red_shortestPath) freeBlues = self.getFreeHexes(hexboard=h, path=blue_shortestPath) if maximizer == h.RED: heuristic_score = freeBlues - freeReds else: heuristic_score = freeReds - freeBlues return heuristic_score
def simple_dijkstra(board: HexBoard, source, is_max): Q = set() V_set = board.get_all_vertices() dist = {} dist_clone = {} # I made a clone of dist to retain the information in dist prev = {} for v in V_set: dist[v] = np.inf dist_clone[v] = np.inf # clone prev[v] = None Q.add(v) dist[source] = 0 dist_clone[source] = dist[source] # clone while len(Q) != 0: u = min(dist, key=dist.get) Q.remove(u) color = board.BLUE if is_max else board.RED neighbors = board.get_neighbors(u) for v in neighbors: if v in Q: # Only check neighbours that are also in "Q" len_u_v = -1 if board.is_color( v, color) else 1 # this isn't working as intended i think... ### BLOCK TO MAKE AI MORE AGGRESSIVE ### if board.border( color, v ): # If there is a move that reaches the border in the simulation if board.check_win(color): # And it results in a win len_u_v = -2 # Make that move more valuable ### END OF AGGRO BLOCK ### alt = dist[u] + len_u_v if alt < dist[v]: dist[v] = alt dist_clone[v] = dist[v] prev[v] = u # We pop "u" from the distance dict to ensure that the keys match the ones in "Q" dist.pop( u ) # This is also why we need the clone, or else we'll return an empty dict return dist_clone, prev
def get_shortest_path(board: HexBoard, distances, color): """Gets the shortest path to a border and returns the length as score""" borders = board.get_borders(color) # print(f"ALL BORDERS: {borders}") shortest = np.inf for border in borders: # print(f"Dist for current border {border} is: {distances[border]}") if distances[border] < shortest: shortest = distances[border] # print(f"Current shortest: {shortest}") return shortest
def MCTS_Player(hyperparameters, isMaximizer): # First Computer's turn (MCTS) mcts_agent = MCTS( game=game, cp=hyperparameters[0], n=hyperparameters[1]) # initialize mcts agent move, params['node'] = mcts_agent.search( params['node'], hyperparameters[2], hyperparameters[3], isMaximizer) params['game'] = HexBoard.makeMove(move, params['game']) params['node'] = util.updateNode(params['node'], params['game'])
def minimax(board: HexBoard, depth: int, is_max: bool) -> float: if depth == 0 or board.is_game_over(): board.print() return dijkstra_eval(board) legals = board.get_move_list() if legals: if is_max: g: float = -_INF for move in legals: updated_board: HexBoard = _update_board(board, move, is_max) g = max(g, minimax(updated_board, depth - 1, not is_max)) else: g: float = _INF for move in legals: updated_board: HexBoard = _update_board(board, move, is_max) g = min(g, minimax(updated_board, depth - 1, not is_max)) return g
def PlayervsComputer(self): game = self.game self.clearOutput() game.print() node = Node(node_type='MIN', board_state=game.board, parent=None) # 1 - initialize node mcts_agent = MCTS(game=game, cp=0.8, n=100) # 3 - initialize mcts agent util = UTIL(infinity=np.inf, maximizer=game.maximizer, minimizer=game.minimizer) # 4 - initialize util class while not game.isGameOver(): if (game.turn % 2) == 0: move = self.getReady(game) game = HexBoard.makeMove(move, game) node = util.updateNode(node, game) else: print("Computer is thinking!!!") itermax = 100 # maximum iteration for search move, node = mcts_agent.search(node, itermax, delta=10, isMaximizer=True) print(f'best move: {move}') game = HexBoard.makeMove(move, game) node = util.updateNode(node, game) self.clearOutput() game.print() if game.isGameOver(): if game.checkWin(game.BLUE): print("!!! Blue Player Won !!!") elif game.checkWin(game.RED): print("!!! Red Player Won !!!")
def initGame(b_size, starter): #clearOutput() h = HexBoard(b_size) if starter == h.RED: h.maximizer = h.BLUE h.minimizer = h.RED else: h.maximizer = h.RED h.minimizer = h.BLUE return h
def IDTT_Player(hyperparameters, isMaximizer): # iterative deepening with 4 depth-Dijkstra boardState = params['node'].board.copy() try: move = IterativeDeepeningTranspositionTable[ boardState.tobytes()] except KeyError: best_value = util.iterativeDeepening( params['node'], isMaximizer, hyperparameters[0], hyperparameters[1]) move = util.getBestMove(params['node'], best_value) IterativeDeepeningTranspositionTable[ boardState.tobytes()] = move params['game'] = HexBoard.makeMove(move, params['game']) params['node'] = util.updateNode(params['node'], params['game'])
def alphabeta_move_Id(board: HexBoard, is_max: bool, show_AI=False): #, depth: int """ Set is_max to True for BLUE player, and False for RED player. You can set the depth to whatever you want really, just don't go too deep it'll take forever. Set show_AI to True if you want to see it's scoring process """ legal_moves = board.get_move_list() print("num of legal moves", len(legal_moves)) best_score = -np.inf best_move = None for move in legal_moves: sim_board = _update_board(board, move, is_max) if sim_board.check_win( sim_board.BLUE if is_max else sim_board.RED ): # KILLER MOVE: If we find a move in the simulation that wins, make that move no matter what if show_AI: print(f"KILLER MOVE FOUND: {move}") best_move = move best_score = np.inf break #thread = threading.Thread(target=iterative_deepening, kwargs=dict(board = sim_board,alpha=-np.inf,beta = np.inf, is_max = is_max)) #thread.start() with concurrent.futures.ThreadPoolExecutor() as executor: future = executor.submit(iterative_deepening, sim_board, alpha=-np.inf, beta=np.inf, is_max=is_max) score = future.result() print("score ", score) # wait here for the result to be available before continuing #thread.join() #score = iterative_deepening(sim_board, alpha=-np.inf, beta=np.inf, # is_max=is_max) # For some reason performs better if you use is_max=False if show_AI: print(f"ID CURRENT SCORE: {score} for MOVE: {move}") if score > best_score: best_score = score best_move = move if show_AI: print(f"BEST MOVE_ID: {best_move} with BEST SCORE: {best_score}") return best_move
def getBestMove(node, bestVal): bestChildren = [] for child in node.children: if child.value == bestVal: bestChildren.append(child) if len(bestChildren) > 0: index = np.random.randint(0, len(bestChildren)) bestNode = bestChildren[index] else: # print("Error, best Move couldn't find. Randomly selects the move!") moves = HexBoard.getMoveList(node) index = np.random.randint(0, len(moves)) return moves[index] b_size = node.board.shape[0] for i in range(b_size): for j in range(b_size): if node.board[i, j] != bestNode.board[i, j]: return i, j
def __init__(self, node_type, board_state, parent): self.id = str(uuid.uuid4()) # to make it unique self.name = self.id[ -3:] # to make it simple during visualizations (for the test purposes) self.type = node_type # for visualization self.board = board_state # for MCTS untriedMoves = HexBoard.getFreeMoves(board_state) self.untriedMoves = untriedMoves # for MCTS self.children = [] # for MCTS if parent is not None: self.parents = [parent] self.parent_type = parent.type # for visualization else: self.parents = [] # for MCTS self.parent_type = None # for visualization self.searched = False self.value = None self.visit = 0 # for MCTS UCT-Selection and finding the best move self.wins = np.inf # for MCTS UCT-Selection self.loss = np.inf
def dijkstra_eval(board: HexBoard): """ Checks the best path from every possible source (e.g. L to R for blue) and then returns the best evaluation score of the board based on the most efficient source """ best_eval_score = -np.inf for i in range(board.get_board_size()): blue_source = (0, i) red_source = (i, 0) blue_dists, _ = simple_dijkstra(board, blue_source, True) red_dists, _ = simple_dijkstra(board, red_source, False) blue_score = get_shortest_path(board, blue_dists, board.BLUE) red_score = get_shortest_path(board, red_dists, board.RED) eval_score = red_score - blue_score if eval_score > best_eval_score: best_eval_score = eval_score return best_eval_score
def initGame(): clearOutput() b_size = int(input("Enter the board size you want to play: ")) h = HexBoard(b_size) ans = str( input("In order to start the game, please choose a side 'R' or 'B': ") ).lower() if ans == 'b': h.maximizer = h.RED h.minimizer = h.BLUE elif ans == 'r': h.maximizer = h.BLUE h.minimizer = h.RED else: print("You pressed a wrong key. Please type 'R' or 'B'.") print("Game is restarting!!!") time.sleep(2) h = initGame() return h
def main(): board = HexBoard(2) num_of_cells = board.get_board_size() * board.get_board_size() for nc in range(int(num_of_cells / 2)): ## Just a small heuristic for opening strategy, you can test this if you want. But then you have to comment the move_blue below out too # if board.size % 2 != 0 and len(board.get_move_list()) == len(board.get_all_vertices()): # If it's the first move and the board is uneven # move_blue = (board.size // 2, board.size // 2) # Always place the first move in the middle # else: # move_blue = ab.alphabeta_move(board, depth=2, is_max=True) move_blue = ab.alphabeta_move(board, depth=4, is_max=True) #move_blue = ab.alphabeta_move_Id(board, is_max=True, show_AI=True) board = ab._update_board(board, move_blue, is_max=True) board.print() if board.is_game_over( ): # TODO: add condition for game over without no winning (board full) print("==== BLUE WINS ====") board.print() # break return "blue" move_red = ab.alphabeta_move(board, depth=2, is_max=True) #move_red = ab.alphabeta_move_Id(board, is_max=True, show_AI=True) board = ab._update_board( board, move_red, is_max=False ) # Using false here and true for the alphabeta is a bit confusing, but we need it to make moves for red here. board.print() if board.is_game_over( ): # TODO: add condition for game over without no winning (board full) print("==== RED WINS ====") board.print() # break return "red"
import numpy as np from hex_skeleton import HexBoard # sanity check that wins are detected for i in range(0, 2): winner = HexBoard.RED if i == 0 else HexBoard.BLUE loser = HexBoard.BLUE if i == 0 else HexBoard.RED board = HexBoard(3) board.place((1, 1), loser) board.place((2, 1), loser) board.place((1, 2), loser) board.place((2, 2), loser) board.place((0, 0), winner) board.place((1, 0), winner) board.place((2, 0), winner) board.place((0, 1), winner) board.place((0, 2), winner) assert (board.check_win(winner) == True) assert (board.check_win(loser) == False) board.print() endable_board = HexBoard(4) # sanity check that random play will at some point end the game while not endable_board.game_over: endable_board.place((np.random.randint(0, 4), np.random.randint(0, 4)), HexBoard.RED) assert (endable_board.game_over == True) assert (endable_board.check_win(HexBoard.RED) == True) assert (endable_board.check_win(HexBoard.BLUE) == False) print("Randomly filled board") endable_board.print()
f.write(f'd = {d} g = {g} a = {a} b = {b}\n') f.close() board.make_empty((i, j)) #virtual_board.print() if a >= g: break if d == search_depth: f = open('movelist.txt', 'a') f.write(f'\n{search.d2l_conversion(best_move[0])},{best_move[1]}') f.close() return g # Initialise the board. board = HexBoard(BOARD_SIZE) # Create a virtual board for the alphabeta algorithm. virtual_board = board # Make a text file for the moves. f = open('movelist.txt', 'w') f.write('Movelist') f.close() while not board.game_over: search_depth = 3 eval_val = alphabeta(virtual_board, d=search_depth, a=-INF, b=INF,
df_movelist = df_movelist.append({'x': x, 'y': y}, ignore_index=True) board.place((x, y), agent2) board.print() # Play the game. def play_game(board, method): for i in range(SIZE * SIZE): board_copy = copy.deepcopy(board) eval_val = alphabeta(board_copy, DEPTH, -INF, INF, agent1, agent2, method) df_alphabeta.to_csv('alphabeta.txt', index=False, mode='a') ai_make_move(board) if board.check_win(agent1): print('A1 WINS') break pl_make_move(board) if board.check_win(agent2): print('A2 WINS') break DEPTH = 3 board = HexBoard(SIZE) play_game(board, 'Dijkstra')
# Agents Matchup 2: [depth = 3, policy = random] vs. [depth = 4, policy = dijkstra] # Agents Matchup 3: [depth = 3, policy = dijkstra] vs. [depth = 4, policy = dijkstra] print("[Initalized Player Matchup: Experiment 1]") print("[---------------------------------------------------------]") res_lst_1 = [] res_elo_1 = [] res_elo_2 = [] res_elo_1.append(player1_ratings.mu) res_elo_2.append(player2_ratings.mu) n_games = 25 # number of games size = 5 # board size board = HexBoard(size) # init board player_1_lst = [ player(agent='AI', color=HexBoard.BLUE, policy="random"), player(agent='AI', color=HexBoard.BLUE, policy="random"), player(agent="AI", color=HexBoard.BLUE, policy="alphabeta", eval="dijkstra", depth=3) ] player_2_lst = [ player(agent="AI", color=HexBoard.BLUE, policy="alphabeta",
def human_compu(algor, method='random', depth=3): """A function that human play with computer, user can choose their prefered board size(3,4 recommanded), color, want to take first move""" global flag, INF, n_nodes, cutoff, Game, size print('Choose a board size:') size = int(input()) Game = HexBoard(size) print('Choose a color: 1(BLUE) or 2(RED)') print('Blue: from left to right; Red: from top to bottom') opp_color = int(input()) my_color = Game.get_opposite_color(opp_color) print('Do you want to start first? Yes(y) or No(n)') first = input() print('Game start!') # human first move do or not if first == 'y' or first == 'Yes': Game.print() x, y = xy_read(Game) Game.place(coordinates=(x, y), color=opp_color) Game.print() root = Tree(Game, color=opp_color, parent=None, coordinate=(x, y), my_color=my_color) else: first_color = my_color last_color = opp_color root = Tree(Game, color=opp_color, parent=None, coordinate=None, my_color=my_color) INF = 99999 # sufficient large number # human and computer play the game until one of them win while not Game.is_game_over(): if algor == 'alphabeta': # varibales intialization n_nodes = 0 cutoff = 0 flag = 0 my_move = alphabeta(n=root, a=-INF, b=INF, d=depth, method=method, depth=depth, my_color=my_color, opp_color=opp_color) print('n_nodes=', n_nodes, '\n cutoff=', cutoff) elif algor == 'idtt': flag = 0 transpositiontable = [] n_nodes = 0 cutoff = 0 my_move = iterativedeepening(n=root, a=-INF, b=INF, DEPTH_stop=5, time_limit=5, my_color=my_color, opp_color=opp_color) print('n_nodes=', n_nodes, '\n cutoff=', cutoff) elif algor == "mcts": my_move = monte_carlo_tree_search(root, cp=1, N=1000) # retunred variable "my_move" is a node and contains info about computer's next step Game.place(coordinates=my_move.location, color=my_move.color) Game.print() if Game.is_game_over(): break else: # read human's next move x, y = xy_read(Game) Game.place(coordinates=(x, y), color=opp_color) Game.print() root = Tree(Game, color=opp_color, parent=None, coordinate=(x, y), my_color=my_color) if Game.check_win(opp_color): print('Game over! You win :-)') else: print('Game over! You lose :-(')
def mcts_mcts(cp1, N1, cp2, N2, size, print_all=False, first=True): #mcts plays with mcts Game = HexBoard(size) A1_color = 1 A2_color = 2 root = Tree(Game, color=A2_color, parent=None, coordinate=None) if print_all: if first == True: print('First move (blue) is mcts') print('Second move (red) is mcts') else: print('First move (blue) is mcts') print('Second move (red) is mcts') while not Game.is_game_over(): if first == True: #mcts moves first A1_move = monte_carlo_tree_search(root, cp=cp1, N=N1) else: A1_move = monte_carlo_tree_search(root, cp=cp2, N=N2) Game.place(coordinates=A1_move.location, color=A1_color) if Game.is_game_over(): if print_all: Game.print() if first == True: #if method1 win, return True return Game.check_win(A1_color) else: #if method1 win, return True return Game.check_win(A2_color) else: root = Tree(Game, color=A1_color, parent=None, coordinate=A1_move.location) if first == True: A2_move = monte_carlo_tree_search(root, cp=cp2, N=N2) else: A2_move = monte_carlo_tree_search(root, cp=cp1, N=N1) Game.place(coordinates=A2_move.location, color=A2_move.color) root = Tree(Game, color=A2_color, parent=None, coordinate=A2_move.location) if print_all: Game.print() if first == True: #if method1 win or not return Game.check_win(A1_color) else: return Game.check_win(A2_color)
def alphaBetaSearchDijkstra(self, node, depth, alpha, beta, isMaximizer): node['searched'] = True if depth == 0 or node['type'] == 'LEAF': # if depth == 0, then it is already a leaf node node['type'] = 'LEAF' #node['value'] = self.dummyEval() # calculate the value if isMaximizer: node['value'] = self.dijkstraEval(node['board'], 1) else: node['value'] = self.dijkstraEval(node['board'], 2) return node['value'] # If we already get this nodes children then this section we be skipped if len(node['children']) <= 0: # get children of this node children = [] moves = HexBoard.getMoveList(node) # If there is no possible move then this is a leaf node if len(moves) == 0: # Node type is changing!!!! node['type'] = 'LEAF' if isMaximizer: node['value'] = self.dijkstraEval(node['board'], 1) else: node['value'] = self.dijkstraEval(node['board'], 2) return node['value'] else: for move in moves: b = node['board'].copy() if isMaximizer: b[move] = self.MAXIMIZER else: b[move] = self.MINIMIZER child_id = str(uuid.uuid4()) node_type = 'MIN' if node['type'] == 'MIN': node_type = 'MAX' child = { 'id': child_id, 'type': node_type, 'children': [], 'parent_type': node['type'], 'searched': False, 'board': b, 'value': None, 'name': child_id[-3:] } children.append(child) node['children'] = children if isMaximizer: bestVal = -self.INF for n in node['children']: bestVal = max( bestVal, self.alphaBetaSearchDijkstra(n, depth - 1, alpha, beta, False)) alpha = max(alpha, bestVal) # Updating alpha if bestVal >= beta: break # beta cutoff, a>=b else: bestVal = self.INF for n in node['children']: bestVal = min( bestVal, self.alphaBetaSearchDijkstra(n, depth - 1, alpha, beta, True)) beta = min(beta, bestVal) # Updating beta if alpha >= bestVal: break # alpha cutoff, a>=b node['value'] = bestVal return bestVal
from hex_skeleton import HexBoard import copy import numpy as np # global vars _board_size: int = 11 _INF: float = 99999.0 # initialize board with size n _board = HexBoard(_board_size) def _update_board(board: HexBoard, l_move, is_max: bool) -> HexBoard: board = copy.deepcopy( board ) # I think this was the problem with the minimax core, it was using a reference instead of a deep copy color = board.BLUE if is_max else board.RED board.place(l_move, color) return board def dummy_eval() -> float: return np.random.randint(0, 10) def simple_dijkstra(board: HexBoard, source, is_max): Q = set() V_set = board.get_all_vertices() dist = {}