Falguni commited on
Commit
56c03b3
·
1 Parent(s): 299b8c0

Add funcionalities

Browse files
Files changed (1) hide show
  1. app.py +137 -36
app.py CHANGED
@@ -1,32 +1,85 @@
1
  from typing import Optional, Tuple
 
2
  import gradio as gr
3
 
4
  from src.util.board_vis import colored_unicode_board
5
- from src.util.pgn_util import cleanup_tmp_pgn, export_pgn, read_pgn
6
  from src.thinksqure_engine import ThinkSquareEngine
7
 
8
 
9
- def annotate_pgn(
10
  file, analysis_time_per_move: float = 0.1, style: Optional[str] = "expert"
11
  ) -> str:
12
- """Annotate a PGN file with engine analysis.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  Args:
15
- file: The PGN file to be annotated.
16
- analysis_time_per_move: Time in seconds for engine analysis per move.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  style: The style of annotation to use. Defaults to "expert". Other options can be "novice", "jarvis", "natural", or any custom style.
18
 
19
  Returns:
20
- The annotated PGN file.
21
  """
22
- cleanup_tmp_pgn()
23
 
24
  if style == "":
25
  style = None
26
 
27
  style = str(style).lower().strip()
28
 
29
- game = read_pgn(file.name)
 
 
 
 
 
 
30
 
31
  try:
32
  analysis_time_per_move = float(analysis_time_per_move)
