Source code for miniosl.usi_process

"""run external usi engine"""
import subprocess
import re
import logging
import copy
import weakref


info_string = re.compile(r'^info\s+string.*$')
info_regexp = re.compile(r'^info.*\s+score\s+cp\s+([0-9-+]+).*\s+pv\s+((\s?[A-Za-z0-9+*]+)*)\s*$')
depth_regexp = re.compile(r'^info.*\s+depth\s+([0-9]+)')
nodes_regexp = re.compile(r'^info.*\s+nodes\s+([0-9]+)')
bestmove_regexp = re.compile(r'^bestmove\s+([A-Za-z0-9+*]+)\s*(ponder\s.*)*$')
idname_regexp = re.compile(r'^id\s+name\s+(.*)\s*$')


def close_session(proc):
    if not proc:
        return False
    logging.debug('closing process')
    proc.stdin.write("quit\n")
    proc.stdin.close()
    if proc.wait() != 0:
        logging.error("close error")
        return False
    return True


[docs]class UsiProcess: """Proxy for usi process create process, initilaze its options and wait until ``readyok`` :param path_and_args: list of path and argments """ debug = False def __init__(self, path_and_args: list[str], setoptions: list[str] = [ "setoption name Threads value 1", "setoption name USI_Hash value 16", ], cwd: str | None = None): self.path_and_args = copy.copy(path_and_args) self.name = None self.usi = subprocess.Popen(path_and_args, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd, close_fds=True, universal_newlines=True) self.writeline("usi") line = self.readline() options_avail = [] while line != "usiok": if line.startswith("option name"): options_avail.append(line) else: match = re.match(idname_regexp, line) if match: self.name = match.group(1) line = self.readline() for line in setoptions: self.writeline(line) self.writeline("isready") ready = self.readline() while re.match(info_string, ready): ready = self.readline() if not ready.startswith("readyok"): err = f'readyok != {ready}' logging.critical(err) raise ValueError(err) # for graceful exit # https://docs.python.org/3/library/weakref.html#finalizer-objects self._finalizer = weakref.finalize(self, close_session, self.usi) def readline(self) -> str: line = self.usi.stdout.readline().rstrip() if self.debug: logging.debug(f'< {line}') return line def writeline(self, line) -> None: if self.debug: logging.debug(f'> {line}') self.usi.stdin.write(line+"\n")
[docs] def close(self) -> bool: """finish process""" ret = self._finalizer().alive() self._finalizer() return ret
[docs] def search(self, position: str, limit: str) -> dict: """search: give position with go commands, and wait ``bestmove`` Returns hash contains "info" :param position: e.g., ``'startpos'`` :param limit: e.g., ``'byoyomi 1000'`` """ self.writeline(f'position {position}') self.writeline(f"go {limit}") result = {} last_depth = None last_node_count = None best_move_identified = False while not best_move_identified: line = self.readline() match_depth = re.match(depth_regexp, line) if match_depth: last_depth = match_depth.group(1) match_nodes = re.match(nodes_regexp, line) if match_nodes: last_node_count = match_nodes.group(1) match_info = False if 'rep' in line else re.match(info_regexp, line) if match_info: score = match_info.group(1) pv = match_info.group(2) result['cp'] = score result['pv'] = pv if last_depth: result['d'] = last_depth if last_node_count: result['n'] = last_node_count match_bestmove = re.match(bestmove_regexp, line) if match_bestmove: result['move'] = match_bestmove.group(1) best_move_identified = True return result