import gradio as gr import random import time class TicTacToeGradio: def __init__(self): self.reset_game() def reset_game(self, vs_computer=False): self.board = [' '] * 9 self.current_player = "X" self.vs_computer = vs_computer self.game_over = False self.winner = None self.players = {"X": "Player 1", "O": "Computer" if vs_computer else "Player 2"} def make_move(self, position): if self.game_over or self.board[position] != ' ': return False self.board[position] = self.current_player return True def computer_move(self): time.sleep(0.8) if self.game_over: return empty_positions = [i for i, val in enumerate(self.board) if val == ' '] if empty_positions: move = random.choice(empty_positions) self.board[move] = "O" def check_winner(self): wins = [(0,1,2),(3,4,5),(6,7,8), (0,3,6),(1,4,7),(2,5,8), (0,4,8),(2,4,6)] for i,j,k in wins: if self.board[i] == self.board[j] == self.board[k] and self.board[i] != ' ': return self.board[i] return None def is_draw(self): return ' ' not in self.board def switch_player(self): self.current_player = "O" if self.current_player == "X" else "X" def get_button_style(self, value): return "❌" if value == "X" else "⭕" if value == "O" else "" def get_status_message(self): if self.winner: emoji = "🔥" if self.winner == "X" else "🤖" if self.vs_computer else "💫" return f"🎉 **{self.players[self.winner]}** wins! {emoji}" elif self.is_draw(): return "🤝 **It's a draw!** Well played!" else: emoji = "🔥" if self.current_player == "X" else "🤖" if self.vs_computer else "💫" return f"🎮 **{self.players[self.current_player]}'s Turn** {emoji}" game = TicTacToeGradio() def handle_click(btn_index): if game.game_over or not game.make_move(btn_index): return update_interface() winner = game.check_winner() if winner: game.winner = winner game.game_over = True return update_interface() if game.is_draw(): game.game_over = True return update_interface() game.switch_player() if game.vs_computer and game.current_player == "O": game.computer_move() winner = game.check_winner() if winner: game.winner = winner game.game_over = True elif game.is_draw(): game.game_over = True else: game.switch_player() return update_interface() def update_interface(): buttons = [ gr.update(value=game.get_button_style(game.board[i]), interactive=(not game.game_over and game.board[i] == ' ')) for i in range(9) ] status = game.get_status_message() return buttons + [status] def start_vs_friend(): game.reset_game(vs_computer=False) return update_interface() def start_vs_computer(): game.reset_game(vs_computer=True) return update_interface() def reset_current_game(): game.reset_game(game.vs_computer) return update_interface() CSS = """ body, .gradio-container { background: #fff0f5 !important; padding: 1rem 1.5rem; box-sizing: border-box; min-height: 100vh; display: flex; justify-content: center; align-items: center; flex-direction: column; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #5b021f; } /* Heading and subtext */ h1 { font-size: clamp(1.8rem, 5vw, 2.5rem); color: #db2777; margin-bottom: 0.2rem; text-align: center; font-weight: 700; user-select: none; } .subtext { font-size: clamp(1rem, 3vw, 1.1rem); color: #9d174d; text-align: center; margin-bottom: 1.5rem; user-select: none; } /* Board container: perfect square, responsive */ #board-container { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); gap: 12px; width: 90vw; max-width: 480px; height: 90vw; max-height: 480px; margin: 0 auto 1.5rem auto; } /* Buttons fill cells fully, keep perfect squares */ .game-btn { width: 100%; height: 100%; font-size: clamp(2rem, 6vw, 3.5rem) !important; border-radius: 12px !important; background: #fff0f5 !important; border: 2px solid #f9a8d4 !important; transition: background 0.2s ease-in-out; padding: 0; display: flex; justify-content: center; align-items: center; user-select: none; } .game-btn:hover { background: #fbcfe8 !important; border-color: #ec4899 !important; } /* Mode Buttons */ .neat-button { padding: 12px 16px; border-radius: 8px; font-size: clamp(0.9rem, 3vw, 1.1rem); margin: 6px 0; background: #fce7f3; border: 1px solid #f472b6; color: #be185d; font-weight: 600; width: 100%; box-sizing: border-box; user-select: none; } .neat-button:hover { background: #f9a8d4; border-color: #ec4899; cursor: pointer; } /* Status */ #status { font-size: clamp(1rem, 4vw, 1.2rem); font-weight: bold; color: #db2777; text-align: center; margin: 1rem 0; user-select: none; } /* Player legend text */ .player-legend { font-size: clamp(0.85rem, 3vw, 1rem); color: #831843; margin-top: 0.5rem; user-select: none; } @media (max-width: 768px) { #board-container { width: 90vw; height: 90vw; max-width: 320px; max-height: 320px; gap: 8px; } .game-btn { font-size: clamp(1.8rem, 7vw, 2.5rem) !important; } .neat-button { font-size: clamp(0.85rem, 4vw, 1rem); } #status { font-size: clamp(0.95rem, 4vw, 1.1rem); } } """ with gr.Blocks(title="Tic Tac Toe", css=CSS) as demo: gr.Markdown("""

💗 TIC TAC TOE

Play solo or with a friend — may the best player win!

""") with gr.Row(): with gr.Column(scale=1, min_width=220): gr.Markdown("### 🎮 Choose Game Mode", elem_id="status") vs_friend_btn = gr.Button("👥 Play vs Friend", elem_classes=["neat-button"]) vs_computer_btn = gr.Button("🤖 Play vs Computer", elem_classes=["neat-button"]) reset_btn = gr.Button("🔄 Reset Game", elem_classes=["neat-button"]) gr.Markdown("""
""") with gr.Column(scale=2, min_width=300): status_display = gr.Markdown("🎮 Select a game mode to begin", elem_id="status") with gr.Column(elem_id="board-container"): btn0 = gr.Button("", elem_classes=["game-btn"]) btn1 = gr.Button("", elem_classes=["game-btn"]) btn2 = gr.Button("", elem_classes=["game-btn"]) btn3 = gr.Button("", elem_classes=["game-btn"]) btn4 = gr.Button("", elem_classes=["game-btn"]) btn5 = gr.Button("", elem_classes=["game-btn"]) btn6 = gr.Button("", elem_classes=["game-btn"]) btn7 = gr.Button("", elem_classes=["game-btn"]) btn8 = gr.Button("", elem_classes=["game-btn"]) buttons = [btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8] all_outputs = buttons + [status_display] for i, btn in enumerate(buttons): btn.click(fn=lambda i=i: handle_click(i), outputs=all_outputs) vs_friend_btn.click(fn=start_vs_friend, outputs=all_outputs) vs_computer_btn.click(fn=start_vs_computer, outputs=all_outputs) reset_btn.click(fn=reset_current_game, outputs=all_outputs) demo.launch()