File size: 3,612 Bytes
ed89329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import gradio as gr
import numpy as np

# Game constants
PLAYER = "X"
AI = "O"
EMPTY = ""

def check_winner(board):
    # Standard Tic-Tac-Toe win conditions
    win_states = [
        [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
    ]
    for line in win_states:
        if board[line[0]] == board[line[1]] == board[line[2]] != EMPTY:
            return board[line[0]]
    if EMPTY not in board:
        return "Tie"
    return None

def minimax(board, depth, is_maximizing):
    res = check_winner(board)
    if res == AI: return 10 - depth
    if res == PLAYER: return depth - 10
    if res == "Tie": return 0

    if is_maximizing:
        best_score = -float('inf')
        for i in range(9):
            if board[i] == EMPTY:
                board[i] = AI
                score = minimax(board, depth + 1, False)
                board[i] = EMPTY
                best_score = max(score, best_score)
        return best_score
    else:
        best_score = float('inf')
        for i in range(9):
            if board[i] == EMPTY:
                board[i] = PLAYER
                score = minimax(board, depth + 1, True)
                board[i] = EMPTY
                best_score = min(score, best_score)
        return best_score

def ai_move(board):
    best_score = -float('inf')
    move = -1
    for i in range(9):
        if board[i] == EMPTY:
            board[i] = AI
            score = minimax(board, 0, False)
            board[i] = EMPTY
            if score > best_score:
                best_score = score
                move = i
    return move

def play_game(board, idx):
    # Convert state to list
    board = list(board)
    
    # Player Move
    if board[idx] == EMPTY:
        board[idx] = PLAYER
    else:
        return board, "Cell already taken!"

    # Check if Player won
    status = check_winner(board)
    if status:
        return board, f"Game Over: {status} wins!" if status != "Tie" else "It's a Tie!"

    # AI Move
    ai_idx = ai_move(board)
    if ai_idx != -1:
        board[ai_idx] = AI

    # Check if AI won
    status = check_winner(board)
    if status:
        return board, f"Game Over: {status} wins!" if status != "Tie" else "It's a Tie!"

    return board, "Your turn, Player X"

def reset_game():
    return [EMPTY] * 9, "New Game! You are X."

# UI Construction
with gr.Blocks(css=".square {height: 100px !important; font-size: 2rem !important;}") as demo:
    gr.Markdown("# 🤖 AI Tic-Tac-Toe")
    gr.Markdown("Try to beat the unbeatable Minimax AI!")
    
    board_state = gr.State([EMPTY] * 9)
    status_msg = gr.Textbox(value="Your turn, Player X", label="Status")
    
    with gr.Column(elem_id="board"):
        buttons = []
        for i in range(3):
            with gr.Row():
                for j in range(3):
                    idx = i * 3 + j
                    btn = gr.Button(value=EMPTY, elem_classes="square")
                    buttons.append(btn)
    
    reset_btn = gr.Button("Reset Game", variant="primary")

    # Click Logic
    for i, btn in enumerate(buttons):
        btn.click(
            play_game, 
            inputs=[board_state, gr.Number(i, visible=False)], 
            outputs=[board_state, status_msg]
        )

    # Update UI buttons when state changes
    board_state.change(
        lambda b: [gr.update(value=val) for val in b], 
        inputs=board_state, 
        outputs=buttons
    )
    
    reset_btn.click(reset_game, outputs=[board_state, status_msg])

if __name__ == "__main__":
    demo.launch()