import re
import subprocess
import sys
from typing import List, Tuple, Any
STOCKFISH_MOVES = {}
RECHESS_ENGINE_MOVES = {}
def main():
stockfish_exe_path, rechess_exe_path, fen, depth = cli()
print(stockfish_exe_path)
print(rechess_exe_path)
print(fen)
print(depth)
print('Executing stockfish, this may take a while...')
stockfish_process = stockfish_init(stockfish_exe_path)
stockfish_perft(stockfish_process, fen, [], int(depth))
print('Executing rechess, this may take a while...')
rechess_process = rechess_init(rechess_exe_path)
rechess_perft(rechess_process, fen, [], int(depth))
print('-------------------------------------')
print('Missing in rechess engine:\n')
compare(STOCKFISH_MOVES, RECHESS_ENGINE_MOVES)
print('-------------------------------------\n')
print('Missing in stockfish:\n')
compare(RECHESS_ENGINE_MOVES, STOCKFISH_MOVES)
print('-------------------------------------')
stockfish_process.terminate()
rechess_process.terminate()
def cli() -> (str, str, str, str):
stockfish_exe_path = sys.argv[1]
rechess_exe_path = sys.argv[2]
fen = sys.argv[3]
depth = sys.argv[4]
return stockfish_exe_path, rechess_exe_path, fen, depth
def stockfish_perft(stockfish_process: Any, fen: str, moves: List[str], depth: int):
if depth == 0:
return add_to_moves_dict(STOCKFISH_MOVES, moves)
output = stockfish(stockfish_process, fen, to_moves_str(moves), depth)
for move in parse_output(output):
stockfish_perft(stockfish_process, fen, moves + [move[0]], depth - 1)
def rechess_perft(rechess_process: Any, fen: str, moves: List[str], depth: int):
if depth == 0:
return add_to_moves_dict(RECHESS_ENGINE_MOVES, moves)
output = rechess(rechess_process, fen, to_moves_str(moves), depth)
for move in parse_output(output):
rechess_perft(rechess_process, fen, moves + [move[0]], depth - 1)
def stockfish_init(stockfish_exe_path: str) -> Any:
return subprocess.Popen(stockfish_exe_path, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def stockfish(stockfish_process: Any, fen: str, moves: str, depth: int) -> str:
command = f'position fen {fen} moves {moves}\ngo perft {depth}\n'
stockfish_process.stdin.write(command)
stockfish_process.stdin.flush()
text = ''
while True:
output = stockfish_process.stdout.readline().strip()
text += output
if 'Nodes searched' in output:
break
return text
def rechess_init(rechess_exe_path: str) -> Any:
return subprocess.Popen([rechess_exe_path, 'chess', 'interactive'], universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def rechess(rechess_process: Any, fen: str, moves: str, depth: int) -> str:
command = f'perft depth {depth} position fen {fen} moves {moves}\n'
rechess_process.stdin.write(command)
rechess_process.stdin.flush()
text = ''
while True:
output = rechess_process.stdout.readline().strip()
text += output
if 'Nodes searched' in output:
break
return text
def parse_output(text: str) -> List[Tuple[str, int]]:
move_count_pattern = r"([a-h][1-8][a-h][1-8][qbkr]?): (\d+)"
move_count_pairs = re.findall(move_count_pattern, text)
moves = []
for move, count in move_count_pairs:
moves += [(move, count)]
return moves
def compare(a, b, path: str = ''):
for key in a.keys():
nested_path = f'{path} {key}'.strip()
if key in b:
compare(a[key], b[key], nested_path)
else:
print(f'[!] {nested_path}')
def to_moves_str(moves: List[str]) -> str:
return ' '.join(moves)
def add_to_moves_dict(dic, moves: List[str]):
d = dic
for move in moves[:-1]:
if move in d:
d = d[move]
else:
d = d.setdefault(move, {})
d[moves[-1]] = {}
if __name__ == '__main__':
main()