@@ -40,13 +93,15 @@ def annotate_pgn(
40
  return str(annotated_game)
41
 
42
 
43
- def render_board(fen: Optional[str] = None, render_mode: str = "svg") -> str:
44
- """Render the chess board in the specified format.
45
- LLMs should use html rendering mode to render the board in SVG format.
 
 
46
 
47
  Args:
48
  fen: The FEN string representing the board state. If None, the initial position is used.
49
- render_mode: The rendering mode for the board. Can be "ascii", "svg", or "unicode". Defaults to "svg".
50
  Returns:
51
  The rendered board as a string in the specified format.
52
  """
@@ -66,37 +121,39 @@ def play_chess(
66
  move: Optional[str] = "",
67
  fen: Optional[str] = "",
68
  draw_board: bool = True,
69
- render_mode: str = "svg",
70
  ) -> Tuple:
71
  """Play a move in a chess game.
72
- Prerequisites:
73
- - User must be asked if they want to play as white or black.
74
- - If user chooses black, pass an empty string in the first move for the engine to play as white.
75
- - If the user chooses white, ask the user for a move and pass it in the first move for the engine to play as black.
76
 
77
- - User must be asked if they want a board drawn.
78
- - If they do, pass `draw_board=True` to this function. LLMs should use html rendering mode to render the board in SVG format.
79
- - If they do not, pass `draw_board=False`.
 
 
 
 
 
 
80
 
81
- - If a move is provided, it must be in long algebraic notation (e.g., "e4", "Nf3", "Bb5").
82
 
83
- To start a new game:
84
- - Pass empty string for fen.
85
- - Pass empty string for engine to play as white.
86
- - Pass a move in long algebraic notation (e.g., "e4", "Nf3", "Bb5") for engine to play as black.
87
 
88
- To coninue a game:
89
- - Pass the FEN string representing the board state prior to the user's last move.
90
- - Pass a move in long algebraic notation (e.g., "e4", "Nf3", "Bb5") for engine to play the next move.
91
 
92
  Args:
93
  move: The move to play in long algebraic notation. If None, the engine will play a move.
94
  fen: The FEN string representing the board state prior to the user's last move. If None, the game starts from the initial position.
95
- draw_board: Whether to draw the board in ASCII/Unicode format. Defaults to True.
96
- render_mode: The rendering mode for the board. Defaults to "svg". This can be "ascii", "svg", or "unicode".
97
 
98
  Returns:
99
- The best move played by the engine, the updated board state in FEN notation, and a string representation of the board if draw_board is True else None.
100
  """
101
 
102
  if move is None or move == "" or move.lower() == "none" or move.lower() == "null":
@@ -126,6 +183,7 @@ def play_chess(
126
 
127
 
128
  with gr.Blocks(title="ThinkSquare") as app:
 
129
  def save_text_to_file(text):
130
  with open("annotated_game.pgn", "w") as f:
131
  f.write(text)
@@ -142,6 +200,12 @@ with gr.Blocks(title="ThinkSquare") as app:
142
  value=None,
143
  )
144
  draw_board_checkbox = gr.Checkbox(label="Draw Board", value=True)
 
 
 
 
 
 
145
 
146
  play_btn = gr.Button("Submit Move")
147
 
@@ -151,11 +215,10 @@ with gr.Blocks(title="ThinkSquare") as app:
151
 
152
  play_btn.click(
153
  fn=play_chess,
154
- inputs=[move_input, fen_input, draw_board_checkbox],
155
  outputs=[best_move_output, updated_fen_output, board_output],
156
  )
157
-
158
- with gr.Tab("PGN Annotation"):
159
 
160
  def toggle_custom_input(style):
161
  if style == "custom":
@@ -163,7 +226,7 @@ with gr.Blocks(title="ThinkSquare") as app:
163
  else:
164
  return gr.update(visible=False, interactive=False)
165
 
166
- gr.Markdown("### PGN Analyzer")
167
  pgn_file = gr.File(label="Upload PGN", file_types=[".pgn"])
168
  analysis_time = gr.Textbox(
169
  label="Analysis Time per Move (seconds)", value="0.1"
@@ -189,11 +252,34 @@ with gr.Blocks(title="ThinkSquare") as app:
189
  )
190
 
191
  analyze_btn.click(
192
- fn=annotate_pgn,
193
  inputs=[pgn_file, analysis_time, style_dropdown],
194
  outputs=annotated_pgn_file,
195
  )
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  with gr.Tab("Render Board", visible=False):
198
  gr.Markdown("### Render Chess Board")
199
  fen_input_render = gr.Textbox(
@@ -213,7 +299,22 @@ with gr.Blocks(title="ThinkSquare") as app:
213
  inputs=[fen_input_render, render_mode_dropdown],
214
  outputs=board_render_output,
215
  )
 
 
 
 
 
 
 
 
216
 
 
 
 
 
 
 
 
217
 
218
  if __name__ == "__main__":
219
  app.launch(mcp_server=True)
 
1
  from typing import Optional, Tuple
2
+ import chess
3
  import gradio as gr
4
 
5
  from src.util.board_vis import colored_unicode_board
6
+ from src.util.pgn_util import pgn_string_to_game, read_pgn
7
  from src.thinksqure_engine import ThinkSquareEngine
8
 
9
 
10
+ def annotate_pgn_file(
11
  file, analysis_time_per_move: float = 0.1, style: Optional[str] = "expert"
12
  ) -> str:
13
+ """Annotate a chess game from a PGN file.
14
+ This function takes a PGN file and annotates the chess game using an engine.
15
+ Instructions for LLMs:
16
+ - LLMs must not use this function. Use the string-based `annotate_pgn` instead.
17
+ """
18
+ pgn_text = read_pgn(file.name)
19
+
20
+ annotated_game = annotate_pgn(
21
+ pgn_text,
22
+ analysis_time_per_move=analysis_time_per_move,
23
+ style=style,
24
+ )
25
+
26
+ return annotated_game
27
+
28
+
29
+ def suggest_move(fen: Optional[str] = None) -> str:
30
+ """Suggest a move for the given FEN position.
31
+ This function can be used to give a hint to the user about the best move to play.
32
+ This function takes a FEN string representing the current board state and returns the best move in SAN format.
33
 
34
  Args:
35
+ fen: The FEN string representing the board state. If None, the initial position is used.
36
+
37
+ Returns:
38
+ The best move in SAN format.
39
+ """
40
+ if fen is None or fen == "" or fen.lower() == "none" or fen.lower() == "null":
41
+ fen = None
42
+
43
+ best_move_san = ThinkSquareEngine.get_best_move(fen)
44
+
45
+ return best_move_san
46
+
47
+
48
+ def annotate_pgn(
49
+ pgn_input: str,
50
+ analysis_time_per_move: float = 0.1,
51
+ style: Optional[str] = "expert",
52
+ ) -> str:
53
+ """Annotate a chess game with engine analysis.
54
+ This function takes a chess game (PGN) in string format.
55
+
56
+ Instructions for LLMs:
57
+ - Do not send the file name or file path as an arument.
58
+ - LLMs must send the chess game in PGN format as a string.
59
+ - If the user provides a file, read the file and extract the PGN content.
60
+ - LLMs should display the annotated game in a Markdown code block with the label "Annotated PGN".
61
+
62
+ Args:
63
+ pgn_input: The chess game in PGN format as a string.
64
+ analysis_time_per_move: Time in seconds for engine analysis per move. Defaults to 0.1 seconds.
65
  style: The style of annotation to use. Defaults to "expert". Other options can be "novice", "jarvis", "natural", or any custom style.
66
 
67
  Returns:
68
+ The annotated game in PGN format as a string.
69
  """
 
70
 
71
  if style == "":
72
  style = None
73
 
74
  style = str(style).lower().strip()
75
 
76
+ if not isinstance(pgn_input, chess.pgn.Game):
77
+ try:
78
+ pgn_input = pgn_string_to_game(pgn_input)
79
+ except Exception as e:
80
+ raise ValueError(f"Invalid PGN input: {e}")
81
+
82
+ game = pgn_input
83
 
84
  try:
85
  analysis_time_per_move = float(analysis_time_per_move)
 
93
  return str(annotated_game)
94
 
95
 
96
+ def render_board(fen: Optional[str] = None, render_mode: str = "ascii") -> str:
97
+ """Render the chess board in the specified format. Default is ASCII.
98
+
99
+ Instructions for LLMs:
100
+ - LLMs should default to ascii rendering mode.
101
 
102
  Args:
103
  fen: The FEN string representing the board state. If None, the initial position is used.
104
+ render_mode: The rendering mode for the board. Can be "ascii", "svg", or "unicode". Defaults to "ascii".
105
  Returns:
106
  The rendered board as a string in the specified format.
107
  """
 
121
  move: Optional[str] = "",
122
  fen: Optional[str] = "",
123
  draw_board: bool = True,
124
+ render_mode: str = "ascii",
125
  ) -> Tuple:
