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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
import random import gradio as gr from mcts import MCTS, Node from typing import Set, Optional, Any
class StoneGameState(Node): def __init__(self, stones_remaining: int, current_player: str): self.stones_remaining = stones_remaining self.current_player = current_player
@property def opponent(self) -> str: return "O" if self.current_player == "X" else "X"
def find_children(self) -> Set["StoneGameState"]: children = set() if self.stones_remaining >= 1: children.add(StoneGameState(self.stones_remaining - 1, self.opponent)) if self.stones_remaining >= 2: children.add(StoneGameState(self.stones_remaining - 2, self.opponent)) return children
def get_random_child(self) -> Optional["StoneGameState"]: possible_moves = [] if self.stones_remaining >= 1: possible_moves.append(1) if self.stones_remaining >= 2: possible_moves.append(2) if not possible_moves: return None move = random.choice(possible_moves) return StoneGameState(self.stones_remaining - move, self.opponent)
def is_terminal(self) -> bool: return self.stones_remaining == 0
def reward(self) -> float: if not self.is_terminal(): raise RuntimeError("reward() called on non-terminal node") return 0.0
def __hash__(self) -> int: return hash((self.stones_remaining, self.current_player))
def __eq__(self, other: Any) -> bool: if not isinstance(other, StoneGameState): return False return ( self.stones_remaining == other.stones_remaining and self.current_player == other.current_player )
def __repr__(self) -> str: return f"Stones: {self.stones_remaining}, Player: {self.current_player}"
current_state = None mcts = MCTS() message_history = []
def start_game(stones_num): global current_state, mcts, message_history current_state = StoneGameState(int(stones_num), "X") mcts = MCTS() message_history = [f"🏁 初始石子: {stones_num}🪨"]
if current_state.stones_remaining > 0: for _ in range(1000): mcts.simulate(current_state) best_move = mcts.select_best_move(current_state) ai_move = current_state.stones_remaining - best_move.stones_remaining message_history.append(f"🖥️ 拿走 {ai_move}🪨 → 剩余 {best_move.stones_remaining}🪨") current_state = best_move
return update_ui()
def player_move(move: int): global current_state, mcts, message_history
if current_state is None or current_state.is_terminal(): return update_ui()
new_stones = current_state.stones_remaining - move message_history.append(f"👤 拿走 {move}🪨 → 剩余 {new_stones}🪨") current_state = StoneGameState(new_stones, "X")
if not current_state.is_terminal(): for _ in range(1000): mcts.simulate(current_state) best_move = mcts.select_best_move(current_state) ai_move = current_state.stones_remaining - best_move.stones_remaining message_history.append(f"🖥️ 拿走 {ai_move}🪨 → 剩余 {best_move.stones_remaining}🪨") current_state = best_move
return update_ui()
def update_ui(): global current_state, message_history if current_state is None: return ( "<div class='history'>等待游戏开始...</div>", "🕹️ 点击开始按钮", gr.Row(visible=False), "" )
history_html = "<div class='history'>" + "<br>".join(message_history) + "</div>"
if current_state.is_terminal(): winner = "🖥️ AI" if current_state.current_player == "O" else "👤 玩家" return ( history_html, f"🏆 游戏结束 | 胜者: {winner}", gr.Row(visible=False), f"<div class='winner'>{winner} 🎉 获胜!</div>" )
return ( history_html, f"{'🖥️ AI' if current_state.current_player == 'X' else '👤 玩家'} 回合 | 剩余: {current_state.stones_remaining}🪨", gr.Row(visible=current_state.current_player == "O"), "" )
css = """ .history { padding: 15px; border: 1px solid #eee; border-radius: 8px; max-height: 300px; overflow-y: auto; } .winner { padding: 20px; background: #4CAF50; color: white; border-radius: 8px; margin-top: 15px; text-align: center; } """
with gr.Blocks(css=css) as demo: gr.Markdown("# 🪨 石子对战")
with gr.Accordion("📚 游戏规则"): gr.Markdown(""" ### 🎯 玩法: 1. 初始设置石子数量 2. AI先手拿取1-2颗石子 3. 玩家后手拿取1-2颗石子 4. **拿到最后一颗石子者胜** """)
with gr.Row(): stones_input = gr.Number(label="🪨 石子数量", value=10, minimum=3, maximum=20) start_btn = gr.Button("🚀 开始游戏", variant="primary")
history = gr.HTML(label="📜 对战记录") status = gr.HTML(label="📊 实时状态")
with gr.Row(visible=False) as controls: move1_btn = gr.Button("🪨 拿1颗", variant="secondary") move2_btn = gr.Button("🪨🪨 拿2颗", variant="secondary")
result = gr.HTML(visible=False)
start_btn.click( start_game, inputs=[stones_input], outputs=[history, status, controls, result] ) move1_btn.click( lambda: player_move(1), outputs=[history, status, controls, result] ) move2_btn.click( lambda: player_move(2), outputs=[history, status, controls, result] )
if __name__ == "__main__": demo.launch()
|