Spaces:
Sleeping
Sleeping
import gradio as gr | |
import numpy as np | |
from typing import List, Tuple, Optional | |
class TicTacToe: | |
def __init__(self): | |
self.board = np.zeros((3, 3)) | |
self.human_player = 1 | |
self.ai_player = -1 | |
def reset_board(self): | |
self.board = np.zeros((3, 3)) | |
return self.format_board() | |
def check_winner(self) -> Optional[int]: | |
# Check rows, columns and diagonals | |
for player in [self.human_player, self.ai_player]: | |
# Rows and columns | |
for i in range(3): | |
if all(self.board[i, :] == player) or all(self.board[:, i] == player): | |
return player | |
# Diagonals | |
if all(np.diag(self.board) == player) or all(np.diag(np.fliplr(self.board)) == player): | |
return player | |
# Check for draw | |
if np.all(self.board != 0): | |
return 0 | |
return None | |
def get_valid_moves(self) -> List[Tuple[int, int]]: | |
return [(i, j) for i in range(3) for j in range(3) if self.board[i, j] == 0] | |
def minimax(self, depth: int, is_maximizing: bool, alpha: float = float('-inf'), beta: float = float('inf')) -> Tuple[int, Optional[Tuple[int, int]]]: | |
winner = self.check_winner() | |
if winner is not None: | |
return winner * 100, None | |
if is_maximizing: | |
best_score = float('-inf') | |
best_move = None | |
for move in self.get_valid_moves(): | |
self.board[move] = self.ai_player | |
score, _ = self.minimax(depth + 1, False, alpha, beta) | |
self.board[move] = 0 | |
if score > best_score: | |
best_score = score | |
best_move = move | |
alpha = max(alpha, best_score) | |
if beta <= alpha: | |
break | |
return best_score, best_move | |
else: | |
best_score = float('inf') | |
best_move = None | |
for move in self.get_valid_moves(): | |
self.board[move] = self.human_player | |
score, _ = self.minimax(depth + 1, True, alpha, beta) | |
self.board[move] = 0 | |
if score < best_score: | |
best_score = score | |
best_move = move | |
beta = min(beta, best_score) | |
if beta <= alpha: | |
break | |
return best_score, best_move | |
def ai_move(self) -> str: | |
if len(self.get_valid_moves()) == 0: | |
return self.format_board() | |
_, best_move = self.minimax(0, True) | |
if best_move is not None: | |
self.board[best_move] = self.ai_player | |
return self.format_board() | |
def format_board(self) -> str: | |
symbols = {0: " ", 1: "X", -1: "O"} | |
return "\n".join([ | |
"-------------", | |
f"| {symbols[self.board[0,0]]} | {symbols[self.board[0,1]]} | {symbols[self.board[0,2]]} |", | |
"-------------", | |
f"| {symbols[self.board[1,0]]} | {symbols[self.board[1,1]]} | {symbols[self.board[1,2]]} |", | |
"-------------", | |
f"| {symbols[self.board[2,0]]} | {symbols[self.board[2,1]]} | {symbols[self.board[2,2]]} |", | |
"-------------" | |
]) | |
def make_move(self, row: int, col: int) -> Tuple[str, str]: | |
# Check if move is valid | |
if self.board[row, col] != 0: | |
return self.format_board(), "Invalid move! Cell already taken." | |
# Make human move | |
self.board[row, col] = self.human_player | |
# Check if game is over after human move | |
winner = self.check_winner() | |
if winner is not None: | |
if winner == self.human_player: | |
return self.format_board(), "You win!" | |
elif winner == 0: | |
return self.format_board(), "It's a draw!" | |
# Make AI move | |
self.ai_move() | |
# Check if game is over after AI move | |
winner = self.check_winner() | |
if winner is not None: | |
if winner == self.ai_player: | |
return self.format_board(), "AI wins!" | |
elif winner == 0: | |
return self.format_board(), "It's a draw!" | |
return self.format_board(), "Game in progress" | |
# Create game instance | |
game = TicTacToe() | |
def play_game(row: int, col: int, board_state: str, game_status: str) -> Tuple[str, str]: | |
if "win" in game_status or "draw" in game_status: | |
return board_state, game_status | |
return game.make_move(row, col) | |
def reset_game() -> Tuple[str, str]: | |
return game.reset_board(), "Game reset! Your turn (X)" | |
# Create Gradio interface | |
with gr.Blocks() as demo: | |
gr.Markdown("# Tic Tac Toe vs AI") | |
gr.Markdown("You play as X, AI plays as O. Click the buttons to make your move!") | |
with gr.Row(): | |
# Game board display | |
board_display = gr.Textbox(value=game.format_board(), label="Game Board", lines=7) | |
game_status = gr.Textbox(value="Your turn (X)", label="Game Status") | |
# Create 3x3 grid of buttons | |
with gr.Row(): | |
for i in range(3): | |
with gr.Column(): | |
for j in range(3): | |
btn = gr.Button(f"Row {i}, Col {j}") | |
btn.click( | |
fn=play_game, | |
inputs=[ | |
gr.Number(value=i, visible=False), | |
gr.Number(value=j, visible=False), | |
board_display, | |
game_status | |
], | |
outputs=[board_display, game_status] | |
) | |
# Reset button | |
reset_btn = gr.Button("Reset Game") | |
reset_btn.click(fn=reset_game, inputs=[], outputs=[board_display, game_status]) | |
# Launch app | |
demo.launch() |