Narendra9009 commited on
Commit
97c3bf5
·
1 Parent(s): 2968926

added new api i.e getFen

Browse files
__pycache__/main.cpython-311.pyc CHANGED
Binary files a/__pycache__/main.cpython-311.pyc and b/__pycache__/main.cpython-311.pyc differ
 
main.py CHANGED
@@ -1,12 +1,24 @@
1
  import io
 
 
2
  from fastapi import FastAPI, File, UploadFile, Form
3
  from fastapi.responses import JSONResponse, StreamingResponse
4
  from PIL import Image, UnidentifiedImageError
5
  from routes.segmentation import segment_chess_board
6
  from routes.detection import detect_pieces
7
  from routes.fen_generator import gen_fen
 
8
  from typing import List, Dict, Any, Union
9
  from pydantic import BaseModel
 
 
 
 
 
 
 
 
 
10
 
11
  app = FastAPI()
12
 
@@ -125,3 +137,29 @@ async def get_fen(file : UploadFile = File(), perspective : str = Form("w"), nex
125
 
126
  except Exception as e:
127
  return JSONResponse(content={"error": "Unexpected error occurred", "details": str(e)}, status_code=500)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import io
2
+ import os
3
+ import tempfile
4
  from fastapi import FastAPI, File, UploadFile, Form
5
  from fastapi.responses import JSONResponse, StreamingResponse
6
  from PIL import Image, UnidentifiedImageError
7
  from routes.segmentation import segment_chess_board
8
  from routes.detection import detect_pieces
9
  from routes.fen_generator import gen_fen
10
+ from routes.chess_review import analyze_pgn
11
  from typing import List, Dict, Any, Union
12
  from pydantic import BaseModel
13
+ import asyncio
14
+ import sys
15
+ import tracemalloc
16
+ tracemalloc.start()
17
+
18
+
19
+ if sys.platform == "win32":
20
+ asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
21
+
22
 
23
  app = FastAPI()
24
 
 
137
 
138
  except Exception as e:
139
  return JSONResponse(content={"error": "Unexpected error occurred", "details": str(e)}, status_code=500)
140
+
141
+ @app.post('/getReview')
142
+ async def getReview(file: UploadFile = File(...)):
143
+ print("call recieved")
144
+
145
+ if not file.filename.endswith(".pgn"):
146
+ return JSONResponse(content={"error": "Invalid file format. Please upload a PGN file"}, status_code=400)
147
+
148
+ try:
149
+ # Save the uploaded file to a temporary file
150
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pgn") as tmp_file:
151
+ tmp_file.write(await file.read())
152
+ tmp_file_path = tmp_file.name
153
+
154
+ # Analyze the PGN file
155
+ analysis_result = await analyze_pgn(tmp_file_path)
156
+
157
+ # Clean up the temporary file
158
+ os.remove(tmp_file_path)
159
+
160
+ if not analysis_result:
161
+ return JSONResponse(content={"error": "No game found in the PGN file"}, status_code=400)
162
+
163
+ return JSONResponse(content=analysis_result, status_code=200)
164
+ except Exception as e:
165
+ return JSONResponse(content={"error": "Unexpected error occurred", "details": str(e)}, status_code=500)
routes/__pycache__/chess_review.cpython-311.pyc ADDED
Binary file (10.2 kB). View file
 
routes/chess_review.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ import chess.pgn
3
+ import chess.engine
4
+ from enum import Enum
5
+ from typing import List, Dict
6
+ import asyncio
7
+ import json
8
+ import sys
9
+
10
+ if sys.platform == "win32":
11
+ asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
12
+
13
+
14
+ def load_opening_book(csv_path):
15
+ opening_book = {}
16
+ try:
17
+ with open(csv_path, newline='', encoding='utf-8') as csvfile:
18
+ reader = csv.reader(csvfile)
19
+ next(reader)
20
+ for row in reader:
21
+ if len(row) < 3:
22
+ continue
23
+ pgn_moves = row[2]
24
+ game = chess.pgn.Game()
25
+ board = game.board()
26
+ for move in pgn_moves.split():
27
+ if "." in move:
28
+ continue
29
+ try:
30
+ chess_move = board.push_san(move)
31
+ fen = " ".join(board.fen().split()[:4])
32
+ opening_book[fen] = chess_move.uci()
33
+ except ValueError:
34
+ break
35
+ except Exception as e:
36
+ print(f"Error loading opening book: {e}")
37
+ return opening_book
38
+
39
+
40
+ engine_path = r"D:\venv\chess-vision\code\stockfish-windows-x86-64-avx2\stockfish\stockfish-windows-x86-64-avx2.exe"
41
+ csv_path = r"D:\venv\chess-vision\code\openings_master.csv"
42
+ opening_book = load_opening_book(csv_path)
43
+
44
+
45
+ class GamePhase(Enum):
46
+ OPENING = "opening"
47
+ MIDDLEGAME = "middlegame"
48
+ ENDGAME = "endgame"
49
+
50
+ class Classification(Enum):
51
+ BRILLIANT = "brilliant"
52
+ GREAT = "great"
53
+ BEST = "best"
54
+ EXCELLENT = "excellent"
55
+ GOOD = "good"
56
+ INACCURACY = "inaccuracy"
57
+ MISTAKE = "mistake"
58
+ MISS = "miss"
59
+ BLUNDER = "blunder"
60
+ BOOK = "book"
61
+ FORCED = "forced"
62
+
63
+ classification_values = {
64
+ Classification.BLUNDER: 0,
65
+ Classification.MISTAKE: 0.2,
66
+ Classification.MISS: 0.3,
67
+ Classification.INACCURACY: 0.4,
68
+ Classification.GOOD: 0.65,
69
+ Classification.EXCELLENT: 0.9,
70
+ Classification.BEST: 1,
71
+ Classification.GREAT: 1,
72
+ Classification.BRILLIANT: 1,
73
+ Classification.BOOK: 1,
74
+ Classification.FORCED: 1,
75
+ }
76
+
77
+ centipawn_classifications = [
78
+ Classification.BEST,
79
+ Classification.EXCELLENT,
80
+ Classification.GOOD,
81
+ Classification.INACCURACY,
82
+ Classification.MISS,
83
+ Classification.MISTAKE,
84
+ Classification.BLUNDER,
85
+ ]
86
+
87
+ FORCED_WIN_THRESHOLD = 500
88
+ MISS_CENTIPAWN_LOSS = 300
89
+ MISS_MATE_THRESHOLD = 3
90
+ ENDGAME_MATERIAL_THRESHOLD = 24
91
+ QUEEN_VALUE = 9
92
+
93
+ def detect_game_phase(board: chess.Board, in_opening: bool) -> GamePhase:
94
+ if in_opening:
95
+ return GamePhase.OPENING
96
+ total_material = sum(
97
+ len(board.pieces(p, color)) * {1: 1, 2: 3, 3: 3, 4: 5, 5: QUEEN_VALUE}[p]
98
+ for color in [chess.WHITE, chess.BLACK] for p in chess.PIECE_TYPES if p != chess.KING
99
+ )
100
+ queens = sum(len(board.pieces(chess.QUEEN, color)) for color in [chess.WHITE, chess.BLACK])
101
+ return GamePhase.ENDGAME if total_material <= ENDGAME_MATERIAL_THRESHOLD or (queens == 0 and total_material <= ENDGAME_MATERIAL_THRESHOLD * 2) else GamePhase.MIDDLEGAME
102
+
103
+ def is_book_move(board, opening_book, max_depth=8):
104
+ return None if board.fullmove_number > max_depth else opening_book.get(" ".join(board.fen().split()[:4]))
105
+
106
+ async def analyze_pgn(pgn_file: str):
107
+ with open(pgn_file) as pgn:
108
+ game = chess.pgn.read_game(pgn)
109
+ if not game:
110
+ return json.dumps({"error": "No game found in PGN file."})
111
+
112
+ results = {
113
+ "moves": [],
114
+ "phases": {},
115
+ "players": {"white": {}, "black": {}}
116
+ }
117
+
118
+ with chess.engine.SimpleEngine.popen_uci(engine_path) as engine:
119
+ board = game.board()
120
+ classifications = {"white": {p.value: [] for p in GamePhase}, "black": {p.value: [] for p in GamePhase}}
121
+ in_opening = True
122
+
123
+ for move_number, node in enumerate(game.mainline(), start=1):
124
+ pre_info = engine.analyse(board, chess.engine.Limit(depth=20))
125
+ pre_eval = pre_info["score"].white().score(mate_score=10000) or 0
126
+ best_move = pre_info.get("pv", [None])[0]
127
+ move = node.move
128
+ board.push(move)
129
+ post_info = engine.analyse(board, chess.engine.Limit(depth=20))
130
+ post_eval = post_info["score"].white().score(mate_score=10000) or 0
131
+ book_move = is_book_move(board, opening_book)
132
+ current_phase = detect_game_phase(board, in_opening)
133
+ if not book_move and in_opening:
134
+ in_opening = False
135
+ eval_loss = abs(pre_eval - post_eval)
136
+ classification = Classification.BOOK if book_move else Classification.BLUNDER
137
+ player = "white" if board.turn == chess.BLACK else "black"
138
+ classifications[player][current_phase.value].append(classification.value)
139
+ results["moves"].append({
140
+ "move_number": move_number,
141
+ "player": player,
142
+ "move": move.uci(),
143
+ "evaluation": post_eval / 100,
144
+ "evaluation_loss": eval_loss / 100,
145
+ "classification": classification.value
146
+ })
147
+
148
+ for phase in GamePhase:
149
+ results["phases"][phase.value] = {
150
+ "white": classifications["white"][phase.value],
151
+ "black": classifications["black"][phase.value]
152
+ }
153
+ results["players"]["white"] = classifications["white"]
154
+ results["players"]["black"] = classifications["black"]
155
+
156
+ return json.dumps(results, indent=4)