Untitled

Run Settings
LanguagePython
Language Version
Run Command
#!/usr/bin/env python3 """ GUI Tic‑Tac‑Toe (Tkinter) Features -------- * Human‑vs‑Human, Human‑vs‑AI (Easy), Human‑vs‑AI (Hard) * Switch difficulty on the fly * Reset / New Game button * Simple, well‑commented code – easy to extend (e.g., add sound, animations) Author : Lumo (Proton) """ import random import tkinter as tk from tkinter import messagebox # ---------------------------------------------------------------------- # Board logic (independent of the UI) # ---------------------------------------------------------------------- WIN_PATTERNS = ( (0, 1, 2), (3, 4, 5), (6, 7, 8), # rows (0, 3, 6), (1, 4, 7), (2, 5, 8), # cols (0, 4, 8), (2, 4, 6), # diagonals ) def check_winner(board): """Return 'X', 'O' if there is a winner, otherwise None.""" for a, b, c in WIN_PATTERNS: if board[a] == board[b] == board[c] != " ": return board[a] return None def is_draw(board): return " " not in board and check_winner(board) is None def available_moves(board): return [i for i, v in enumerate(board) if v == " "] # --------------------------- AI --------------------------------------- def ai_random(board, player): """Easy AI – pick a random empty square.""" move = random.choice(available_moves(board)) board[move] = player return move def minimax(board, depth, is_maximizing, ai_player, human_player): """Classic minimax returning (score, move).""" winner = check_winner(board) if winner == ai_player: return 10 - depth, None if winner == human_player: return depth - 10, None if is_draw(board): return 0, None if is_maximizing: best_score = -float("inf") best_move = None for m in available_moves(board): board[m] = ai_player score, _ = minimax(board, depth + 1, False, ai_player, human_player) board[m] = " " if score > best_score: best_score, best_move = score, m return best_score, best_move else: best_score = float("inf") best_move = None for m in available_moves(board): board[m] = human_player score, _ = minimax(board, depth + 1, True, ai_player, human_player) board[m] = " " if score < best_score: best_score, best_move = score, m return best_score, best_move def ai_minimax(board, player): """Hard AI – perfect play.""" _, move = minimax(board, 0, True, player, "O" if player == "X" else "X") board[move] = player return move # ---------------------------------------------------------------------- # Tkinter GUI # ---------------------------------------------------------------------- class TicTacToeGUI(tk.Tk): def __init__(self): super().__init__() self.title("Tic‑Tac‑Toe") self.resizable(False, False) # ---------- Game state ---------- self.board = [" "] * 9 self.current_player = "X" # X always starts self.game_mode = tk.StringVar(value="HvH") # HvH, HvE, HvHARD self.difficulty = "easy" # easy / hard (used only for AI modes) # ---------- UI elements ---------- self.create_widgets() self.update_status() # ------------------------------------------------------------------ # UI construction # ------------------------------------------------------------------ def create_widgets(self): # Top frame – mode selector top = tk.Frame(self) top.pack(pady=5) tk.Label(top, text="Mode:").pack(side=tk.LEFT) modes = [("Human vs Human", "HvH"), ("Human vs AI (Easy)", "HvE"), ("Human vs AI (Hard)", "HvHARD")] for txt, val in modes: rb = tk.Radiobutton( top, text=txt, variable=self.game_mode, value=val, command=self.reset_game) rb.pack(side=tk.LEFT, padx=4) # Board – 3×3 grid of buttons self.buttons = [] board_frame = tk.Frame(self) board_frame.pack(padx=10, pady=10) for i in range(9): btn = tk.Button( board_frame, text=" ", font=("Helvetica", 32), width=3, height=1, command=lambda idx=i: self.on_square_click(idx)) btn.grid(row=i // 3, column=i % 3, padx=2, pady=2) btn.bind("<Enter>", lambda e, b=btn: b.config(bg="#e0e0e0")) btn.bind("<Leave>", lambda e, b=btn: b.config(bg="SystemButtonFace")) self.buttons.append(btn) # Bottom frame – status, reset, difficulty switch bottom = tk.Frame(self) bottom.pack(pady=5) self.status_lbl = tk.Label(bottom, text="", font=("Helvetica", 12)) self.status_lbl.pack(side=tk.LEFT, padx=10) self.switch_btn = tk.Button( bottom, text="Switch Difficulty", command=self.toggle_difficulty) self.switch_btn.pack(side=tk.RIGHT, padx=5) reset_btn = tk.Button(bottom, text="New Game", command=self.reset_game) reset_btn.pack(side=tk.RIGHT, padx=5) # ------------------------------------------------------------------ # Game flow helpers # ------------------------------------------------------------------ def reset_game(self): """Clear board, set starting player, update UI.""" self.board = [" "] * 9 self.current_player = "X" for b in self.buttons: b.config(text=" ", state=tk.NORMAL) self.update_status() # If AI goes first (only possible in Human‑vs‑AI modes), # make the first AI move automatically. if self.game_mode.get() != "HvH" and self.current_player == "O": self.after(200, self.ai_move) def toggle_difficulty(self): """Flip between easy and hard AI (only matters for AI modes).""" self.difficulty = "hard" if self.difficulty == "easy" else "easy" messagebox.showinfo( "Difficulty switched", f"AI difficulty is now **{self.difficulty.upper()}**.", parent=self) def update_status(self): """Refresh the status line.""" if self.game_mode.get() == "HvH": txt = f"Turn: Player {self.current_player}" else: if self.current_player == "X": txt = "Your turn (X)" else: txt = f"Computer's turn ({self.current_player})" self.status_lbl.config(text=txt) # ------------------------------------------------------------------ # Event handlers # ------------------------------------------------------------------ def on_square_click(self, idx): """Human clicked a square.""" if self.board[idx] != " ": return # ignore already‑filled squares (shouldn't happen) # Place the mark self.board[idx] = self.current_player self.buttons[idx].config(text=self.current_player, state=tk.DISABLED) # Check for end‑game winner = check_winner(self.board) if winner: self.end_game(f"Player {winner} wins!") return if is_draw(self.board): self.end_game("It's a draw!") return # Switch player self.current_player = "O" if self.current_player == "X" else "X" self.update_status() # If it's now the AI's turn, schedule its move if self.game_mode.get() != "HvH" and self.current_player == "O": self.after(300, self.ai_move) # slight delay for UX def ai_move(self): """Perform an AI move according to the selected difficulty.""" if self.difficulty == "easy": move = ai_random(self.board, "O") else: move = ai_minimax(self.board, "O") # Update UI self.buttons[move].config(text="O", state=tk.DISABLED) # End‑game check winner = check_winner(self.board) if winner: self.end_game(f"Player {winner} wins!") return if is_draw(self.board): self.end_game("It's a draw!") return # Back to human self.current_player = "X" self.update_status() def end_game(self, message): """Disable all squares and show result.""" for b in self.buttons: b.config(state=tk.DISABLED) self.status_lbl.config(text=message) messagebox.showinfo("Game Over", message, parent=self) # ---------------------------------------------------------------------- # Run the application # ---------------------------------------------------------------------- if __name__ == "__main__": app = TicTacToeGUI() app.mainloop()
Editor Settings
Theme
Key bindings
Full width
Lines