Spaces:
Running
Running
| from __future__ import annotations | |
| from pathlib import Path | |
| import gradio as gr | |
| import numpy as np | |
| from game import is_terminal, legal_actions, play, winner | |
| from mcts import NeuralMCTS | |
| from model import MicroZeroNet | |
| from safetensors.torch import load_file | |
| ARTIFACT = Path(__file__).resolve().parent / "artifacts" / "microzero" | |
| MODEL = MicroZeroNet() | |
| MODEL.load_state_dict(load_file(ARTIFACT / "model.safetensors")) | |
| MODEL.eval() | |
| SYMBOLS = {0: ".", 1: "X", -1: "O"} | |
| def neural_action( | |
| model: MicroZeroNet, | |
| board: np.ndarray, | |
| player: int, | |
| simulations: int, | |
| seed: int, | |
| ) -> int: | |
| search = NeuralMCTS(model, simulations=simulations) | |
| policy = search.policy( | |
| board, | |
| player, | |
| temperature=0.0, | |
| add_noise=False, | |
| rng=np.random.default_rng(seed), | |
| ) | |
| return int(np.argmax(policy)) | |
| def render(board: list[int], message: str) -> str: | |
| cells = [SYMBOLS[int(value)] for value in board] | |
| rows = [" | ".join(cells[index : index + 3]) for index in range(0, 9, 3)] | |
| return "```\n" + "\n---------\n".join(rows) + f"\n```\n{message}" | |
| def new_game() -> tuple[list[int], str]: | |
| board = [0] * 9 | |
| return board, render(board, "You are X. Choose a cell from 0 to 8.") | |
| def move(board: list[int], action: int) -> tuple[list[int], str]: | |
| array = np.asarray(board, dtype=np.int8) | |
| action = int(action) | |
| if action not in legal_actions(array): | |
| return board, render(board, "That cell is occupied.") | |
| array = play(array, action, 1) | |
| if is_terminal(array): | |
| message = "You win." if winner(array) == 1 else "Draw." | |
| return array.tolist(), render(array.tolist(), message) | |
| response = neural_action(MODEL, array, -1, simulations=96, seed=7000 + action) | |
| array = play(array, response, -1) | |
| result = winner(array) | |
| if result == -1: | |
| message = f"MicroZero played {response} and won." | |
| elif is_terminal(array): | |
| message = f"MicroZero played {response}. Draw." | |
| else: | |
| message = f"MicroZero played {response}. Your turn." | |
| return array.tolist(), render(array.tolist(), message) | |
| with gr.Blocks(title="MicroZero") as demo: | |
| gr.Markdown("# MicroZero\nPlay against the neural MCTS agent.") | |
| state = gr.State([0] * 9) | |
| board = gr.Markdown(render([0] * 9, "You are X. Choose a cell from 0 to 8.")) | |
| action = gr.Slider(0, 8, value=4, step=1, label="Your move") | |
| with gr.Row(): | |
| play_button = gr.Button("Play", variant="primary") | |
| reset_button = gr.Button("New game") | |
| play_button.click(move, inputs=[state, action], outputs=[state, board]) | |
| reset_button.click(new_game, outputs=[state, board]) | |
| if __name__ == "__main__": | |
| demo.launch() | |