126
  """Play a move in a chess game.
 
 
 
 
127
 
128
+ Instructions for LLMs:
129
+ Prerequisites:
130
+ - User must be asked if they want to play as white or black.
131
+ - If user chooses black, pass an empty string in the first move for the engine to play as white.
132
+ - If the user chooses white, ask the user for a move and pass it in the first move for the engine to play as black.
133
+
134
+ - User must be asked if they want a board drawn.
135
+ - If they do, pass `draw_board=True` to this function.
136
+ - If they do not, pass `draw_board=False`.
137
 
138
+ - If a move is provided, it must be in long algebraic notation (e.g., "e4", "Nf3", "Bb5").
139
 
140
+ To start a new game:
141
+ - Pass empty string for fen.
142
+ - Pass empty string for engine to play as white.
143
+ - Pass a move in long algebraic notation (e.g., "e4", "Nf3", "Bb5") for engine to play as black.
144
 
145
+ To coninue a game:
146
+ - Pass the FEN string representing the board state prior to the user's last move.
147
+ - Pass a move in long algebraic notation (e.g., "e4", "Nf3", "Bb5") for engine to play the next move.
148
 
149
  Args:
150
  move: The move to play in long algebraic notation. If None, the engine will play a move.
151
  fen: The FEN string representing the board state prior to the user's last move. If None, the game starts from the initial position.
152
+ draw_board: Whether to draw the board in ASCII/Unicode/svg format. Defaults to True.
153
+ render_mode: The rendering mode for the board. Defaults to "ascii". This can be "ascii", "svg", or "unicode". LLMs should default to "ascii".
154
 
155
  Returns:
156
+ The best move played by the engine, the updated board state in FEN notation, and a board representation if draw_board is True else None.
157
  """
