def minimax_max_node(board, color): """ Return the minimum score the MAX player can achieve with any move starting at board. """ if not get_possible_moves(board, color): return compute_utility(board, color) alpha = -math.inf for i, j in get_possible_moves(board, color): new_board = play_move(board, color, i, j) utility = minimax_min_node(new_board, color) if utility > alpha: alpha = utility return alpha
def alphabeta_max_node(board, color, alpha, beta, limit, caching = 0, ordering = 0): if caching == 1: if board_mapping.get(board) != None: return None, board_mapping.get(board) myself = color return_move = tuple() possible_moves = get_possible_moves(board, myself) if len(possible_moves) == 0 or limit == 0: return None, compute_utility(board, myself) if ordering == 1: unsorted_score = {} sorted_score = {} for m in possible_moves: new_board = play_move(board, myself, m[0], m[1]) unsorted_score.update({m:compute_utility(new_board, myself)}) sorted_score = sorted(unsorted_score.items(), key=lambda i:i[1], reverse=True) possible_moves.clear() for i in sorted_score: possible_moves.append(i[0]) old_score = float('-inf') for move in possible_moves: new_board = play_move(board, myself, move[0], move[1]) if(alpha < beta): score = alphabeta_min_node(new_board, myself, alpha, beta, limit-1, caching, ordering) if score[1] > old_score: old_score = score[1] alpha = old_score return_move = move if caching == 1: board_mapping.update({board : old_score}) return return_move, old_score
def alphabeta_max_node(board, color, beta, level, limit): if not get_possible_moves(board, color): return compute_utility(board, color) alpha = -math.inf for i, j in get_possible_moves(board, color): new_board = play_move(board, color, i, j) if level + 1 > limit: continue utility = alphabeta_min_node(new_board, color, alpha, level + 1, limit) if utility > alpha: alpha = utility if alpha > beta: return alpha return alpha
def minmax_mode(maxi, board, color, limit, caching): use_heuristics = False # default FALSE # CHECK CACHE if (caching and (board, color, maxi) in cache): return cache[board, color, maxi] test_color = color if maxi else next_color(color) # DEPTH LIMIT REACHED if limit == 0: if use_heuristics: return None, compute_heuristic(board, color) else: return None, compute_utility(board, color) moves = get_possible_moves(board, test_color) # TERMINAL STATE if len(moves) == 0: move_tup = (None, compute_utility(board, color)) if (caching): cache[board, color, maxi] = move_tup return move_tup move_tups = [] for move in moves: next_board = play_move(board, test_color, move[0], move[1]) if maxi: move_util = minimax_min_node(next_board, color, limit - 1, caching)[1] else: move_util = minimax_max_node(next_board, color, limit - 1, caching)[1] move_tups.append((move, move_util)) if maxi: goal_tup = max(move_tups, key=lambda t: t[1]) else: goal_tup = min(move_tups, key=lambda t: t[1]) if (caching): cache[board, color, maxi] = goal_tup return goal_tup
def minimax_max_node(board, color, limit, caching=0): #returns highest possible utility # Determine the player color if color == 1: oppo_color = 2 else: oppo_color = 1 # the valid moved valid_moves = get_possible_moves(board, color) if (len(valid_moves) == 0): # no move valid and board is not cached return None, compute_utility(board, color) elif (limit == 0): return None, compute_utility(board, color) else: u_value_best = -1 * len(board) * len( board) # set at max possible value move_best = None for move in valid_moves: new_board = play_move(board, color, move[0], move[1]) # check if the new board is in the cache if new_board in cached and caching: new_move, u_value = cached[new_board] else: new_move, u_value = minimax_max_node(new_board, color, limit - 1, caching) if (caching): cached[new_board] = (new_move, u_value) # get the min value: if (u_value > u_value_best): u_value_best = u_value move_best = move return (move_best, u_value_best) return ((0, 0), 0)
def alphabeta_max_node(board, color, alpha, beta, depth, maxdepth): if depth > maxdepth or len(get_possible_moves(board, color)) <= 0: if hash(board) not in seen_boards: seen_boards.add(hash(board)) seen_boards_cost[hash(board)] = compute_utility(board, color) return seen_boards_cost[hash(board)] else: nowAlpha = alpha bestVal = -3000000000 bestCheck = [] for x in get_possible_moves(board, color): playedBoard = play_move(board, color, x[0], x[1]) if playedBoard not in seen_boards_heuristic: seen_boards_heuristic.add(playedBoard) seen_boards_heuristic_cost[playedBoard] = get_heuristic( playedBoard, color) #toCheck = alphabeta_max_node(playedBoard, color, switch_color(currentColor), alpha, nowBeta, depth + 1, maxdepth ) bestCheck.append( (seen_boards_heuristic_cost[playedBoard], playedBoard)) bestCheck.sort() bestCheck.reverse() for x, playedBoard in bestCheck: #playedBoard = play_move(board, currentColor, x[0], x[1]) #toCheck = alphabeta_min_node(playedBoard, color, switch_color(currentColor), nowAlpha, beta, depth + 1, maxdepth ) toCheck = 0 if playedBoard not in seen_boards_min: seen_boards_min.add(playedBoard) seen_boards_min_cost[playedBoard] = alphabeta_min_node( playedBoard, color, nowAlpha, beta, depth + 1, maxdepth) toCheck = seen_boards_min_cost[playedBoard] toCheck += random.randint(-volatile_level, volatile_level) if toCheck > bestVal: bestVal = toCheck if bestVal > nowAlpha: nowAlpha = bestVal if nowAlpha >= beta: break #return max(childrenNodes) return bestVal
def minimax_min_node(board, color): #function MIN-VALUE(state) returns a utility value #if TERMINAL-TEST(state) then return UTILITY(state) vāā #for each a in ACTIONS(state) do #v ā MIN(v, MAX-VALUE(RESULT(s, a))) return v moves = get_possible_moves(board, color) move = min(moves) #(col,row) tuples representing legal moves for player color results = play_move(board, color, move) #computes successor board state that results from player color playing move (a(col,row)tuple) if color.status == "FINAL": return color.compute_utility(board, color) v = inf for (a, s) in color.results(board): v = min(v, minimax_max_node(board, color)) return v
def select_move_alphabeta(board, color): moves = get_possible_moves(board, color) max_score = float("-inf") best_move = [0, 0] alpha = float("-inf") beta = float("inf") for move in moves: new_board = play_move(board, color, move[0], move[1]) score = alphabeta_min_node(new_board, color, alpha, beta, 1, limit) if score > max_score: max_score = score best_move[0] = move[0] best_move[1] = move[1] alpha = max(alpha, max_score) return best_move[0], best_move[1]
def select_move_alphabeta(board, color): max_utility = float("-inf") best_move = None for move in get_possible_moves(board, color): i,j = move new_board = play_move(board, color, i,j) utility = alphabeta_min_node(new_board, color, 0, float("-inf"), float("inf"), 24) # select the move that gives the highest utility if utility > max_utility: max_utility = utility best_move = (i, j) return best_move
def minimax_max_node(board, color): if board in move_dictionary: return move_dictionary[board] else: max_next_nodes = get_possible_moves(board, color) utility_list = [] if (len(max_next_nodes) == 0): return compute_utility(board, color) for items in max_next_nodes: successor_board = play_move(board, color, items[0], items[1]) curr_utility = minimax_min_node(successor_board, color) utility_list.append(curr_utility) move_dictionary.update({board: min(utility_list)}) return max(utility_list)
def ordered_moves(board, moves, color, current_player): '''This is for ordering possible moves''' dic = dict() result = [] for move in moves: (column, row) = move new_board = play_move(board, current_player, column, row) successor_utility = compute_utility(new_board, color) if successor_utility in dic and dic[successor_utility] != [move]: dic[successor_utility].append(move) else: dic[successor_utility] = [move] ordered_utility = sorted(list(dic.keys()), reverse=True) for utility in ordered_utility: result += dic[utility] return result
def minimax_min_node(board, color): anti_color = 3 - color moves = get_possible_moves(board, anti_color) if not moves: return compute_utility(board, color) mini_score = float("inf") for move in moves: new_board = play_move(board, anti_color, move[0], move[1]) score = minimax_max_node(new_board, color) if score < mini_score: mini_score = score return mini_score
def minimax_min_node(board, color, depth, end_time): possible_moves = get_possible_moves(board, color) current_time = datetime.now() if possible_moves == [] or depth == 0 or current_time >= end_time: score = get_score_weighted(board) # get_score(board) return score[color%2] - score[(color+1)%2] else: next_color = color % 2 + 1 best_min_score = 1000000 for move in possible_moves: new_board = play_move(board, color, move[0], move[1]) move_score = minimax_max_node(new_board, next_color, depth - 1, end_time) if move_score < best_min_score: best_min_score = move_score return best_min_score return None
def select_move_alphabeta(board, color): moves = get_possible_moves(board, color) best_move = None alpha = -math.inf for move in moves: if move == (0, 0) or move == (0, 7) or move == (7, 0) or move == (7, 7): return move i, j = move new_board = play_move(board, color, i, j) score = alphabeta_min_node(new_board, color, alpha, 0, 4) if score > alpha: best_move = move alpha = score return best_move
def alphabeta_min_node(board, color, alpha, beta, limit, caching = 0, ordering = 0): if caching: global cache if board in cache.keys(): return cache[(board,color)] opponent_col = None if color == 1: opponent_col = 2 else: opponent_col = 1 list_of_moves = get_possible_moves(board, opponent_col) if list_of_moves == [] or limit == 0: return (None, compute_utility(board,color)) children = [] for move in list_of_moves: children.append(play_move(board, opponent_col, move[0], move[1])) if ordering: utilities = [] for c in children: utilities.append(compute_utility(c, color)) zipped = list(zip(utilities, children, list_of_moves)) zipped.sort(key = lambda x: x[0]) children = [] list_of_moves = [] for x, y, z in zipped: children.append(y) list_of_moves.append(z) ut_val = float("inf") values = [] for c in children: ut_val = min(ut_val, alphabeta_max_node(c,color, alpha, beta, limit-1)[1]) values.append(ut_val) beta = min(beta, ut_val) if beta <= alpha: return (None, ut_val) move = list_of_moves[values.index(ut_val)] if caching: cache[(board,color)] = (move, ut_val) return (move, ut_val)
def alphabeta_max_node(board, color, alpha, beta, limit, caching=0, ordering=0): moves = get_possible_moves(board, color) if moves == [] or limit == 0: return (-1, -1), compute_utility(board, color) max_utility = -float('inf') successors = [] for i in range(len(moves)): temp_board = play_move(board, color, moves[i][0], moves[i][1]) successors.append((temp_board, compute_utility(temp_board, color))) if ordering == 1: successors.sort(key=takeSecond, reverse=True) for i in range(len(successors)): if caching == 1: if successors[i][0] in cached: temp_utility = cached[successors[i][0]] else: temp_move, temp_utility = alphabeta_min_node( successors[i][0], color, alpha, beta, limit - 1, caching, ordering) cached[successors[i][0]] = temp_utility else: temp_move, temp_utility = alphabeta_min_node( successors[i][0], color, alpha, beta, limit - 1, caching, ordering) if temp_utility > max_utility: max_utility = temp_utility max_move = moves[i] if alpha < max_utility: alpha = max_utility if beta <= alpha: break return max_move, max_utility
def alphabeta_max_node(board, color, alpha, beta, limit, caching=0, ordering=0): #IMPLEMENT (and replace the line below) # Check cache if caching and (board, color) in cache: return cache[(board, color)] best_move = None max_utility = float('-inf') possible_moves = get_possible_moves(board, color) # Check if limit reached or end of game if limit == 0 or len(possible_moves) == 0: return None, compute_utility(board, color) # Order (move, board) tuples according to the utility successor states move_and_board = [] for move in possible_moves: nxt_board = play_move(board, color, move[0], move[1]) move_and_board.append((move, nxt_board)) if ordering: move_and_board.sort(key=lambda move_and_board: compute_utility( move_and_board[1], color), reverse=True) for (move, nxt_board) in move_and_board: # Compute next utility _, nxt_utility = alphabeta_min_node(nxt_board, color, alpha, beta, limit - 1, caching, ordering) if max_utility < nxt_utility: best_move = move max_utility = nxt_utility # Prune alpha = max(alpha, max_utility) if alpha >= beta: break # Cache the board if caching: cache[(board, color)] = (best_move, max_utility) return best_move, max_utility
def minimax_min_node(board, color): if color == 2: opp_color = 1 elif color == 1: opp_color = 2 if not get_possible_moves(board, opp_color): return compute_utility(board, color) beta = math.inf for i, j in get_possible_moves(board, opp_color): new_board = play_move(board, opp_color, i, j) utility = minimax_max_node(new_board, color) if utility < beta: beta = utility return beta
def minimax_max_node(board, color): possibleMoves = get_possible_moves(board, color) if not possibleMoves: return compute_utility(board, color) bestMove = None bestScore = -math.inf for y in possibleMoves: newBoard = play_move(board, color, y[0], y[1]) score = minimax_min_node(newBoard, oppColor) if score > bestScore: bestMove = move bestScore = score return bestScore
def minimax_max_node(board, color): if board in minimax_cache.keys(): return minimax_cache[board] opponent_color = 0 if color == 1: opponent_color = 2 else: opponent_color = 1 possible_moves = get_possible_moves(board, color) if not possible_moves: return compute_utility(board, color) v = float("-inf") for move in possible_moves: new_board = play_move(board, color, move[0], move[1]) v = max(v, minimax_min_node(new_board, opponent_color)) minimax_cache[new_board] = v return v
def select_move_minimax(board, color): """ Given a board and a player color, decide on a move. The return value is a tuple of integers (i,j), where i is the column and j is the row on the board. """ moves = get_possible_moves(board, color) bestMoveScore = -math.inf bestMove = (1, 1) for i in moves: newBoard = play_move(board, color, i[0], i[1]) score = minimax_min_node(newBoard, color) if score > bestMoveScore: bestMoveScore = score bestMove = i return bestMove
def select_move_minimax(board, color): """ Given a board and a player color, decide on a move. The return value is a tuple of integers (i,j), where i is the column and j is the row on the board. """ best_value = 0 for (i, j) in get_possible_moves(board, color): next_board = play_move(board, color, i, j) value = minimax_min_node(next_board, color) if value > best_value: best_value = value x = i y = j return x, y
def move_mobility_heuristic(board,color): ''' A secondary heuristic to maximize the number of plays the AI can do. Intended to synergize with sweet 16. ''' if color == 1: opposing_color = 2 else: opposing_color = 1 opposing_possible_moves = 0 moves = get_possible_moves(board,color) for (column,row) in moves: new_board = play_move(board,color,column,row) if len(get_possible_moves(new_board,opposing_color)) > opposing_possible_moves: opposing_possible_moves = len(get_possible_moves(new_board,opposing_color)) #sys.stderr.write('Move mobility: {}\n'.format(len(moves) - opposing_possible_moves)) return len(moves) - opposing_possible_moves + random.randint(0,9)
def select_move_alphabeta(board, color): dic = {} if color == 1: opponent_color = 2 else: opponent_color = 1 v = -inf #v = alphabeta_max_node(board, color, -inf, inf) for successor in get_possible_moves(board, color): new_board = play_move(board, color, successor[0], successor[1]) v = max(v, alphabeta_min_node(new_board, opponent_color, -inf, inf, 1, 5)) if v not in dic: dic.update({v: successor}) return dic[v]
def minimax_max_node(board, color, limit, caching = 0): #returns highest possible utility #IMPLEMENT (and replace the line belowS global cache if color==1: other_color=2 else: other_color=1 limit-=1 if caching==1: if board in cache: return cache[board] #iniitlazing a very low value for the node best_node = float("-Inf") #checking if we are at the end leaf if not get_possible_moves(board,color) or limit==0: return compute_utility(board,color) for move in get_possible_moves(board,color): best_node= max(best_node,minimax_min_node(play_move(board,color,move[0],move[1]),opponent,lim)) return best_node
def alphabeta_max_node(board, color, beta, level, limit): level += 1 if level >= limit: return heuristic_evaluation(board, color) opp_color = 1 if color == 2 else 2 moves = get_possible_moves(board, color) if not moves: return compute_utility(board, color) alpha = -math.inf for x in moves: one, two = x testBoard = play_move(board, color, one, two) minNode = alphabeta_min_node(testBoard, opp_color, alpha, level, limit) if minNode >= beta: return minNode alpha = max(alpha, minNode) return alpha
def minimax_min_node(board, color, depth, temp_max_depth=max_depth): if depth >= max_depth: return compute_utility(board, color) moves = get_possible_moves(board, color) move_utility = {} if len(moves) != 0: for (column, row) in moves: new_board = play_move(board, color, column, row) #sys.stderr.write(str(new_board) + '\n') if not new_board in move_utility.values(): utility = minimax_max_node(new_board, color, depth + 1) move_utility[utility] = new_board else: return compute_utility(new_board, color) return min(move_utility.keys()) return compute_utility(board, color)
def minimax_min_node(board, color, limit, caching=0): # Determine the player color if color == 1: oppo_color = 2 else: oppo_color = 1 # the valid moved valid_moves = get_possible_moves(board, oppo_color) if (len(valid_moves) == 0): # no move valid and board is not cached return None, compute_utility(board, color) elif (limit == 0): # reach search limit return None, compute_utility(board, color) else: u_value_best = float("Inf") # inf as looking for small number move_best = None for move in valid_moves: new_board = play_move(board, oppo_color, move[0], move[1]) # check if the new board is in the cache if new_board in cached and caching: new_move, u_value = cached[new_board] else: new_move, u_value = minimax_max_node(new_board, color, limit - 1, caching) if (caching): cached[new_board] = (new_move, u_value) # get the min value: if (u_value < u_value_best): u_value_best = u_value move_best = move return (move_best, u_value_best) return ((0, 0), 0)
def minimax_min_node(board, color): # Get the color of the next player if color == 1: other_color = 2 else: other_color = 1 # Get the allowed moves all_moves = get_possible_moves(board, other_color) # If there are no moves left, return the utility if len(all_moves) == 0: return None, compute_utility(board, color) # Else if there are moves, get their utility and return the min else: # Get the maximum utility possible to use as a starting point for min min_utility = len(board) * len(board) min_move = None # For each possible move, get the max utiltiy for each in all_moves: # Get the next board from that move next_board = play_move(board, other_color, each[0], each[1]) # First check the cache for the board if next_board in board_utilities: move, new_utiltiy = board_utilities[next_board] else: # If the new utility is less than the current min, update min_utiltiy move, new_utiltiy = minimax_max_node(next_board, color) board_utilities[next_board] = (move, new_utiltiy) if new_utiltiy < min_utility: min_utility = new_utiltiy min_move = each # After checking every move, return the minimum utility return min_move, min_utility # Default return - should never be called return None, None
def select_move_alphabeta(board, color): best_utility = -math.inf beta = math.inf best_move = 0, 0 new_color = 1 if color == 2 else 2 # caching_states = get_cached_states(color) possible_moves = get_possible_moves(board, color) level = 0 limit = 5 if len(possible_moves) > 0: sorted_states_list = [] for move in possible_moves: new_board = play_move(board, color, move[0], move[1]) sort_utility = compute_utility(new_board, new_color) heappush(sorted_states_list, (sort_utility, new_board, move)) sorted_states = [x[1] for x in sorted_states_list] moves = [x[2] for x in sorted_states_list] index = 0 for board_state in sorted_states: if board_state in caching_states: utility = caching_states[board_state] else: utility = alphabeta_min_node(board_state, new_color, best_utility, beta, level + 1, limit) caching_states[board_state] = utility if utility > best_utility: best_utility = utility best_move = moves[index] index += 1 """ for move in possible_moves: new_board = play_move(board, color, move[0], move[1]) if new_board in caching_states: utility = caching_states[new_board] else: utility = alphabeta_min_node(new_board, new_color, best_utility, beta, level + 1, limit) caching_states[new_board] = utility if utility > best_utility: best_utility = utility best_move = move """ return best_move