forked from niklasf/python-chess
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathperft.py
More file actions
executable file
·132 lines (108 loc) · 3.93 KB
/
Copy pathperft.py
File metadata and controls
executable file
·132 lines (108 loc) · 3.93 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
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Run perft test to check correctness and speed of the legal move generator.
"""
import chess
import chess.variant
import multiprocessing
import functools
import time
import argparse
import sys
def perft(depth, board):
if depth == 1:
return board.legal_moves.count()
elif depth > 1:
count = 0
for move in board.legal_moves:
board.push(move)
count += perft(depth - 1, board)
board.pop()
return count
else:
return 1
def parallel_perft(pool, depth, board):
if depth == 1:
return board.legal_moves.count()
elif depth > 1:
def successors(board):
for move in board.legal_moves:
board_after = board.copy(stack=False)
board_after.push(move)
yield board_after
return sum(pool.imap_unordered(functools.partial(perft, depth - 1), successors(board)))
else:
return 1
def sdiv(a, b):
try:
return a / b
except ZeroDivisionError:
return float("Inf")
def main(perft_file, VariantBoard, perft_f, max_depth, max_nodes):
current_id = None
board = VariantBoard(chess960=True)
column = 0
total_nodes = 0
start_time = time.time()
for line in perft_file:
# Skip comments and empty lines.
line = line.strip()
if not line or line.startswith("#") or line.startswith("%"):
continue
cmd, arg = line.split(None, 1)
if cmd == "id":
current_id = arg
elif cmd == "epd":
board.set_epd(arg)
elif cmd == "perft":
depth, nodes = map(int, arg.split(None, 1))
if (max_depth and depth > max_depth) or (max_nodes and nodes > max_nodes):
continue
perft_nodes = perft_f(depth, board)
if nodes != perft_nodes:
print()
print()
print(" !!! Failure in", current_id or "<no-name>")
print(" epd", board.epd())
print(" perft", depth, nodes, "(got %d instead)" % perft_nodes)
print()
print(board)
print()
for move in sorted(board.legal_moves, key=lambda m: m.uci()):
board.push(move)
print("%s: %d" % (move, perft_f(depth - 1, board)))
board.pop()
sys.exit(1)
total_nodes += perft_nodes
print(".", end="", flush=True)
column += 1
if column >= 40:
column = 0
sys.stdout.write(" nodes %d nps %.0f\n" % (total_nodes, sdiv(total_nodes, time.time() - start_time)))
else:
print()
print("Unknown command:", cmd, arg)
sys.exit(2)
if column:
sys.stdout.write(" nodes %d nps %.0f\n" % (total_nodes, sdiv(total_nodes, time.time() - start_time)))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("perft", nargs="+", type=argparse.FileType("r"),
help="Perft test suite(s)")
parser.add_argument("--max-depth", type=int, help="Skip deeper perft tests")
parser.add_argument("--max-nodes", type=int, default=1000000,
help="Skip larger perft tests. Defaults to 1000000")
parser.add_argument("-v", "--variant", default="standard",
help="Use a non-standard chess variant")
parser.add_argument("-t", "--threads", type=int, help="Number of threads")
args = parser.parse_args()
VariantBoard = chess.variant.find_variant(args.variant)
if args.threads == 1:
perft_f = perft
else:
pool = multiprocessing.Pool(args.threads)
perft_f = functools.partial(parallel_perft, pool)
for perft_file in args.perft:
print("###", perft_file.name)
main(perft_file, VariantBoard, perft_f, args.max_depth, args.max_nodes)