158
 
159
  if move is None or move == "" or move.lower() == "none" or move.lower() == "null":
 
183
 
184
 
185
  with gr.Blocks(title="ThinkSquare") as app:
186
+
187
  def save_text_to_file(text):
188
  with open("annotated_game.pgn", "w") as f:
189
  f.write(text)
 
200
  value=None,
201
  )
202
  draw_board_checkbox = gr.Checkbox(label="Draw Board", value=True)
203
+ render_mode_dropdown = gr.Dropdown(
204
+ choices=["ascii", "svg", "unicode"],
205
+ value="svg",
206
+ label="Render Mode",
207
+ visible=False,
208
+ )
209
 
210
  play_btn = gr.Button("Submit Move")
211
 
 
215
 
216
  play_btn.click(
217
  fn=play_chess,
218
+ inputs=[move_input, fen_input, draw_board_checkbox, render_mode_dropdown],
219
  outputs=[best_move_output, updated_fen_output, board_output],
220
  )
221
+ with gr.Tab("Chess Game Annotation", visible=True):
 
222
 
223
  def toggle_custom_input(style):
224
  if style == "custom":
 
226
  else:
227
  return gr.update(visible=False, interactive=False)
228
 
229
+ gr.Markdown("### Analyze and Annotate a PGN File")
230
  pgn_file = gr.File(label="Upload PGN", file_types=[".pgn"])
231
  analysis_time = gr.Textbox(
232
  label="Analysis Time per Move (seconds)", value="0.1"
 
252
  )
253
 
254
  analyze_btn.click(
255
+ fn=annotate_pgn_file,
256
  inputs=[pgn_file, analysis_time, style_dropdown],
257
  outputs=annotated_pgn_file,
258
  )
259
 
260
+ with gr.Tab("Annotate PGN", visible=True):
261
+ gr.Markdown("### Annotate a PGN String")
262
+ pgn_input = gr.Textbox(
263
+ label="PGN String",
264
+ placeholder="Paste your PGN string here",
265
+ lines=10,
266
+ value=None,
267
+ )
268
+ analysis_time_input = gr.Textbox(
269
+ label="Analysis Time per Move (seconds)", value="0.1"
270
+ )
271
+ style_input = gr.Textbox(label="Style (optional)", value="expert")
272
+
273
+ annotate_btn = gr.Button("Annotate PGN")
274
+
275
+ annotated_output = gr.Textbox(label="Annotated PGN Output", lines=10)
276
+
277
+ annotate_btn.click(
278
+ fn=annotate_pgn,
279
+ inputs=[pgn_input, analysis_time_input, style_input],
280
+ outputs=annotated_output,
281
+ )
282
+
283
  with gr.Tab("Render Board", visible=False):
284
  gr.Markdown("### Render Chess Board")
285
  fen_input_render = gr.Textbox(
 
299
  inputs=[fen_input_render, render_mode_dropdown],
300
  outputs=board_render_output,
301
  )
302
+ with gr.Tab("Suggest Move", visible=False):
303
+ gr.Markdown("### Suggest a Move")
304
+ fen_input_suggest = gr.Textbox(
305
+ label="FEN String (optional)",
306
+ placeholder="Leave blank for initial position",
307
+ value=None,
308
+ )
309
+ suggest_btn = gr.Button("Suggest Move")
310
 
311
+ suggested_move_output = gr.Textbox(label="Suggested Move (SAN)")
312
+
313
+ suggest_btn.click(
314
+ fn=suggest_move,
315
+ inputs=fen_input_suggest,
316
+ outputs=suggested_move_output,
317
+ )
318
 
319
  if __name__ == "__main__":
320
  app.launch(mcp_server=True)