Commit
·
4b7d2b8
0
Parent(s):
Initial commit
Browse files- .DS_Store +0 -0
- .gitattributes +36 -0
- README.md +13 -0
- app.py +79 -0
- bin/stockfish +3 -0
- requirements.txt +1 -0
- src/thinksqure_engine.py +67 -0
.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
.gitattributes
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
bin/stockfish filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: ThinkSquare
|
3 |
+
emoji: 🏆
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: green
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 5.32.1
|
8 |
+
app_file: app.py
|
9 |
+
pinned: false
|
10 |
+
license: apache-2.0
|
11 |
+
---
|
12 |
+
|
13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Optional, Tuple
|
2 |
+
import gradio as gr
|
3 |
+
|
4 |
+
from ThinkSquare.src.thinksqure_engine import ThinkSquareEngine
|
5 |
+
|
6 |
+
|
7 |
+
def play_chess(
|
8 |
+
move: Optional[str] = "", fen: Optional[str] = "", draw_ascii: bool = True
|
9 |
+
) -> Tuple:
|
10 |
+
"""Play a move in a chess game.
|
11 |
+
Prerequisites:
|
12 |
+
- User must be asked if they want to play as white or black. If user chooses black, pass an empty string in the first move for the engine to play as white.
|
13 |
+
- User must be asked if they want a board drawn in ASCII format.
|
14 |
+
- If a move is provided, it must be in long algebraic notation (e.g., "e4", "Nf3", "Bb5").
|
15 |
+
|
16 |
+
To start a new game:
|
17 |
+
- Pass empty string for fen.
|
18 |
+
- Pass empty string for engine to play as white.
|
19 |
+
- Pass a move in long algebraic notation (e.g., "e4", "Nf3", "Bb5") for engine to play as black.
|
20 |
+
|
21 |
+
To coninue a game:
|
22 |
+
- Pass the FEN string representing the board state prior to the user's last move.
|
23 |
+
- Pass a move in long algebraic notation (e.g., "e4", "Nf3", "Bb5") for engine to play the next move.
|
24 |
+
|
25 |
+
Args:
|
26 |
+
move: The move to play in long algebraic notation. If None, the engine will play a move.
|
27 |
+
fen: The FEN string representing the board state prior to the user's last move. If None, the game starts from the initial position.
|
28 |
+
draw_ascii: Whether to draw the board in ASCII format. Defaults to True.
|
29 |
+
|
30 |
+
Returns:
|
31 |
+
The best move played by the engine, the updated board state in FEN notation, and a string representation of the board if draw_ascii is True else None.
|
32 |
+
"""
|
33 |
+
|
34 |
+
if move is None or move == "" or move.lower() == "none" or move.lower() == "null":
|
35 |
+
move = None
|
36 |
+
if fen is None or fen == "" or fen.lower() == "none" or fen.lower() == "null":
|
37 |
+
fen = None
|
38 |
+
|
39 |
+
if move is not None:
|
40 |
+
is_valid = ThinkSquareEngine.is_valid_move(move, fen)
|
41 |
+
|
42 |
+
if not is_valid:
|
43 |
+
return "Invalid move", "", ""
|
44 |
+
|
45 |
+
fen = ThinkSquareEngine.get_fen_after_move(move, fen)
|
46 |
+
|
47 |
+
assert fen is not None, "FEN after move should not be None"
|
48 |
+
|
49 |
+
bestmove_san = ThinkSquareEngine.get_best_move(fen)
|
50 |
+
fen_after_move = ThinkSquareEngine.get_fen_after_move(bestmove_san, fen)
|
51 |
+
|
52 |
+
board_str = (
|
53 |
+
ThinkSquareEngine.get_ascii_board(fen_after_move) if draw_ascii else None
|
54 |
+
)
|
55 |
+
|
56 |
+
return bestmove_san, fen_after_move, board_str
|
57 |
+
|
58 |
+
|
59 |
+
demo = gr.Interface(
|
60 |
+
fn=play_chess,
|
61 |
+
inputs=[
|
62 |
+
gr.Textbox(
|
63 |
+
label="Your Move (in standard algebraic notation - SAN) (Optional)",
|
64 |
+
placeholder="e.g. e4, Nf3, Bb5, etc. Optional - ",
|
65 |
+
value=None,
|
66 |
+
),
|
67 |
+
gr.Textbox(
|
68 |
+
label="FEN String (optional)",
|
69 |
+
placeholder="Optional - Leave blank to start from initial position",
|
70 |
+
value=None,
|
71 |
+
),
|
72 |
+
gr.Checkbox(label="Draw ASCII Board", value=True),
|
73 |
+
],
|
74 |
+
outputs=("text", "text", "text"),
|
75 |
+
title="Play Chess with an engine",
|
76 |
+
description="This interface allows you to play chess against the Stockfish engine. You can provide a move in standard algebraic notation and an optional FEN string representing the board state. The engine will respond with its best move, the updated FEN string, and an ASCII representation of the board if requested.",
|
77 |
+
)
|
78 |
+
|
79 |
+
demo.launch(mcp_server=True)
|
bin/stockfish
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7fecbc0b26454b62be5e3b237b58dc5666401b56e520aeb1b0bf8f53fa8f2ef3
|
3 |
+
size 78768416
|
requirements.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
chess==1.11.2
|
src/thinksqure_engine.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Optional
|
2 |
+
import chess
|
3 |
+
import chess.engine
|
4 |
+
|
5 |
+
|
6 |
+
class ThinkSquareEngine:
|
7 |
+
_ENGINE = "ThinkSquare/bin/stockfish"
|
8 |
+
|
9 |
+
@staticmethod
|
10 |
+
def get_best_move(fen: Optional[str] = None, time_limit=0.1):
|
11 |
+
if fen is None:
|
12 |
+
fen = chess.STARTING_FEN
|
13 |
+
|
14 |
+
board = chess.Board(fen)
|
15 |
+
|
16 |
+
with chess.engine.SimpleEngine.popen_uci(ThinkSquareEngine._ENGINE) as engine:
|
17 |
+
result = engine.play(board, chess.engine.Limit(time=time_limit))
|
18 |
+
best_move = result.move
|
19 |
+
bestmove_san = board.san(best_move)
|
20 |
+
|
21 |
+
return bestmove_san
|
22 |
+
|
23 |
+
@staticmethod
|
24 |
+
def is_valid_move(
|
25 |
+
move_san: str,
|
26 |
+
fen: Optional[str] = None,
|
27 |
+
) -> bool:
|
28 |
+
if fen is None:
|
29 |
+
fen = chess.STARTING_FEN
|
30 |
+
|
31 |
+
board = chess.Board(fen)
|
32 |
+
|
33 |
+
try:
|
34 |
+
move = board.parse_san(move_san)
|
35 |
+
return board.is_legal(move)
|
36 |
+
except ValueError:
|
37 |
+
return False
|
38 |
+
|
39 |
+
@staticmethod
|
40 |
+
def get_fen_after_move(
|
41 |
+
move_san: str,
|
42 |
+
fen: Optional[str] = None,
|
43 |
+
) -> Optional[str]:
|
44 |
+
if fen is None:
|
45 |
+
fen = chess.STARTING_FEN
|
46 |
+
|
47 |
+
board = chess.Board(fen)
|
48 |
+
|
49 |
+
try:
|
50 |
+
move = board.parse_san(move_san)
|
51 |
+
if board.is_legal(move):
|
52 |
+
board.push(move)
|
53 |
+
return board.fen()
|
54 |
+
else:
|
55 |
+
return None
|
56 |
+
except ValueError:
|
57 |
+
return None
|
58 |
+
|
59 |
+
@staticmethod
|
60 |
+
def get_ascii_board(fen: Optional[str] = None) -> str:
|
61 |
+
if fen is None:
|
62 |
+
fen = chess.STARTING_FEN
|
63 |
+
|
64 |
+
board = chess.Board(fen)
|
65 |
+
ascii_representation = str(board)
|
66 |
+
|
67 |
+
return ascii_representation
|