Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Copy to the same folder
- # executable file of Fairy Stockfish, https://github.com/fairy-stockfish/Fairy-Stockfish/releases
- # variants.ini containing your defined variant, https://github.com/fairy-stockfish/Fairy-Stockfish/blob/master/src/variants.ini
- # autoplayer.ini created from the commented out block below, change the variant name
- """
- [Engine]
- path = stockfish
- variant = chess
- threads = 1
- hash = 1024
- [Game]
- games = 100
- depth = 20
- limit_moves = 100
- equal_phase = 10
- draw_threshold = 10
- """
- # and autoplayer.py
- import subprocess
- import configparser
- import re
- import os
- import platform
- from datetime import datetime
- class UCIEngine:
- def __init__(self, name, common_config):
- self.name = name
- engine_path = common_config.get('path', 'stockfish')
- if '.' not in engine_path:
- if platform.system() == 'Windows':
- self.path = engine_path + '.exe'
- else:
- self.path = engine_path
- else:
- self.path = engine_path
- self.process = subprocess.Popen(
- self.path,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- universal_newlines=True,
- bufsize=1
- )
- self.send("uci")
- self.wait_for("uciok")
- variant = common_config.get('variant')
- if variant:
- self.send(f"load variants.ini")
- self.send(f"setoption name UCI_Variant value {variant}")
- for option, value in common_config.items():
- if option == 'threads':
- self.send(f"setoption name Threads value {value}")
- elif option == 'hash':
- self.send(f"setoption name Hash value {value}")
- self.send("setoption name Ponder value true")
- self.send("isready")
- self.wait_for("readyok")
- def send(self, command):
- self.process.stdin.write(command + "\n")
- self.process.stdin.flush()
- def wait_for(self, response):
- while True:
- line = self.process.stdout.readline().strip()
- if line == response:
- break
- def get_diagram(self, moves):
- position = "startpos" if not moves else f"startpos moves {' '.join(moves)}"
- self.send(f"position {position}")
- self.send("d")
- diagram_lines = []
- capture = False
- while True:
- line = self.process.stdout.readline().strip()
- if line.startswith("+---"):
- capture = True
- if capture:
- if line and len(line) > 0 and line[0].isalpha() and " " in line:
- line = " " + line
- diagram_lines.append(line)
- if not line or line.startswith("Fen:"):
- if diagram_lines and not diagram_lines[-1]:
- diagram_lines.pop()
- break
- self.send("isready")
- self.wait_for("readyok")
- return diagram_lines
- def get_move(self, moves, depth):
- position = "startpos" if not moves else f"startpos moves {' '.join(moves)}"
- self.send(f"position {position}")
- self.send(f"go depth {depth}")
- score = "?"
- ponder = None
- while True:
- line = self.process.stdout.readline().strip()
- if "currmove" in line or "lowerbound" in line or "upperbound" in line:
- continue
- if "score cp" in line:
- match = re.search(r'score cp (-?\d+)', line)
- if match:
- score = match.group(1)
- elif "score mate" in line:
- match = re.search(r'score mate (-?\d+)', line)
- if match:
- score = f"M{match.group(1)}"
- if line.startswith("bestmove"):
- parts = line.split()
- move = parts[1]
- if len(parts) > 3 and parts[2] == "ponder":
- ponder = parts[3]
- break
- return move, score, ponder
- def quit(self):
- self.send("quit")
- self.process.terminate()
- def play_game(engine1, engine2, game_config, game_number):
- print(f"\n=== GAME {game_number} ===\n")
- print("Starting position:")
- diagram = engine1.get_diagram([])
- for line in diagram:
- print(line)
- print()
- moves = []
- scores = []
- engines = [engine1, engine2]
- current = 0
- ponder_move = None
- opponent_last_score = None
- balanced_count = 0
- depth = game_config.get('depth', '25')
- limit_moves = int(game_config.get('limit_moves', '100'))
- equal_phase = int(game_config.get('equal_phase', '10'))
- draw_threshold = int(game_config.get('draw_threshold', '10'))
- while True:
- if ponder_move:
- if len(moves) > 0 and moves[-1] == ponder_move:
- engines[current].send("ponderhit")
- else:
- engines[current].send("stop")
- while True:
- line = engines[current].process.stdout.readline().strip()
- if line.startswith("bestmove"):
- break
- move, score, ponder = engines[current].get_move(moves, depth)
- if not move or move == "(none)":
- if opponent_last_score and str(opponent_last_score).startswith("M") and not str(opponent_last_score).startswith("M-"):
- winner = engines[1-current].name
- print(f"\n{winner} wins!")
- elif score and str(score).startswith("M-"):
- winner = engines[1-current].name
- print(f"\n{winner} wins!")
- elif opponent_last_score and str(opponent_last_score).startswith("M-"):
- winner = engines[current].name
- print(f"\n{winner} wins!")
- else:
- print("\nDraw!")
- winner = "Draw"
- if ponder_move:
- engines[1-current].send("stop")
- while True:
- line = engines[1-current].process.stdout.readline().strip()
- if line.startswith("bestmove"):
- break
- return winner, moves, scores
- moves.append(move)
- scores.append(score)
- try:
- score_value = int(score) if not score.startswith('M') else 999
- if abs(score_value) <= draw_threshold:
- balanced_count += 1
- else:
- balanced_count = 0
- except:
- balanced_count = 0
- if len(moves) >= limit_moves and balanced_count >= equal_phase:
- print(f"\nDraw by termination")
- if ponder_move:
- engines[1-current].send("stop")
- while True:
- line = engines[1-current].process.stdout.readline().strip()
- if line.startswith("bestmove"):
- break
- return "Draw", moves, scores
- print(f"Move {len(moves)}: {move} ({score})")
- print()
- diagram = engines[current].get_diagram(moves)
- for line in diagram:
- print(line)
- print()
- if ponder and ponder != "(none)":
- ponder_position = f"startpos moves {' '.join(moves)} {ponder}"
- engines[1-current].send(f"position {ponder_position}")
- engines[1-current].send("go ponder")
- ponder_move = ponder
- else:
- ponder_move = None
- opponent_last_score = score
- current = 1 - current
- return "Draw", moves, scores
- def save_game(game_number, moves, scores, result, filename):
- timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
- with open(filename, "a", encoding="utf-8") as f:
- f.write(f"Game {game_number} {timestamp}\n")
- moves_with_scores = []
- for i, move in enumerate(moves):
- if i < len(scores):
- moves_with_scores.append(f"{move}({scores[i]})")
- else:
- moves_with_scores.append(move)
- f.write(f"{' '.join(moves_with_scores)}\n")
- f.write(f"{result}\n")
- f.write("\n")
- def main():
- config = configparser.ConfigParser()
- config.read('Autoplayer.ini')
- common_config = dict(config['Engine'])
- game_config = dict(config['Game'])
- num_games = int(game_config.get('games', '1'))
- variant = common_config.get('variant', 'games')
- output_file = f"{variant}.txt"
- engine1 = UCIEngine("White", common_config)
- engine2 = UCIEngine("Black", common_config)
- results = {
- engine1.name: 0,
- engine2.name: 0,
- "Draw": 0
- }
- for game_num in range(1, num_games + 1):
- result, moves, scores = play_game(engine1, engine2, game_config, game_num)
- save_game(game_num, moves, scores, result, output_file)
- if result in [engine1.name, engine2.name]:
- results[result] += 1
- else:
- results["Draw"] += 1
- print(f"\nScore after {game_num} games:")
- print(f"{engine1.name}: {results[engine1.name]} - {engine2.name}: {results[engine2.name]} - Draws: {results['Draw']}")
- engine1.quit()
- engine2.quit()
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment