Apr-27-2023, 10:51 PM
(This post was last modified: Apr-27-2023, 10:52 PM by freethrownucleus.)
I want to change this 2-player game into a game against computer. I tried to implement this minimax algorithm that's in the first class in my code but I can't make it work. I tried a couple of options but I can't handle it.
I would appreciate some help!
Here's the old code version without my silly tries (XD) and class Robot with the minimax function that I'm trying to implement and adapt in the beginning of the code:
I would appreciate some help!
Here's the old code version without my silly tries (XD) and class Robot with the minimax function that I'm trying to implement and adapt in the beginning of the code:
import tkinter as tk #TRYING TO IMPLEMENT CLASS ROBOT #START class Robot: def __init__(self, cube): self.boards = cube.boards def get_scores(self, board): """Get scores for all open squares on a board""" def minimax(mark, square, alpha=-1000, beta=1000, depth=0): """minimax algorithm with alpha/beta pruning""" # Place the mark and check for a win square.mark = mark if square.check_win(): # Give extra weight to earlier wins/losses score = 10 - depth if mark is ROBOT else depth - 10 board.depth = min(board.depth, depth) elif len(empty_squares := board.empty_squares()) == 0: # No plays left. Draw. score = 0 elif mark is PLAYER: # Pick best move for robot score = -1000 for s in empty_squares: score = max(score, minimax(ROBOT, s, alpha, beta, depth+1)) alpha = max(alpha, score) if alpha > beta: break else: # Guess what move player will make score = 1000 for s in empty_squares: score = min(score, minimax(PLAYER, s, alpha, beta, depth+1)) beta = min(beta, score) if alpha > beta: break # Remove mark and return score for the square square.mark = EMPTY return score # Collect scores for empty squares. If board is empty, # minimax will return 0 for all squares board.depth = 10 empty_squares = board.empty_squares() if len(empty_squares) == 9: board.scores = [[0, s] for s in empty_squares] else: # Calling minimax twice. The first is to find the square # giving us best chance to win board. Second is to prevent # abandoning board early and giving player an easy win. board.scores = [[minimax("O", s), s] for s in empty_squares] [minimax("X", s, depth=1) for s in empty_squares] def play(self): """Place robot mark.""" # Get scores for all empty squares depth = 10 for board in self.boards: self.get_scores(board) depth = min(depth, board.depth) # Select board with minimum depth. This is the board that # will win/lose in the least number of moves. scores = [] for board in self.boards: if board.depth <= depth: scores.extend(board.scores) # Randomly select from best scores on the selected board. max_score = max(score[0] for score in scores) squares = [score[1] for score in scores if score[0] >= max_score] return random.choice(squares) #END class Square(tk.Button): """Special button for playing tic-tac-toe.""" colors = {"O": "blue", "X": "red"} def __init__(self, parent, index): super().__init__(parent, width=3, font=('Comic Sans MS', 20, 'bold')) self.index = index self._mark = None @property def mark(self): """What marker appears in square.""" return self._mark @mark.setter def mark(self, mark): self._mark = mark self["fg"] = self.colors.get(mark, 'black') self["text"] = '' if mark is None else mark class TicTacToe(tk.Tk): """Play tic-tac-toe. Players take turn clicking on empty buttons to place their marker. The first player to get three markers in a row, up/down, sideways or diagonally, wins """ # All possible winning conbinations wins = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (9, 10, 11), (12, 13, 14), (15, 16, 17), (9, 12, 15), (10, 13, 16), (11, 14, 17), (18, 19, 20), (21, 22, 23), (24, 25, 26), (18, 21, 24), (19, 22, 25), (20, 23, 26), (27, 28, 29), (30, 31, 32), (33, 34, 35), (27, 30, 33), (28, 31, 34), (29, 32, 35), (36, 37, 38), (39, 40, 41), (42, 43, 44), (36, 39, 42), (37, 40, 43), (38, 41, 44), (45, 46, 47), (48, 49, 50), (51, 52, 53), (45, 48, 51), (46, 49, 52), (47, 50, 53), (3, 6, 36, 39), (4, 7, 37, 40), (5, 8, 38, 41), (18, 21, 39, 42), (19, 22, 40, 43), (20, 23, 41, 44), (28, 29, 36, 37), (31, 32, 39, 40), (34, 35, 42, 43), (37, 38, 45, 46), (40, 41, 48, 49), (43, 44, 51, 52), (9, 12, 21, 24), (10, 13, 22, 25), (11, 14, 23, 26), (0, 3, 12, 15), (1, 4, 13, 16), (2, 5, 14, 17), (27, 28, 46, 47), (30, 31, 49, 50), (33, 34, 52, 53), (0, 1, 27, 30), (3, 4, 28, 31), (6, 7, 29, 32), (1, 2, 47, 50), (4, 5, 46, 49), (7, 8, 45, 48), (18, 19, 32, 35), (21, 22, 31, 34), (24, 25, 30, 33), (19, 20, 48, 51), (22, 23, 49, 52), (25, 26, 50, 53), (15, 16, 27, 28), (12, 13, 30, 31), (9, 10, 33, 34), (16, 17, 46, 47), (13, 14, 49, 50), (10, 11, 52, 53), (6, 37, 41, 51), (7, 38, 48, 52), (7, 32, 34, 36), (8, 35, 37, 39), (19, 28, 32, 42), (20, 29, 39, 43), (18, 41, 43, 45), (19, 44, 46, 48), (19, 23, 32, 42), (19, 21, 44, 48)) #last one example of a diagonal combo over 2 boards def __init__(self): super().__init__() self.title('Tic Tac Toe') # create a frame to hold all four boards self.boards_frame = tk.Frame(self) self.boards_frame.pack(side="top", padx=5, pady=5) # create the left board self.frame1 = tk.Frame(self.boards_frame) self.frame1.pack(side="left") self.squares1 = [] for i in range(9): self.squares1.append(Square(self.frame1, i)) for square in self.squares1: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares1)) self.frame1.pack(side="top") self.frame2 = tk.Frame(self.boards_frame) self.frame2.pack(side="left") for i in range(9, 18): self.squares1.append(Square(self.frame2, i)) for square in self.squares1: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares1)) self.frame3 = tk.Frame(self.boards_frame) self.frame3.pack(side="left") for i in range(18, 27): self.squares1.append(Square(self.frame3, i)) for square in self.squares1: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares1)) self.frame4 = tk.Frame(self.boards_frame) self.frame4.pack(side="left") for i in range(27, 36): self.squares1.append(Square(self.frame4, i)) for square in self.squares1: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares1)) self.frame5 = tk.Frame(self.boards_frame) self.frame5.pack(side="left") for i in range(36, 45): self.squares1.append(Square(self.frame5, i)) for square in self.squares1: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares1)) self.frame6 = tk.Frame(self.boards_frame) self.frame6.pack(side="left") for i in range(45, 54): self.squares1.append(Square(self.frame6, i)) for square in self.squares1: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares1)) self.frame2.pack(side="bottom") self.frame3.pack(side="bottom") ''' # create the middle-left board self.frame2 = tk.Frame(self.boards_frame) self.frame2.pack(side="left") self.squares2 = [Square(self.frame2, i) for i in range(9)] for square in self.squares2: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares2, self.squares1)) # create the middle-right board self.frame3 = tk.Frame(self.boards_frame) self.frame3.pack(side="left") self.squares3 = [Square(self.frame3, i) for i in range(9)] for square in self.squares3: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares3, self.squares1, self.squares2, self.squares4, self.squares5, self.squares6)) self.frame4 = tk.Frame(self.boards_frame) self.frame4.pack(side="left") self.squares4 = [Square(self.frame4, i) for i in range(9)] for square in self.squares4: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares4, self.squares1, self.squares2, self.squares3, self.squares5, self.squares6)) self.frame5 = tk.Frame(self.boards_frame) self.frame5.pack(side="left") self.squares5 = [Square(self.frame5, i) for i in range(9)] for square in self.squares5: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares5, self.squares1, self.squares2, self.squares3, self.squares4, self.squares6)) self.frame1.pack(side="bottom") self.frame2.pack(side="top") self.frame6 = tk.Frame(self.boards_frame) self.frame6.pack(side="left") self.squares6 = [Square(self.frame6, i) for i in range(9)] for square in self.squares6: row = square.index // 3 column = square.index % 3 square.grid(row=row, column=column, padx=5, pady=5) square.configure(command=lambda arg=square: self.play(arg, self.squares6, self.squares1, self.squares2, self.squares3, self.squares4, self.squares5)) self.frame3.pack(side="bottom") ''' self.message = tk.Label(self, text=' ', width=30) self.message.pack(side="bottom") self.new_game = False self.player = "X" self.reset(self.squares1) def reset(self, squares1): """Reset board to empty""" self.open_squares = 54 self.new_game = False for square in squares1: square.mark = None ''' for square in squares2: square.mark = None for square in squares3: square.mark = None for square in squares4: square.mark = None for square in squares5: square.mark = None for square in squares6: square.mark = None ''' self.message['text'] = f"{self.player}'s turn" def play(self, square, squares1): """Put player marker in slot if open. Check for win or tie""" if self.new_game: return if square.mark is not None: self.message['text'] = "Invalid move" return square.mark = self.player self.open_squares -= 1 # check for a win for win in self.wins: if all(squares1[i].mark == self.player for i in win): self.message['text'] = f"{self.player} wins!" self.new_game = True return ''' if all(squares2[i].mark == self.player for i in win): self.message['text'] = f"{self.player} wins!" self.new_game = True return if all(squares3[i].mark == self.player for i in win): self.message['text'] = f"{self.player} wins!" self.new_game = True return if all(squares4[i].mark == self.player for i in win): self.message['text'] = f"{self.player} wins!" self.new_game = True return if all(squares5[i].mark == self.player for i in win): self.message['text'] = f"{self.player} wins!" self.new_game = True return if all(squares6[i].mark == self.player for i in win): self.message['text'] = f"{self.player} wins!" self.new_game = True return ''' # check for a tie if self.open_squares == 0: self.message['text'] = "Tie game" self.new_game = True return # switch players self.player = "O" if self.player == "X" else "X" self.message['text'] = f"{self.player}'s turn" def game_over(self, squares1): """Check for winner or tie""" # Check all winning combinations for win in self.wins: if all(squares1[i].mark == self.player for i in win): for i in win: squares1[i]["fg"] = "green" self.message["text"] = f"{self.player} wins!" return True ''' if all(squares2[i].mark == self.player for i in win): for i in win: squares2[i]["fg"] = "green" self.message["text"] = f"{self.player} wins!" return True if all(squares3[i].mark == self.player for i in win): for i in win: squares3[i]["fg"] = "green" self.message["text"] = f"{self.player} wins!" return True if all(squares4[i].mark == self.player for i in win): for i in win: squares4[i]["fg"] = "green" self.message["text"] = f"{self.player} wins!" return True if all(squares5[i].mark == self.player for i in win): for i in win: squares5[i]["fg"] = "green" self.message["text"] = f"{self.player} wins!" return True if all(squares6[i].mark == self.player for i in win): for i in win: squares6[i]["fg"] = "green" self.message["text"] = f"{self.player} wins!" return True ''' # Check for a tie if self.open_squares <= 0: self.message["text"] = "Game ends in a tie" return True return False TicTacToe().mainloop()