Narendra9009 commited on
Commit
46c25c8
·
1 Parent(s): f1de4a9

changes made for getting text based review

Browse files
__pycache__/main.cpython-313.pyc CHANGED
Binary files a/__pycache__/main.cpython-313.pyc and b/__pycache__/main.cpython-313.pyc differ
 
main.py CHANGED
@@ -85,6 +85,7 @@ async def get_fen(file : UploadFile = File(), perspective : str = Form("w"), nex
85
 
86
  @app.post('/getReview')
87
  async def getReview(file_upload: FileUpload):
 
88
  print(os.getcwd())
89
  print("call recieved")
90
 
 
85
 
86
  @app.post('/getReview')
87
  async def getReview(file_upload: FileUpload):
88
+ # this function returns text based and overall review of the game by taking base64 encoded pgn file as input
89
  print(os.getcwd())
90
  print("call recieved")
91
 
routes/__pycache__/chess_review.cpython-313.pyc CHANGED
Binary files a/routes/__pycache__/chess_review.cpython-313.pyc and b/routes/__pycache__/chess_review.cpython-313.pyc differ
 
routes/__pycache__/tex_based_review.cpython-313.pyc ADDED
Binary file (3.93 kB). View file
 
routes/chess_review.py CHANGED
@@ -9,8 +9,14 @@ from datetime import datetime
9
  import csv
10
  import json
11
  from fastapi.responses import JSONResponse
 
 
 
12
 
13
- app = FastAPI()
 
 
 
14
 
15
  class GamePhase(Enum):
16
  OPENING = "opening"
@@ -147,6 +153,7 @@ book_csv_path = os.path.join(os.getcwd(), "assets", "openings_master.csv")
147
 
148
  def analyze_pgn(pgn_file: str) -> Dict:
149
  opening_book = load_opening_book(book_csv_path)
 
150
 
151
  with open(pgn_file) as pgn:
152
  game = chess.pgn.read_game(pgn)
@@ -157,7 +164,8 @@ def analyze_pgn(pgn_file: str) -> Dict:
157
  result = {
158
  "move_analysis": [],
159
  "phase_analysis": {},
160
- "player_summaries": {}
 
161
  }
162
 
163
  with chess.engine.SimpleEngine.popen_uci(engine_path) as engine:
@@ -171,18 +179,30 @@ def analyze_pgn(pgn_file: str) -> Dict:
171
 
172
  for move_number, node in enumerate(game.mainline(), start=1):
173
  # Analyze position before the move
174
- pre_info = engine.analyse(board, chess.engine.Limit(depth=20))
 
175
  pre_eval = pre_info["score"].white().score(mate_score=10000) or 0
176
- best_move = pre_info.get("pv", [None])[0]
 
177
 
178
- # Make the move
 
 
 
 
179
  move = node.move
180
- board.push(move)
181
-
182
  # Analyze position after the move
183
- post_info = engine.analyse(board, limit = chess.engine.Limit(time = 0.3))
 
 
 
 
 
 
184
  post_eval = post_info["score"].white().score(mate_score=10000) or 0
185
-
186
  # Determine game phase
187
  book_move = is_book_move(board, opening_book)
188
  current_phase = detect_game_phase(board, in_opening)
@@ -191,7 +211,7 @@ def analyze_pgn(pgn_file: str) -> Dict:
191
 
192
  # Calculate evaluation loss
193
  eval_loss = abs(pre_eval - post_eval)
194
-
195
  # Initial classification
196
  classification = Classification.BOOK if book_move else None
197
  if not classification:
@@ -205,7 +225,7 @@ def analyze_pgn(pgn_file: str) -> Dict:
205
  # Check for missed opportunities
206
  is_winning = abs(pre_eval) >= FORCED_WIN_THRESHOLD
207
  is_forced_win = pre_info["score"].is_mate() and pre_info["score"].relative.mate() <= MISS_MATE_THRESHOLD
208
- if is_winning and move != best_move and (eval_loss >= MISS_CENTIPAWN_LOSS or is_forced_win):
209
  classification = Classification.MISS
210
 
211
  # Check for brilliant moves
@@ -220,14 +240,18 @@ def analyze_pgn(pgn_file: str) -> Dict:
220
  classifications[player][current_phase].append(classification)
221
  phase_data[current_phase].append(classification)
222
 
223
- # Add move analysis to result
224
  result["move_analysis"].append({
225
  "move_number": move_number,
226
  "player": "White" if board.turn == chess.BLACK else "Black",
227
- "move": move.uci(),
228
  "evaluation": post_eval / 100,
229
  "evaluation_loss": eval_loss / 100,
230
- "classification": classification.value
 
 
 
 
231
  })
232
 
233
  # Phase analysis
@@ -248,7 +272,8 @@ def analyze_pgn(pgn_file: str) -> Dict:
248
  for phase in GamePhase:
249
  phase_moves = classifications[color][phase]
250
  for m in phase_moves:
251
- counts[m.value] += 1
 
252
 
253
  result["player_summaries"][player] = counts
254
 
@@ -267,8 +292,11 @@ def analyze_pgn(pgn_file: str) -> Dict:
267
 
268
 
269
  def get_phase_rating(classified_moves: List[Classification]) -> Classification:
 
270
  if not classified_moves:
271
  return Classification.GOOD
 
 
272
 
273
  total = sum(classification_values[m] for m in classified_moves)
274
  average = total / len(classified_moves)
 
9
  import csv
10
  import json
11
  from fastapi.responses import JSONResponse
12
+ import asyncio
13
+ import sys
14
+ from routes.tex_based_review import review_chess_game, validate_json
15
 
16
+
17
+
18
+ if sys.platform == "win32":
19
+ asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
20
 
21
  class GamePhase(Enum):
22
  OPENING = "opening"
 
153
 
154
  def analyze_pgn(pgn_file: str) -> Dict:
155
  opening_book = load_opening_book(book_csv_path)
156
+ text_based_result = review_chess_game(pgn_file)
157
 
158
  with open(pgn_file) as pgn:
159
  game = chess.pgn.read_game(pgn)
 
164
  result = {
165
  "move_analysis": [],
166
  "phase_analysis": {},
167
+ "player_summaries": {},
168
+ "test_based_review": text_based_result
169
  }
170
 
171
  with chess.engine.SimpleEngine.popen_uci(engine_path) as engine:
 
179
 
180
  for move_number, node in enumerate(game.mainline(), start=1):
181
  # Analyze position before the move
182
+ pre_info = engine.analyse(board, chess.engine.Limit(time=0.3), multipv=3)[0]
183
+
184
  pre_eval = pre_info["score"].white().score(mate_score=10000) or 0
185
+
186
+ pre_pv_moves = pre_info.get("pv", [])
187
 
188
+ # Get best move and follow-up moves in UCI notation
189
+ best_move_pre = pre_pv_moves[0].uci() if pre_pv_moves else None
190
+ follow_up_pre = [m.uci() for m in pre_pv_moves[:min(len(pre_pv_moves), 5)]]
191
+
192
+ # Make the user move
193
  move = node.move
194
+ board.push(move) # Update the board state
195
+
196
  # Analyze position after the move
197
+ post_info = engine.analyse(board, chess.engine.Limit(time=0.3), multipv=3)[0]
198
+
199
+ # Get best move and follow-up moves AFTER move is played (in UCI notation)
200
+ post_pv_moves = post_info.get("pv", [])
201
+ best_move_post = post_pv_moves[0].uci() if post_pv_moves else None
202
+ follow_up_post = [m.uci() for m in post_pv_moves[:min(len(post_pv_moves), 5)]]
203
+
204
  post_eval = post_info["score"].white().score(mate_score=10000) or 0
205
+
206
  # Determine game phase
207
  book_move = is_book_move(board, opening_book)
208
  current_phase = detect_game_phase(board, in_opening)
 
211
 
212
  # Calculate evaluation loss
213
  eval_loss = abs(pre_eval - post_eval)
214
+
215
  # Initial classification
216
  classification = Classification.BOOK if book_move else None
217
  if not classification:
 
225
  # Check for missed opportunities
226
  is_winning = abs(pre_eval) >= FORCED_WIN_THRESHOLD
227
  is_forced_win = pre_info["score"].is_mate() and pre_info["score"].relative.mate() <= MISS_MATE_THRESHOLD
228
+ if is_winning and move != best_move_pre and (eval_loss >= MISS_CENTIPAWN_LOSS or is_forced_win):
229
  classification = Classification.MISS
230
 
231
  # Check for brilliant moves
 
240
  classifications[player][current_phase].append(classification)
241
  phase_data[current_phase].append(classification)
242
 
243
+ # Add move analysis to result (using UCI notation)
244
  result["move_analysis"].append({
245
  "move_number": move_number,
246
  "player": "White" if board.turn == chess.BLACK else "Black",
247
+ "user_move": move.uci(),
248
  "evaluation": post_eval / 100,
249
  "evaluation_loss": eval_loss / 100,
250
+ "classification": classification.value,
251
+ "best_move_pre": best_move_pre, # Best move BEFORE move is played (UCI)
252
+ "follow_up_pre": follow_up_pre, # Follow-up moves BEFORE move is played (UCI)
253
+ "best_move_post": best_move_post, # Best move AFTER move is played (UCI)
254
+ "follow_up_post": follow_up_post # Follow-up moves AFTER move is played (UCI)
255
  })
256
 
257
  # Phase analysis
 
272
  for phase in GamePhase:
273
  phase_moves = classifications[color][phase]
274
  for m in phase_moves:
275
+ m_enum = Classification(m) if isinstance(m, str) else m # Convert if needed
276
+ counts[m_enum.value] += 1
277
 
278
  result["player_summaries"][player] = counts
279
 
 
292
 
293
 
294
  def get_phase_rating(classified_moves: List[Classification]) -> Classification:
295
+
296
  if not classified_moves:
297
  return Classification.GOOD
298
+
299
+ classified_moves = [Classification(m) if isinstance(m, str) else m for m in classified_moves]
300
 
301
  total = sum(classification_values[m] for m in classified_moves)
302
  average = total / len(classified_moves)
routes/tex_based_review.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ from groq import Groq
5
+ import re
6
+
7
+ def review_chess_game(pgn_file_path):
8
+ # Load environment variables and initialize Groq client
9
+ load_dotenv()
10
+ API_KEY = "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"
11
+ if not API_KEY:
12
+ raise ValueError("API key not found. Please set GROQ_API_KEY in the .env file.")
13
+ client = Groq(api_key=API_KEY)
14
+
15
+ # JSON structured prompt
16
+ template = (
17
+ "You are tasked with reviewing a chess game in PGN format: {pgn_content}. "
18
+ "Please provide the analysis in JSON format with the following structure:\n"
19
+ "{{\n"
20
+ ' "summary": "Brief game summary",\n'
21
+ ' "move_reviews": [\n'
22
+ ' {{"move": "e4", "evaluation": "Good", "commentary": "Solid central control"}},\n'
23
+ ' {{"move": "d5", "evaluation": "Brilliant", "commentary": "Aggressive center contest"}}\n'
24
+ ' ],\n'
25
+ ' "biggest_blunders": {{\n'
26
+ ' "player1": "Qxb7",\n'
27
+ ' "player2": "None"\n'
28
+ ' }},\n'
29
+ ' "recommendations": {{\n'
30
+ ' "player1": "Focus on central control",\n'
31
+ ' "player2": "Continue aggressive play"\n'
32
+ ' }}\n'
33
+ "}}\n"
34
+ "Make sure the JSON is well-formatted and does not contain any invalid content."
35
+ )
36
+
37
+ # Read PGN content
38
+ try:
39
+ with open(pgn_file_path, 'r') as file:
40
+ pgn_content = file.read()
41
+ except FileNotFoundError:
42
+ return {"error": f"File '{pgn_file_path}' not found."}
43
+
44
+ # Format the prompt
45
+ prompt = template.format(pgn_content=pgn_content)
46
+
47
+ # Interact with Groq API
48
+ try:
49
+ completion = client.chat.completions.create(
50
+ model="llama-3.3-70b-versatile",
51
+ messages=[{"role": "user", "content": prompt}],
52
+ temperature=1,
53
+ max_tokens=4096,
54
+ top_p=1,
55
+ stream=True,
56
+ stop=None,
57
+ )
58
+
59
+ response_text = ""
60
+ for message in completion:
61
+ if message.choices and message.choices[0].delta.content:
62
+ response_text += message.choices[0].delta.content
63
+ response_text = response_text.strip()
64
+
65
+ # Remove unnecessary text before JSON starts
66
+ response_text = re.sub(r"(?s)^.*?\{", "{", response_text).strip()
67
+ response_text = re.sub(r"```json|```", "", response_text).strip()
68
+
69
+
70
+ # Ensure the response is valid JSON
71
+ try:
72
+ structured_data = json.loads(response_text)
73
+ return structured_data
74
+ except json.JSONDecodeError as e:
75
+ return {"error": f"Failed to parse JSON: {str(e)}", "raw_response": response_text}
76
+
77
+
78
+ except Exception as e:
79
+ return {"error": f"Error processing PGN: {str(e)}"}
80
+
81
+ def validate_json(review):
82
+ """Check if the input is a valid JSON string or dictionary."""
83
+ if isinstance(review, dict):
84
+ print("Valid JSON (dictionary)")
85
+ return True # Already a valid Python dictionary
86
+
87
+ try:
88
+ json.loads(review) # Attempt to parse JSON string
89
+ print("Valid JSON (string)")
90
+ return True
91
+ except json.JSONDecodeError:
92
+ print("Invalid JSON response")
93
+ return False # Return False if JSON is invalid
94
+