1+ #!/usr/bin/env python3
2+
3+ import argparse
4+ import glob
5+ from collections import defaultdict
6+ from itertools import chain
7+ import chess .pgn
8+
9+ # [LL, LD/DL, WL/DD/LW, WD/DW, WW]
10+ PTNML_INDEX = {- 2 : 0 , - 1 : 1 , 0 : 2 , 1 : 3 , 2 : 4 }
11+
12+
13+ def score_from_new (headers ):
14+ result = headers ["Result" ]
15+
16+ if result == "1/2-1/2" :
17+ return 0
18+
19+ new_is_white = headers ["White" ] == "new"
20+
21+ if new_is_white :
22+ return 1 if result == "1-0" else - 1
23+ else :
24+ return 1 if result == "0-1" else - 1
25+
26+
27+ def main ():
28+ parser = argparse .ArgumentParser (
29+ description = "Extract WDL and pentanomial statistics from fastchess PGNs"
30+ )
31+ parser .add_argument ("pgns" , nargs = "+" )
32+ args = parser .parse_args ()
33+
34+ pairs = defaultdict (list )
35+
36+ # WDL = [loss, draw, win]
37+ wdl = [0 , 0 , 0 ]
38+ games = 0
39+
40+ for filename in chain .from_iterable (glob .glob (p ) for p in args .pgns ):
41+ with open (filename , encoding = "utf-8" ) as f :
42+ while (game := chess .pgn .read_game (f )) is not None :
43+ headers = game .headers
44+
45+ score = score_from_new (headers )
46+
47+ games += 1
48+ if score < 0 :
49+ wdl [0 ] += 1
50+ elif score == 0 :
51+ wdl [1 ] += 1
52+ else :
53+ wdl [2 ] += 1
54+
55+ fen = headers .get ("FEN" )
56+ round_id = headers .get ("Round" )
57+
58+ if fen is None or round_id is None :
59+ raise RuntimeError (
60+ f"{ filename } : missing FEN or Round tag"
61+ )
62+
63+ # Include filename because each shard starts rounds again
64+ key = (
65+ filename ,
66+ round_id ,
67+ fen ,
68+ )
69+
70+ pairs [key ].append (score )
71+
72+ ptnml = [0 ] * 5
73+ pairs_count = 0
74+
75+ for key , scores in pairs .items ():
76+ if len (scores ) != 2 :
77+ print (
78+ f"Skipping incomplete pair { key } : got { len (scores )} games"
79+ )
80+ continue
81+
82+ # Make sure the two games are the repeat pair
83+ if scores [0 ] + scores [1 ] not in PTNML_INDEX :
84+ raise RuntimeError ("Invalid pair score" )
85+
86+ ptnml [PTNML_INDEX [scores [0 ] + scores [1 ]]] += 1
87+ pairs_count += 1
88+
89+ print (f"Games: { games } " )
90+ print (f"Pairs: { pairs_count } " )
91+
92+ print ()
93+ print ("WDL:" )
94+ print (f" Loss: { wdl [0 ]} " )
95+ print (f" Draw: { wdl [1 ]} " )
96+ print (f" Win : { wdl [2 ]} " )
97+ print ()
98+ print (wdl )
99+
100+ print ()
101+ print ("PTNML:" )
102+ names = [
103+ "LL" ,
104+ "LD" ,
105+ "WL/DD" ,
106+ "WD" ,
107+ "WW" ,
108+ ]
109+
110+ for name , count in zip (names , ptnml ):
111+ print (f" { name :10} : { count } " )
112+
113+ print ()
114+ print (ptnml )
115+
116+
117+ if __name__ == "__main__" :
118+ main ()
0 commit comments