-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_opening.py
More file actions
119 lines (96 loc) · 3.88 KB
/
generate_opening.py
File metadata and controls
119 lines (96 loc) · 3.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import requests
import chess
import chess.pgn
import urllib.parse
import io
def get_opening_stats(fen, time_control, rating):
url = "https://explorer.lichess.ovh/lichess"
params = {
"fen": fen,
"speeds": time_control,
"ratings": rating,
}
encoded_params = urllib.parse.urlencode(params)
full_url = f"{url}?{encoded_params}"
try:
response = requests.get(full_url)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error fetching data from Lichess API: {e}")
print(f"Response status code: {response.status_code}")
print(f"Response content: {response.text}")
print(f"Full URL: {full_url}")
return None
def get_best_move(stats):
moves = stats.get("moves", [])
if not moves:
return None
best_move = max(moves, key=lambda m: (m["white"] + 0.5 * m["draws"]) / (m["white"] + m["draws"] + m["black"]))
return best_move["san"]
def get_valid_responses(stats, min_probability=0.05):
moves = stats.get("moves", [])
if not moves:
return []
total_games = sum(move["white"] + move["draws"] + move["black"] for move in moves)
return [move["san"] for move in moves if (move["white"] + move["draws"] + move["black"]) / total_games >= min_probability]
def create_variation_tree(node, board, depth, time_control, rating, is_player_turn=True):
if depth == 0:
return
stats = get_opening_stats(board.fen(), time_control, rating)
if stats is None:
return
if is_player_turn:
# Player's move (White)
best_move = get_best_move(stats)
if best_move is None:
return
new_node = node.add_variation(board.parse_san(best_move))
new_board = board.copy()
new_board.push_san(best_move)
create_variation_tree(new_node, new_board, depth - 1, time_control, rating, False)
else:
# Opponent's moves (Black)
valid_responses = get_valid_responses(stats)
for response in valid_responses:
new_node = node.add_variation(board.parse_san(response))
new_board = board.copy()
new_board.push_san(response)
create_variation_tree(new_node, new_board, depth - 1, time_control, rating, True)
def create_pgn(depth, time_control, rating):
game = chess.pgn.Game()
game.headers["Event"] = "Lichess Opening Explorer"
game.headers["Site"] = "https://lichess.org/"
game.headers["Date"] = "????.??.??"
game.headers["Round"] = "-"
game.headers["White"] = "Player"
game.headers["Black"] = "Opponent"
game.headers["Result"] = "*"
board = chess.Board()
create_variation_tree(game, board, depth, time_control, rating)
return game
def main():
fen = input("Enter FEN (press Enter for starting position): ").strip()
if not fen:
fen = chess.STARTING_FEN
time_control = input("Enter time control (bullet,blitz,rapid,classical or leave empty for all): ").strip().lower()
if not time_control:
time_control = "bullet,blitz,rapid,classical"
min_rating = input("Enter minimum rating (1600,1800,2000,2200,2500 or leave empty for all): ").strip()
if not min_rating:
rating = "0,1600,1800,2000,2200,2500"
else:
rating = f"{min_rating},2500"
depth = int(input("Enter the depth of variations to explore: "))
print("\nGenerating PGN...")
game = create_pgn(depth, time_control, rating)
pgn_string = io.StringIO()
exporter = chess.pgn.FileExporter(pgn_string)
game.accept(exporter)
with open("generated_variations.pgn", "w") as pgn_file:
pgn_file.write(pgn_string.getvalue())
print("PGN file 'generated_variations.pgn' has been created.")
print("\nGenerated PGN:")
print(pgn_string.getvalue())
if __name__ == "__main__":
main()