Spaces:
Sleeping
Sleeping
| import os | |
| os.system("pip install gradio chess cairosvg torch huggingface") | |
| import gradio as gr | |
| import chess | |
| import chess.svg | |
| from io import BytesIO | |
| import cairosvg | |
| import torch | |
| from huggingface_hub import hf_hub_download | |
| from torch import nn | |
| import numpy as np | |
| from PIL import Image | |
| eval = 0 | |
| # Global board state | |
| wait_list_ip = [] | |
| wait_list_name = [] | |
| user_n = 0 | |
| board = chess.Board() | |
| player_color = chess.WHITE # Default | |
| ai_color = chess.BLACK # Default | |
| model_path = hf_hub_download("sigmoidneuron123/NeoChess", "chessy_model.pth") | |
| device = torch.device("cpu") | |
| def board_to_tensor(board): | |
| piece_encoding = { | |
| 'P': 1, 'N': 2, 'B': 3, 'R': 4, 'Q': 5, 'K': 6, | |
| 'p': 7, 'n': 8, 'b': 9, 'r': 10, 'q': 11, 'k': 12 | |
| } | |
| tensor = torch.zeros(64, dtype=torch.long) | |
| for square in chess.SQUARES: | |
| piece = board.piece_at(square) | |
| if piece: | |
| tensor[square] = piece_encoding[piece.symbol()] | |
| else: | |
| tensor[square] = 0 | |
| return tensor.unsqueeze(0) | |
| class NN1(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.embedding = nn.Embedding(13, 64) | |
| self.attention = nn.MultiheadAttention(embed_dim=64, num_heads=16) | |
| self.neu = 512 | |
| self.neurons = nn.Sequential( | |
| nn.Linear(4096, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, self.neu), | |
| nn.ReLU(), | |
| nn.Linear(self.neu, 64), | |
| nn.ReLU(), | |
| nn.Linear(64, 4) | |
| ) | |
| def forward(self, x): | |
| x = self.embedding(x) | |
| x = x.permute(1, 0, 2) | |
| attn_output, _ = self.attention(x, x, x) | |
| x = attn_output.permute(1, 0, 2).contiguous() | |
| x = x.view(x.size(0), -1) | |
| x = self.neurons(x) | |
| return x | |
| model = NN1().to(device) | |
| file = torch.load(model_path,map_location=device) | |
| model.load_state_dict(file) | |
| model.eval() | |
| def get_evaluation(board): | |
| tensor = board_to_tensor(board).to(device) | |
| with torch.no_grad(): | |
| evaluation = model(tensor)[0][0].item() | |
| if board.turn == chess.WHITE: | |
| return evaluation | |
| else: | |
| return -evaluation | |
| def order_moves(board): | |
| # Example heuristic: captures first, then others | |
| captures = [] | |
| non_captures = [] | |
| for move in board.legal_moves: | |
| if board.is_capture(move): | |
| captures.append(move) | |
| else: | |
| non_captures.append(move) | |
| # Put captures first | |
| return captures + non_captures | |
| def search(board, depth, alpha, beta): | |
| """ | |
| A negamax search function. | |
| """ | |
| if depth == 0 or board.is_game_over(): | |
| return get_evaluation(board) | |
| max_eval = float('-inf') | |
| for move in order_moves(board): | |
| board.push(move) | |
| evali = -search(board, depth - 1, -beta, -alpha) | |
| board.pop() | |
| max_eval = max(max_eval, evali) | |
| alpha = max(alpha, evali) | |
| if alpha >= beta: | |
| break | |
| return max_eval | |
| def ai_actor(board): | |
| evaling = {} | |
| for move in board.legal_moves: | |
| board.push(move) | |
| evaling[move] = -search(board, depth=2, alpha=float('-inf'), beta=float('inf')) | |
| board.pop() | |
| keys = list(evaling.keys()) | |
| vals = list(evaling.values()) | |
| logits = torch.tensor(vals).to(device) | |
| probs = torch.softmax(logits,dim=0) | |
| idx = torch.argmax(probs) | |
| idx = int(idx) | |
| return (keys[idx],vals[idx]) | |
| # Convert board to image | |
| def board_to_image(): | |
| svg_data = chess.svg.board(board, orientation=player_color) | |
| png_data = BytesIO() | |
| cairosvg.svg2png(bytestring=svg_data.encode('utf-8'), write_to=png_data) | |
| return np.array(Image.open(BytesIO(png_data.getvalue())).convert("RGB")) | |
| # Handle move input | |
| def make_move(move_uci,request: gr.Request): | |
| global board, eval, wait_list_ip, user_n | |
| if wait_list_ip[user_n] == request.client.host: | |
| if board.turn == player_color: | |
| if not move_uci: | |
| return "Please enter a move.", board_to_image(), f'Your Eval: {eval}' | |
| if board.is_game_over(): | |
| return "Game over!", board_to_image(), f'Your Eval: {eval}' | |
| # Player's move | |
| try: | |
| board.push_uci(move_uci) | |
| except: | |
| return "Invalid move!", board_to_image(), f'Your Eval: {eval}' | |
| # AI's move | |
| if not board.is_game_over(): | |
| ai_act = ai_actor(board) | |
| ai_move = ai_act[0].uci() | |
| eval = -ai_act[1] | |
| if ai_move: | |
| board.push_uci(ai_move) | |
| return "Move accepted.", board_to_image(), f'Your Eval: {eval}' | |
| else: | |
| ai_act = ai_actor(board) | |
| ai_move = ai_act[0].uci() | |
| eval = -ai_act[1] | |
| if ai_move: | |
| board.push_uci(ai_move) | |
| if not move_uci: | |
| return "Please enter a move.", board_to_image(), f'Your Eval: {eval}' | |
| if board.is_game_over(): | |
| return "Game over!", board_to_image(), f'Your Eval: {eval}' | |
| # Player's move | |
| try: | |
| board.push_uci(move_uci) | |
| except: | |
| return "Invalid move!", board_to_image(), f'Your Eval: {eval}' | |
| return "Move accepted.", board_to_image(), f'Your Eval: {eval}' | |
| else: | |
| return "Wrong user!", board_to_image(), f'Your Eval: {eval}' | |
| def wait(user_name,request: gr.Request): | |
| global wait_list_ip, wait_list_name | |
| wait_list_ip.append(request.client.host) | |
| wait_list_name.append(user_name) | |
| return None | |
| def next(request: gr.Request): | |
| global wait_list_ip, user_n, wait_list_name | |
| if wait_list_ip[user_n] == request.client.host: | |
| try: | |
| user_n += 1 | |
| wait_list_ip[user_n] | |
| except: | |
| pass | |
| return f"Current_user: {wait_list_name[user_n]}" | |
| # Reset game | |
| def reset_game(request: gr.Request): | |
| global board, wait_list_ip, user_n | |
| if wait_list_ip[user_n] == request.client.host: | |
| board = chess.Board() | |
| return board_to_image() | |
| # Set player color | |
| def set_color(color_choice,request: gr.Request): | |
| global player_color, ai_color, board, wait_list_ip, user_n | |
| if wait_list_ip[user_n] == request.client.host: | |
| player_color = chess.WHITE if color_choice == "White" else chess.BLACK | |
| ai_color = not player_color | |
| board.reset() | |
| return board_to_image() | |
| # --- Gradio UI --- | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## ♟️ Chess AI") | |
| eval_val = gr.Markdown('Your Eval: 0.0') | |
| current_user = gr.Markdown("Current_user: None") | |
| with gr.Row(): | |
| color_choice = gr.Radio(choices=["White", "Black"], value="White", label="Choose your side") | |
| reset_btn = gr.Button("♻️ Reset Board") | |
| board_output = gr.Image(value=board_to_image(), label="Chess Board", type="numpy") | |
| with gr.Row(): | |
| move_input = gr.Textbox(label="Enter your move (UCI format, e.g., e2e4)") | |
| result = gr.Textbox(label="Result") | |
| submit = gr.Button("Play Move") | |
| with gr.Row(): | |
| name = gr.Textbox(label="Enter user name") | |
| wai = gr.Button("Wait") | |
| nx = gr.Button("Next") | |
| submit.click(make_move, inputs=[move_input], outputs=[result, board_output, eval_val]) | |
| reset_btn.click(reset_game, outputs=[board_output]) | |
| color_choice.change(set_color, inputs=[color_choice], outputs=board_output) | |
| wai.click(wait, inputs=[name], outputs=[]) | |
| nx.click(next,outputs=[current_user]) | |
| demo.launch() | |