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()