tic-tac-toe-ai / app.py
aicodingfun's picture
Update app.py
2a60635 verified
import numpy as np
import random
import gradio as gr
from PIL import Image, ImageDraw
def ai_move(board):
best_score = -np.inf
best_move = None
for r, c in available_moves(board):
board[r][c] = "X"
score = minimax(board, 0, False) # AI 使用最大化策略,深度從 0 開始
board[r][c] = ""
if score > best_score:
best_score = score
best_move = (r, c)
if best_move:
board[best_move[0]][best_move[1]] = "X"
return board
def initialize_board():
return [["", "", ""] for _ in range(3)]
def draw_board(board):
img_size = 300
cell_size = img_size // 3
img = Image.new("RGB", (img_size, img_size), "white")
draw = ImageDraw.Draw(img)
# Draw grid lines
for i in range(1, 3):
draw.line([(0, i * cell_size), (img_size, i * cell_size)], fill="black", width=3)
draw.line([(i * cell_size, 0), (i * cell_size, img_size)], fill="black", width=3)
# Draw ⨉ and 𐤏
for r in range(3):
for c in range(3):
if board[r][c] == "X":
draw.line([(c * cell_size + 10, r * cell_size + 10), ((c + 1) * cell_size - 10, (r + 1) * cell_size - 10)], fill="red", width=3)
draw.line([((c + 1) * cell_size - 10, r * cell_size + 10), (c * cell_size + 10, (r + 1) * cell_size - 10)], fill="red", width=3)
elif board[r][c] == "O":
draw.ellipse([(c * cell_size + 10, r * cell_size + 10), ((c + 1) * cell_size - 10, (r + 1) * cell_size - 10)], outline="blue", width=3)
return img
def check_winner(board):
for i in range(3):
if board[i][0] == board[i][1] == board[i][2] and board[i][0] != "":
return board[i][0]
if board[0][i] == board[1][i] == board[2][i] and board[0][i] != "":
return board[0][i]
if board[0][0] == board[1][1] == board[2][2] and board[0][0] != "":
return board[0][0]
if board[0][2] == board[1][1] == board[2][0] and board[0][2] != "":
return board[0][2]
if all(cell != "" for row in board for cell in row):
return "Tie"
return None
def available_moves(board):
return [(r, c) for r in range(3) for c in range(3) if board[r][c] == ""]
def minimax(board, depth, is_maximizing):
result = check_winner(board)
if result == "O":
return -10 + depth
elif result == "X":
return 10 - depth
elif result == "Tie":
return 0
if is_maximizing:
best_score = -np.inf
for r, c in available_moves(board):
board[r][c] = "X"
score = minimax(board, depth + 1, False)
board[r][c] = ""
best_score = max(score, best_score)
return best_score
else:
best_score = np.inf
for r, c in available_moves(board):
board[r][c] = "O"
score = minimax(board, depth + 1, True)
board[r][c] = ""
best_score = min(score, best_score)
return best_score
def play_tic_tac_toe(evt: gr.SelectData, board, game_over):
# 確認 evt 是否有效
if not evt or not hasattr(evt, "index") or not evt.index:
return draw_board(board), "Invalid move! Please select a valid square.", False
if game_over:
return draw_board(board), "Game already finished! Please reset to play again.", True
try:
r, c = evt.index[1] // 100, evt.index[0] // 100
except (TypeError, IndexError):
return draw_board(board), "Invalid selection! Please try again.", False
if board[r][c] != "":
return draw_board(board), "Invalid move! This spot is already taken. Please select an empty square.", False
board[r][c] = "O"
result = check_winner(board)
if result:
return draw_board(board), f"Game Over! Player {result} wins!" if result != "Tie" else "It's a tie!", True
board = ai_move(board)
result = check_winner(board)
if result:
return draw_board(board), f"Game Over! Player {result} wins!" if result != "Tie" else "It's a tie!", True
return draw_board(board), "Game in progress", False
def reset_game():
board = initialize_board()
return draw_board(board), board, "New game started!", False
with gr.Blocks() as app:
gr.Markdown("# Tic-Tac-Toe AI Game")
board = initialize_board()
board_image = gr.Image(value=draw_board(board), interactive=False, label="Board", show_download_button=False, show_fullscreen_button=False)
message_display = gr.Textbox(label="Game Status", value="Game in progress")
reset_button = gr.Button("Reset Game")
board_state = gr.State(board)
game_over_state = gr.State(False)
board_image.select(play_tic_tac_toe, inputs=[board_state, game_over_state], outputs=[board_image, message_display, game_over_state])
reset_button.click(reset_game, inputs=[], outputs=[board_image, board_state, message_display, game_over_state])
app.launch()