def run (no_debug, no_idle_add_debug, no_thread_debug, log_viewer, chess_file, ics_host, ics_port): # Start logging if log_viewer: log.logger.addHandler(GLogHandler(logemitter)) log.logger.setLevel(logging.WARNING if no_debug is True else logging.DEBUG) oldlogs = [l for l in os.listdir(getUserDataPrefix()) if l.endswith(".log")] conf.set("max_log_files", conf.get("max_log_files", 10)) if len(oldlogs) >= conf.get("max_log_files", 10): oldlogs.sort() try: os.remove(addUserDataPrefix(oldlogs[0])) except OSError as e: pass signal.signal(signal.SIGINT, Gtk.main_quit) def cleanup (): SubProcess.finishAllSubprocesses() atexit.register(cleanup) pychess = PyChess(log_viewer, chess_file) idle_add.debug = not no_idle_add_debug sys.stdout = LogPipe(sys.stdout, "stdout") sys.stderr = LogPipe(sys.stderr, "stdout") log.info("PyChess %s %s rev. %s %s started" % (VERSION_NAME, VERSION, pychess.hg_rev, pychess.hg_date)) log.info("Command line args: '%s'" % chess_file) if not no_thread_debug: start_thread_dump() if ics_host: ICLogon.host = ics_host if ics_port: ICLogon.port = ics_port Gtk.main()
def do_startup(self): Gtk.Application.do_startup(self) if self.purge_recent: items = recentManager.get_items() for item in items: uri = item.get_uri() if item.get_application_info("pychess"): recentManager.remove_item(uri) self.git_rev = "" self.initGlade(self.log_viewer) self.addPerspectives() self.handleArgs(self.chess_file) checkversion() self.loaded_cids = {} self.saved_cids = {} self.terminated_cids = {} log.info("PyChess %s %s git %s" % (VERSION_NAME, VERSION, self.git_rev)) log.info("Command line args: '%s'" % self.chess_file) log.info("Platform: %s" % platform.platform()) log.info("Python version: %s.%s.%s" % sys.version_info[0:3]) log.info("Pyglib version: %s.%s.%s" % GLib.pyglib_version)
def __init__(self, handle, progressbar): ChessFile.__init__(self, handle) self.handle = handle self.progressbar = progressbar self.pgn_is_string = isinstance(handle, StringIO) if self.pgn_is_string: self.games = [self.load_game_tags(), ] else: self.skip = 0 self.limit = 100 self.order_col = game.c.offset self.is_desc = False self.reset_last_seen() # filter expressions to .sqlite .bin .scout self.tag_query = None self.fen = None self.scout_query = None self.init_tag_database() self.scoutfish = None self.init_scoutfish() self.chess_db = None self.init_chess_db() self.games, self.offs_ply = self.get_records(0) log.info("%s contains %s game(s)" % (self.path, self.count), extra={"task": "SQL"})
def __init__(self): GObject.GObject.__init__(self) self.engines = [] self.jsonpath = addUserConfigPrefix("engines.json") try: self._engines = json.load(open(self.jsonpath)) except ValueError as err: log.warning( "engineNest: Couldn\'t read engines.json, renamed it to .bak\n%s\n%s" % (self.jsonpath, err)) os.rename(self.jsonpath, self.jsonpath + ".bak") self._engines = deepcopy(backup) except IOError as err: log.info( "engineNest: Couldn\'t open engines.json, creating a new.\n%s" % err) self._engines = deepcopy(backup) # Try to detect engines shipping .eng files on Linux (suggested by HGM on talkcess.com forum) for protocol in ("xboard", "uci"): for path in ("/usr/local/share/games/plugins", "/usr/share/games/plugins"): path = os.path.join(path, protocol) if os.path.isdir(path): for entry in os.listdir(path): ext = os.path.splitext(entry)[1] if ext == ".eng": with open(os.path.join(path, entry)) as file_handle: plugin_spec = file_handle.readline().strip() if not plugin_spec.startswith("plugin spec"): continue engine_command = file_handle.readline().strip() supported_variants = file_handle.readline( ).strip() if not supported_variants.startswith("chess"): continue new_engine = {} if engine_command.startswith( "cd " ) and engine_command.find(";") > 0: parts = engine_command.split(";") working_directory = parts[0][3:] engine_command = parts[1] new_engine[ "workingDirectory"] = working_directory find = False for engine in self._engines: if engine["name"] == engine_command: find = True break if not find: new_engine["protocol"] = protocol new_engine["name"] = engine_command self._engines.append(new_engine)
def zero_reached(self, timemodel, color): if conf.get("autoCallFlag") and \ self.gamemodel.status == RUNNING and \ timemodel.getPlayerTime(1 - self.color) <= 0: log.info('Automatically sending flag call on behalf of player %s.' % self.name) self.emit("offer", Offer(FLAG_CALL))
def onGameStarted(self, gamemodel): if gamemodel.examined: if gamemodel.players[0].name == gamemodel.connection.username: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] else: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] elif gamemodel.isObservationGame(): # no local player but enable chat to send/receive whisper/kibitz pass elif gamemodel.players[0].__type__ == LOCAL: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] if gamemodel.players[1].__type__ == LOCAL: log.warning("Chatpanel loaded with two local players") elif gamemodel.players[1].__type__ == LOCAL: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] else: log.info("Chatpanel loaded with no local players") self.chatView.hide() if isinstance(gamemodel, ICGameModel): if gamemodel.connection.ICC: gamemodel.connection.client.run_command("set-2 %s 1" % DG_PLAYERS_IN_MY_GAME) else: allob = 'allob ' + str(gamemodel.ficsgame.gameno) gamemodel.connection.client.run_command(allob) if hasattr(self, "player") and not gamemodel.examined: self.player_cid = self.player.connect("messageReceived", self.onMessageReieved) self.chatView.enable()
def run (cls, uri=None): cls._ensureReady() if cls.widgets["newgamedialog"].props.visible: cls.widgets["newgamedialog"].present() return if not uri: res = ionest.opendialog.run() ionest.opendialog.hide() if res != Gtk.ResponseType.ACCEPT: return else: if not uri[uri.rfind(".")+1:] in ionest.enddir: log.info("Ignoring strange file: %s" % uri) return cls.loadSidePanel.set_filename(uri) cls.filechooserbutton.emit("file-activated") cls._hideOthers() cls.widgets["newgamedialog"].set_title(_("Open Game")) cls.widgets["loadsidepanel"].show() def _validate(gamemodel): return True def _callback (gamemodel, p0, p1): if not cls.loadSidePanel.isEmpty(): uri = cls.loadSidePanel.get_filename() loader = ionest.enddir[uri[uri.rfind(".")+1:]] position = cls.loadSidePanel.getPosition() gameno = cls.loadSidePanel.getGameno() ionest.generalStart(gamemodel, p0, p1, (uri, loader, gameno, position)) else: ionest.generalStart(gamemodel, p0, p1) cls._generalRun(_callback, _validate)
def onGameStarted(self, gamemodel): if gamemodel.examined: if gamemodel.players[0].name == gamemodel.connection.username: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] else: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] elif gamemodel.isObservationGame(): # no local player but enable chat to send/receive whisper/kibitz pass elif gamemodel.players[0].__type__ == LOCAL: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] if gamemodel.players[1].__type__ == LOCAL: log.warning("Chatpanel loaded with two local players") elif gamemodel.players[1].__type__ == LOCAL: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] else: log.info("Chatpanel loaded with no local players") self.chatView.hide() if isinstance(gamemodel, ICGameModel): if gamemodel.connection.ICC: gamemodel.connection.client.run_command("set-2 %s 1" % DG_PLAYERS_IN_MY_GAME) else: allob = 'allob ' + str(gamemodel.ficsgame.gameno) gamemodel.connection.client.run_command(allob) if hasattr(self, "player") and not gamemodel.examined and self.player_cid is None: self.player_cid = self.player.connect("messageReceived", self.onMessageRecieved) self.chatView.enable()
def __init__(self, handle, progressbar): ChessFile.__init__(self, handle) self.handle = handle self.progressbar = progressbar self.pgn_is_string = isinstance(handle, StringIO) if self.pgn_is_string: self.games = [ self.load_game_tags(), ] else: self.skip = 0 self.limit = 100 self.last_seen_offs = [-1] # filter expressions to .sqlite .bin .scout self.text = "" self.fen = "" self.query = {} self.init_tag_database() self.scoutfish = None self.init_scoutfish() self.chess_db = None self.init_chess_db() self.games, self.offs_ply = self.get_records(0) log.info("%s contains %s game(s)" % (self.path, self.count), extra={"task": "SQL"})
def zero_reached(self, timemodel, color): if conf.get('autoCallFlag', False) and \ self.gamemodel.status == RUNNING and \ timemodel.getPlayerTime(1 - self.color) <= 0: log.info('Automatically sending flag call on behalf of player %s.' % self.name) self.emit("offer", Offer(FLAG_CALL))
def zero_reached(self, timemodel, color): if conf.get('autoCallFlag', False) and self.players[1 - color].__type__ == ARTIFICIAL: if self.status == RUNNING and timemodel.getPlayerTime(color) <= 0: log.info( 'Automatically sending flag call on behalf of player %s.' % self.players[1 - color].name) self.players[1 - color].emit("offer", Offer(FLAG_CALL))
def zero_reached(self, timemodel, color): if conf.get('autoCallFlag'): if self.status == RUNNING and timemodel.getPlayerTime(color) <= 0: log.info( 'Automatically sending flag call on behalf of player %s.' % self.players[1 - color].name) self.players[1 - color].emit("offer", Offer(FLAG_CALL))
def __init__(self, handle, progressbar=None): ChessFile.__init__(self, handle) self.handle = handle self.progressbar = progressbar self.pgn_is_string = isinstance(handle, StringIO) if self.pgn_is_string: self.games = [ self.load_game_tags(), ] else: self.skip = 0 self.limit = 100 self.order_col = game.c.offset self.is_desc = False self.reset_last_seen() # filter expressions to .sqlite .bin .scout self.tag_query = None self.fen = None self.scout_query = None self.scoutfish = None self.chess_db = None self.sqlite_path = os.path.splitext(self.path)[0] + '.sqlite' self.engine = dbmodel.get_engine(self.sqlite_path) self.tag_database = TagDatabase(self.engine) self.games, self.offs_ply = self.get_records(0) log.info("%s contains %s game(s)" % (self.path, self.count), extra={"task": "SQL"})
def start(self): try: if not self.isConnected(): yield from self._connect() while self.isConnected(): yield from self.client.parse() except CancelledError: pass except Exception as err: exc_type, exc_value, exc_traceback = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_traceback) log.info("FICSConnection.run: %s" % repr(err), extra={"task": (self.host, "raw")}) self.close() if isinstance( err, ( IOError, LogOnException, EOFError, socket.error, socket.gaierror, socket.herror, ), ): self.emit("error", err) else: raise finally: if isinstance(self, FICSMainConnection): self.emit("disconnected")
def draw(self, game): if not game.opponent.adjournment: log.warning("AdjournManager.draw: no adjourned game vs %s" % game.opponent) return log.info("AdjournManager.draw: offering sdraw for adjourned game=%s" % game) self.connection.client.run_command("sdraw %s" % game.opponent.name)
def resume(self, game): if not game.opponent.adjournment: log.warning("AdjournManager.resume: no adjourned game vs %s" % game.opponent) return log.info("AdjournManager.resume: offering resume for adjourned game=%s" % game) self.connection.client.run_command("match %s" % game.opponent.name)
def abort(self, game): if not game.opponent.adjournment: log.warning("AdjournManager.abort: no adjourned game vs %s" % game.opponent) return log.info("AdjournManager.abort: offering sabort for adjourned game=%s" % game) self.connection.client.run_command("sabort %s" % game.opponent.name)
def resign (self, game): """ This is (and draw and abort) are possible even when one's opponent is not logged on """ if not game.opponent.adjournment: log.warning("AdjournManager.resign: no adjourned game vs %s" % game.opponent) return log.info("AdjournManager.resign: resigning adjourned game=%s" % game) self.connection.client.run_command("resign %s" % game.opponent.name)
def write(self, string): logstr = "*" * len(string) if self.sensitive else string self.sensitive = False log.info(logstr, extra={"task": (self.name, "raw")}) if self.timeseal: self.transport.write(self.protocol.encode(bytearray(string, "utf-8")) + b"\n") else: self.transport.write(string.encode() + b"\n")
def resign(self, game): """ This is (and draw and abort) are possible even when one's opponent is not logged on """ if not game.opponent.adjournment: log.warning("AdjournManager.resign: no adjourned game vs %s" % game.opponent) return log.info("AdjournManager.resign: resigning adjourned game=%s" % game) self.connection.client.run_command("resign %s" % game.opponent.name)
def __init__(self): GObject.GObject.__init__(self) self.engines = [] self.jsonpath = addUserConfigPrefix("engines.json") try: self._engines = json.load(open(self.jsonpath)) except ValueError as err: log.warning( "engineNest: Couldn\'t read engines.json, renamed it to .bak\n%s\n%s" % (self.jsonpath, err)) os.rename(self.jsonpath, self.jsonpath + ".bak") self._engines = deepcopy(backup) except IOError as err: log.info( "engineNest: Couldn\'t open engines.json, creating a new.\n%s" % err) self._engines = deepcopy(backup) # Try to detect engines shipping .eng files on Linux (suggested by HGM on talkcess.com forum) for protocol in ("xboard", "uci"): for path in ("/usr/local/share/games/plugins", "/usr/share/games/plugins"): path = os.path.join(path, protocol) if os.path.isdir(path): for entry in os.listdir(path): ext = os.path.splitext(entry)[1] if ext == ".eng": with open(os.path.join(path, entry)) as file_handle: plugin_spec = file_handle.readline().strip() if not plugin_spec.startswith("plugin spec"): continue engine_command = file_handle.readline().strip() supported_variants = file_handle.readline().strip() if not supported_variants.startswith("chess"): continue new_engine = {} if engine_command.startswith("cd ") and engine_command.find(";") > 0: parts = engine_command.split(";") working_directory = parts[0][3:] engine_command = parts[1] new_engine["workingDirectory"] = working_directory find = False for engine in self._engines: if engine["name"] == engine_command: find = True break if not find: new_engine["protocol"] = protocol new_engine["name"] = engine_command self._engines.append(new_engine)
def __init__(self): self.libgtb = None self.initialized = False # Get a list of files in the tablebase folder. configuredTbPath = conf.get("egtb_path", "") tbPath = configuredTbPath or getDataPrefix() try: tbPathContents = os.listdir(tbPath) except OSError as e: if configuredTbPath: log.warning("Unable to open Gaviota TB folder: %s" % repr(e)) return # Find files named *.gtb.cp# and pick the most common "#". # (This is the compression scheme; the library currently only uses one at a time.) schemeCount = [0] * 10 for filename in tbPathContents: match = re.search("\.gtb\.cp(\d)$", filename) if match: schemeCount[int(match.group(1))] += 1 compressionScheme = max(zip(schemeCount, range(10))) if compressionScheme[0] == 0: if configuredTbPath: log.warning("Could not find any Gaviota TB files in %s" % configuredTbPath) return compressionScheme = compressionScheme[1] # Locate and load the library. if not self._loadLibrary(): return self._setupFunctionPrototypes() self.pathList = self.tbpaths_init() self.pathList = self.tbpaths_add(self.pathList, tbPath.encode()) initInfo = self.tb_init(True, compressionScheme, self.pathList) self.initialized = (self.tb_is_initialized() != 0) if not self.initialized: log.warning(initInfo or "Failed to initialize Gaviota EGTB library") self.pathList = self.tbpaths_done(self.pathList) return elif initInfo: log.info(initInfo) # TODO: Set up a WDL cache area once the engine can use it. self.initialized &= self.tbcache_init(4 * 1024 * 1024, 0) if not self.initialized: log.warning("Failed to initialize Gaviota EGTB cache") self.tb_done() self.pathList = self.tbpaths_done(self.pathList) return self.availability = self.tb_availability()
def run(no_debug, idle_add_debug, thread_debug, log_viewer, purge_recent, chess_file, ics_host, ics_port): # Start logging if log_viewer: log.logger.addHandler(GLogHandler(logemitter)) log.logger.setLevel(logging.WARNING if no_debug is True else logging.DEBUG) oldlogs = [ l for l in os.listdir(getUserDataPrefix()) if l.endswith(".log") ] conf.set("max_log_files", conf.get("max_log_files", 10)) oldlogs.sort() l = len(oldlogs) while l > conf.get("max_log_files", 10): try: os.remove(addUserDataPrefix(oldlogs[0])) del oldlogs[0] except OSError: pass l -= 1 if purge_recent: items = recentManager.get_items() for item in items: uri = item.get_uri() if item.get_application_info("pychess"): recentManager.remove_item(uri) signal.signal(signal.SIGINT, Gtk.main_quit) signal.signal(signal.SIGTERM, Gtk.main_quit) def cleanup(): ICLogon.stop() SubProcess.finishAllSubprocesses() atexit.register(cleanup) pychess = PyChess(log_viewer, chess_file) idle_add.debug = idle_add_debug sys.stdout = LogPipe(sys.stdout, "stdout") sys.stderr = LogPipe(sys.stderr, "stdout") log.info("PyChess %s %s git %s" % (VERSION_NAME, VERSION, pychess.git_rev)) log.info("Command line args: '%s'" % chess_file) log.info("Platform: %s" % platform.platform()) log.info("Python version: %s.%s.%s" % sys.version_info[0:3]) log.info("Pyglib version: %s.%s.%s" % GLib.pyglib_version) if thread_debug: start_thread_dump() if ics_host: ICLogon.host = ics_host if ics_port: ICLogon.port = ics_port Gtk.main()
def run(no_debug, idle_add_debug, thread_debug, log_viewer, purge_recent, chess_file, ics_host, ics_port): # Start logging if log_viewer: log.logger.addHandler(GLogHandler(logemitter)) log.logger.setLevel(logging.WARNING if no_debug is True else logging.DEBUG) oldlogs = [l for l in os.listdir(getUserDataPrefix()) if l.endswith(".log")] conf.set("max_log_files", conf.get("max_log_files", 10)) oldlogs.sort() l = len(oldlogs) while l > conf.get("max_log_files", 10): try: os.remove(addUserDataPrefix(oldlogs[0])) del oldlogs[0] except OSError: pass l -= 1 if purge_recent: items = recentManager.get_items() for item in items: uri = item.get_uri() if item.get_application_info("pychess"): recentManager.remove_item(uri) signal.signal(signal.SIGINT, Gtk.main_quit) signal.signal(signal.SIGTERM, Gtk.main_quit) def cleanup(): ICLogon.stop() SubProcess.finishAllSubprocesses() atexit.register(cleanup) pychess = PyChess(log_viewer, chess_file) idle_add.debug = idle_add_debug sys.stdout = LogPipe(sys.stdout, "stdout") sys.stderr = LogPipe(sys.stderr, "stdout") log.info("PyChess %s %s git %s" % (VERSION_NAME, VERSION, pychess.git_rev)) log.info("Command line args: '%s'" % chess_file) log.info("Platform: %s" % platform.platform()) log.info("Python version: %s.%s.%s" % sys.version_info[0:3]) log.info("Pyglib version: %s.%s.%s" % GLib.pyglib_version) if thread_debug: start_thread_dump() if ics_host: ICLogon.host = ics_host if ics_port: ICLogon.port = ics_port Gtk.main()
def __init__(self, path, games): PGNFile.__init__(self, path, games) self.path = path self.engine = dbmodel.get_engine(path) self.DB_MAXINT_SHIFT = get_maxint_shift(self.engine) self.cols = [ game.c.id.label("Id"), pl1.c.name.label('White'), pl2.c.name.label('Black'), game.c.result.label('Result'), event.c.name.label('Event'), site.c.name.label('Site'), game.c.round.label('Round'), game.c.date_year.label('Year'), game.c.date_month.label('Month'), game.c.date_day.label('Day'), game.c.white_elo.label('WhiteElo'), game.c.black_elo.label('BlackElo'), game.c.ply_count.label('PlyCount'), game.c.eco.label('ECO'), game.c.time_control.label('TimeControl'), game.c.fen.label('Board'), game.c.fen.label('FEN'), game.c.variant.label('Variant'), annotator.c.name.label('Annotator') ] self.from_obj = [ game.outerjoin(pl1, game.c.white_id == pl1.c.id).outerjoin( pl2, game.c.black_id == pl2.c.id).outerjoin( event, game.c.event_id == event.c.id).outerjoin( site, game.c.site_id == site.c.id).outerjoin( annotator, game.c.annotator_id == annotator.c.id) ] self.count = self.engine.execute(count_games).scalar() log.info("%s contains %s game(s)" % (self.path, self.count), extra={"task": "SQL"}) self.update_count_stats() self.select = select(self.cols, from_obj=self.from_obj) self.colnames = self.engine.execute(self.select).keys() self.query = self.select self.orderby = None self.where_tags = None self.where_bitboards = None self.ply = None
def write(self, str): self.writebuf += bytearray(str, "utf-8") if b"\n" not in self.writebuf: return if not self.connected: return i = self.writebuf.rfind(b"\n") str = self.writebuf[:i] self.writebuf = self.writebuf[i+1:] logstr = "*"*len(str) if self.sensitive else str log.info(logstr, extra={"task": (self.name, "raw")}) str = self.encode(str) self.sock.send(str+b"\n")
def write(self, str): self.writebuf += bytearray(str, "utf-8") if b"\n" not in self.writebuf: return if not self.connected: return i = self.writebuf.rfind(b"\n") str = self.writebuf[:i] self.writebuf = self.writebuf[i + 1:] logstr = "*" * len(str) if self.sensitive else str log.info(logstr, extra={"task": (self.name, "raw")}) str = self.encode(str) self.sock.send(str + b"\n")
def __init__(self): GObject.GObject.__init__(self) self.engines = [] self.jsonpath = addUserConfigPrefix("engines.json") try: self._engines = json.load(open(self.jsonpath)) except ValueError as e: log.warning( "engineNest: Couldn\'t read engines.json, renamed it to .bak\n%s" % (self.jsonpath, e)) os.rename(self.jsonpath, self.jsonpath + ".bak") self._engines = deepcopy(backup) except IOError as e: log.info( "engineNest: Couldn\'t open engines.json, creating a new.\n%s" % e) self._engines = deepcopy(backup) for protocol in ("xboard", "uci"): for path in ("/usr/local/share/games/plugins", "/usr/share/games/plugins"): path = os.path.join(path, protocol) if os.path.isdir(path): for entry in os.listdir(path): name, ext = os.path.splitext(entry) if ext == ".eng": with open(os.path.join(path, entry)) as f: plugin_spec = f.readline().strip() engine_command = f.readline().strip() new_engine = {} if engine_command.startswith( "cd " ) and engine_command.find(";") > 0: parts = engine_command.split(";") working_directory = parts[0][3:] engine_command = parts[1] new_engine[ "workingDirectory"] = working_directory find = False for engine in self._engines: if engine["name"] == engine_command: find = True break if not find: new_engine["protocol"] = protocol new_engine["name"] = engine_command self._engines.append(new_engine)
def run(self): try: try: if not self.isConnected(): self._connect() while self.isConnected(): self.client.parse() except Exception as e: log.info("FICSConnection.run: %s" % repr(e), extra={"task": (self.host, "raw")}) self.close() if isinstance(e, (IOError, LogOnException, EOFError, socket.error, socket.gaierror, socket.herror)): self.emit("error", e) else: raise finally: self.emit("disconnected")
def end(self, status, reason): if self.status not in UNFINISHED_STATES: log.info( "GameModel.end: Can't end a game that's already ended: %s %s" % (status, reason)) return if self.status not in (WAITING_TO_START, PAUSED, RUNNING): self.needsSave = True log.debug("GameModel.end: players=%s, self.ply=%s: Ending a game with status %d for reason %d" % ( repr(self.players), str(self.ply), status, reason)) self.status = status self.reason = reason self.emit("game_ended", reason) self.__pause()
def onGameStarted(self, gamemodel): if gamemodel.players[0].__type__ == LOCAL: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] if gamemodel.players[1].__type__ == LOCAL: log.warning("Chatpanel loaded with two local players") elif gamemodel.players[1].__type__ == LOCAL: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] else: log.info("Chatpanel loaded with no local players") self.chatView.hide() if hasattr(self, "player"): self.player.connect("messageRecieved", self.onMessageReieved) self.chatView.enable()
def onGameStarted (self, gamemodel): if gamemodel.players[0].__type__ == LOCAL: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] if gamemodel.players[1].__type__ == LOCAL: log.warning("Chatpanel loaded with two local players") elif gamemodel.players[1].__type__ == LOCAL: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] else: log.info("Chatpanel loaded with no local players") self.chatView.hide() if hasattr(self, "player"): self.player.connect("messageRecieved", self.onMessageReieved) self.chatView.enable()
def run(no_debug, no_idle_add_debug, no_thread_debug, log_viewer, chess_file, ics_host, ics_port): # Start logging if log_viewer: log.logger.addHandler(GLogHandler(logemitter)) log.logger.setLevel(logging.WARNING if no_debug is True else logging.DEBUG) oldlogs = [ l for l in os.listdir(getUserDataPrefix()) if l.endswith(".log") ] conf.set("max_log_files", conf.get("max_log_files", 10)) if len(oldlogs) >= conf.get("max_log_files", 10): oldlogs.sort() try: os.remove(addUserDataPrefix(oldlogs[0])) except OSError as e: pass signal.signal(signal.SIGINT, Gtk.main_quit) def cleanup(): SubProcess.finishAllSubprocesses() atexit.register(cleanup) pychess = PyChess(log_viewer, chess_file) idle_add.debug = not no_idle_add_debug sys.stdout = LogPipe(sys.stdout, "stdout") sys.stderr = LogPipe(sys.stderr, "stdout") log.info("PyChess %s %s git %s" % (VERSION_NAME, VERSION, pychess.git_rev)) log.info("Command line args: '%s'" % chess_file) log.info("Platform: %s" % platform.platform()) log.info("Python version: %s.%s.%s" % sys.version_info[0:3]) log.info("Pyglib version: %s.%s.%s" % GLib.pyglib_version) if not no_thread_debug: start_thread_dump() if ics_host: ICLogon.host = ics_host if ics_port: ICLogon.port = ics_port Gtk.main()
def write(self, string): self.writebuf += bytearray(string, "utf-8") if b"\n" not in self.writebuf: return if not self.connected: return i = self.writebuf.rfind(b"\n") string = self.writebuf[:i] self.writebuf = self.writebuf[i + 1:] logstr = "*" * len(string) if self.sensitive else string self.sensitive = False log.info(logstr, extra={"task": (self.name, "raw")}) if not self.ICC: string = self.encode(string) try: self.sock.send(string + b"\n") except: pass
def onGameStarted (self, gamemodel): if gamemodel.isObservationGame() or gamemodel.examined: # no local player but enable chat to send/receive whisper/kibitz pass elif gamemodel.players[0].__type__ == LOCAL: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] if gamemodel.players[1].__type__ == LOCAL: log.warning("Chatpanel loaded with two local players") elif gamemodel.players[1].__type__ == LOCAL: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] else: log.info("Chatpanel loaded with no local players") self.chatView.hide() if hasattr(self, "player"): self.player.connect("messageReceived", self.onMessageReieved) self.chatView.enable()
def run(no_debug, no_glock_debug, no_thread_debug, log_viewer, chess_file, ics_host, ics_port): # Start logging log.logger.setLevel(logging.WARNING if no_debug is True else logging.DEBUG) if log_viewer: Log.set_gui_log_emitter() oldlogs = [ l for l in os.listdir(getUserDataPrefix()) if l.endswith(".log") ] conf.set("max_log_files", conf.get("max_log_files", 10)) if len(oldlogs) >= conf.get("max_log_files", 10): oldlogs.sort() try: os.remove(addUserDataPrefix(oldlogs[0])) except OSError as e: pass signal.signal(signal.SIGINT, Gtk.main_quit) def cleanup(): SubProcess.finishAllSubprocesses() atexit.register(cleanup) Gdk.threads_init() GObject.threads_init() Gdk.threads_enter() pychess = PyChess(log_viewer, chess_file) glock.debug = not no_glock_debug log.info("PyChess %s %s rev. %s %s started" % (VERSION_NAME, VERSION, pychess.hg_rev, pychess.hg_date)) log.info("Command line args: '%s'" % chess_file) if not no_thread_debug: start_thread_dump() if ics_host: ICLogon.host = ics_host if ics_port: ICLogon.port = ics_port Gtk.main() Gdk.threads_leave()
def onGameStarted(self, gamemodel): if gamemodel.isObservationGame() or gamemodel.examined: # no local player but enable chat to send/receive whisper/kibitz allob = 'allob ' + str(gamemodel.ficsgame.gameno) gamemodel.connection.client.run_command(allob) elif gamemodel.players[0].__type__ == LOCAL: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] if gamemodel.players[1].__type__ == LOCAL: log.warning("Chatpanel loaded with two local players") elif gamemodel.players[1].__type__ == LOCAL: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] else: log.info("Chatpanel loaded with no local players") self.chatView.hide() if hasattr(self, "player"): self.player.connect("messageReceived", self.onMessageReieved) self.chatView.enable()
def __init__(self): GObject.GObject.__init__(self) self.engines = [] self.jsonpath = addUserConfigPrefix("engines.json") try: self._engines = json.load(open(self.jsonpath)) except ValueError as e: log.warning("engineNest: Couldn't read engines.json, renamed it to .bak\n%s" % (self.jsonpath, e)) os.rename(self.jsonpath, self.jsonpath + ".bak") self._engines = deepcopy(backup) except IOError as e: log.info("engineNest: Couldn't open engines.json, creating a new.\n%s" % e) self._engines = deepcopy(backup) for protocol in ("xboard", "uci"): for path in ("/usr/local/share/games/plugins", "/usr/share/games/plugins"): path = os.path.join(path, protocol) if os.path.isdir(path): for entry in os.listdir(path): name, ext = os.path.splitext(entry) if ext == ".eng": with open(os.path.join(path, entry)) as f: plugin_spec = f.readline().strip() engine_command = f.readline().strip() new_engine = {} if engine_command.startswith("cd ") and engine_command.find(";") > 0: parts = engine_command.split(";") working_directory = parts[0][3:] engine_command = parts[1] new_engine["workingDirectory"] = working_directory find = False for engine in self._engines: if engine["name"] == engine_command: find = True break if not find: new_engine["protocol"] = protocol new_engine["name"] = engine_command self._engines.append(new_engine)
def start(self): try: try: if not self.isConnected(): yield from self._connect() while self.isConnected(): yield from self.client.parse() except Exception as err: exc_type, exc_value, exc_traceback = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_traceback) log.info("FICSConnection.run: %s" % repr(err), extra={"task": (self.host, "raw")}) self.close() if isinstance(err, (IOError, LogOnException, EOFError, socket.error, socket.gaierror, socket.herror)): self.emit("error", err) else: raise finally: if isinstance(self, FICSMainConnection): self.emit("disconnected")
def run(cls, uri=None): cls._ensureReady() if cls.widgets["newgamedialog"].props.visible: cls.widgets["newgamedialog"].present() return if not uri: res = ionest.opendialog.run() ionest.opendialog.hide() if res != Gtk.ResponseType.ACCEPT: return else: if not uri[uri.rfind(".") + 1:] in ionest.enddir: log.info("Ignoring strange file: %s" % uri) return cls.loadSidePanel.set_filename(uri) cls.filechooserbutton.emit("file-activated") cls._hideOthers() cls.widgets["newgamedialog"].set_title(_("Open Game")) cls.widgets["loadsidepanel"].show() def _validate(gamemodel): return True def _callback(gamemodel, p0, p1): if not cls.loadSidePanel.isEmpty(): uri = cls.loadSidePanel.get_filename() loader = ionest.enddir[uri[uri.rfind(".") + 1:]] position = cls.loadSidePanel.getPosition() gameno = cls.loadSidePanel.getGameno() ionest.generalStart(gamemodel, p0, p1, (uri, loader, gameno, position)) else: ionest.generalStart(gamemodel, p0, p1) cls._generalRun(_callback, _validate)
def start(self, host, port, connected_event, timeseal=True): if self.canceled: raise CanceledException() self.port = port self.host = host self.connected_event = connected_event self.timeseal = timeseal self.name = host if host == "chessclub.com": self.ICC = True self.timeseal = False # You can get ICC timestamp from # https://www.chessclub.com/user/resources/icc/timestamp/ if sys.platform == "win32": timestamp = "timestamp_win32.exe" else: timestamp = "timestamp_linux_2.6.8" altpath = getEngineDataPrefix() path = shutil.which(timestamp, os.X_OK, path=altpath) if path is None: binary = "https://www.chessclub.com/user/resources/icc/timestamp/%s" % timestamp filename = download_file(binary) if filename is not None: dest = shutil.move(filename, os.path.join(altpath, timestamp)) os.chmod(dest, stat.S_IEXEC | stat.S_IREAD | stat.S_IWRITE) if path: self.host = "127.0.0.1" self.port = 5500 try: self.timestamp_proc = subprocess.Popen(["%s" % path, "-p", "%s" % self.port]) log.info("%s started OK" % path) except OSError as err: log.info("Can't start %s OSError: %s %s" % (path, err.errno, err.strerror)) self.port = port self.host = host else: log.info("%s not found, downloading..." % path) def cb(reader, writer): reader.stream_writer = writer reader.connected_event.set() loop = asyncio.get_event_loop() self.reader = ICSStreamReader(_DEFAULT_LIMIT, loop, self.connected_event, self.name) self.protocol = ICSStreamReaderProtocol(self.reader, cb, loop, self.name, self.timeseal) coro = loop.create_connection(lambda: self.protocol, self.host, self.port) self.transport, _protocol = yield from coro # writer = asyncio.StreamWriter(transport, protocol, reader, loop) if self.timeseal: self.write(self.get_init_string())
def onGameStarted(self, gamemodel): if gamemodel.isObservationGame() or gamemodel.examined: # no local player but enable chat to send/receive whisper/kibitz pass elif gamemodel.players[0].__type__ == LOCAL: self.player = gamemodel.players[0] self.opplayer = gamemodel.players[1] if gamemodel.players[1].__type__ == LOCAL: log.warning("Chatpanel loaded with two local players") elif gamemodel.players[1].__type__ == LOCAL: self.player = gamemodel.players[1] self.opplayer = gamemodel.players[0] else: log.info("Chatpanel loaded with no local players") self.chatView.hide() if isinstance(gamemodel, ICGameModel): allob = 'allob ' + str(gamemodel.ficsgame.gameno) gamemodel.connection.client.run_command(allob) if hasattr(self, "player"): self.player.connect("messageReceived", self.onMessageReieved) self.chatView.enable()
def start(self, host, port, connected_event): if self.canceled: raise CanceledException() self.port = port self.host = host self.connected_event = connected_event self.name = host if host == "chessclub.com": self.ICC = True if self.timeseal and timestamp_path is not None: self.host = "127.0.0.1" self.port = 5500 try: if sys.platform == "win32": # To prevent engines opening console window startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW else: startupinfo = None create = asyncio.create_subprocess_exec( *["%s" % timestamp_path, "-p", "%s" % self.port], startupinfo=startupinfo) self.timestamp_proc = yield from create log.info("%s started OK" % timestamp_path) except OSError as err: log.info("Can't start %s OSError: %s %s" % (timestamp_path, err.errno, err.strerror)) self.port = port self.host = host else: log.info("%s not found" % timestamp_path) self.timeseal = False def cb(reader, writer): reader.stream_writer = writer reader.connected_event.set() loop = asyncio.get_event_loop() self.reader = ICSStreamReader(_DEFAULT_LIMIT, loop, self.connected_event, self.name) self.protocol = ICSStreamReaderProtocol(self.reader, cb, loop, self.name, self.timeseal) coro = loop.create_connection(lambda: self.protocol, self.host, self.port) self.transport, _protocol = yield from coro # writer = asyncio.StreamWriter(transport, protocol, reader, loop) if self.timeseal: self.write(self.get_init_string())
def start(self, host, port, connected_event): if self.canceled: raise CanceledException() self.port = port self.host = host self.connected_event = connected_event self.name = host if host == "chessclub.com": self.ICC = True if self.timeseal and timestamp_path is not None: self.host = "127.0.0.1" self.port = 5500 try: if sys.platform == "win32": # To prevent engines opening console window startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW else: startupinfo = None create = asyncio.create_subprocess_exec(* ["%s" % timestamp_path, "-p", "%s" % self.port], startupinfo=startupinfo) self.timestamp_proc = yield from create log.info("%s started OK" % timestamp_path) except OSError as err: log.info("Can't start %s OSError: %s %s" % (timestamp_path, err.errno, err.strerror)) self.port = port self.host = host else: log.info("%s not found" % timestamp_path) self.timeseal = False def cb(reader, writer): reader.stream_writer = writer reader.connected_event.set() loop = asyncio.get_event_loop() self.reader = ICSStreamReader(_DEFAULT_LIMIT, loop, self.connected_event, self.name) self.protocol = ICSStreamReaderProtocol(self.reader, cb, loop, self.name, self.timeseal) coro = loop.create_connection(lambda: self.protocol, self.host, self.port) self.transport, _protocol = yield from coro # writer = asyncio.StreamWriter(transport, protocol, reader, loop) if self.timeseal: self.write(self.get_init_string())
def do_startup(self): Gtk.Application.do_startup(self) if self.purge_recent: items = recent_manager.get_items() for item in items: uri = item.get_uri() if item.get_application_info("pychess"): recent_manager.remove_item(uri) self.git_rev = "" self.initGlade(self.log_viewer) self.addPerspectives() self.handleArgs(self.chess_file) create_task(checkversion()) self.loaded_cids = {} self.saved_cids = {} self.terminated_cids = {} log.info("PyChess %s %s git %s" % (VERSION_NAME, VERSION, self.git_rev)) log.info("Command line args: '%s'" % self.chess_file) log.info("Platform: %s" % platform.platform()) log.info("Python version: %s.%s.%s" % sys.version_info[0:3]) log.info("Pyglib version: %s.%s.%s" % GLib.pyglib_version) log.info("Gtk version: %s.%s.%s" % (Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version()))
def _connect(self): self.connecting = True self.emit("connecting") try: self.emit('connectingMsg', _("Connecting to server")) for i, port in enumerate(self.ports): log.debug("Trying port %d" % port, extra={"task": (self.host, "raw")}) try: connected_event = asyncio.Event() self.client = ICSTelnet() asyncio.async(self.client.start(self.host, port, connected_event)) yield from connected_event.wait() except socket.error as err: log.debug("Failed to open port %d %s" % (port, err), extra={"task": (self.host, "raw")}) if i + 1 == len(self.ports): raise else: continue else: break yield from self.client.read_until("login: "******"Logging on to server")) # login with registered handle if self.password: self.client.write(self.username) got = yield from self.client.read_until( "password:"******"enter the server as", "Try again.") if got == 0: self.client.sensitive = True self.client.write(self.password) # No such name elif got == 1: raise LogOnException(NOTREG % self.username) # Bad name elif got == 2: raise LogOnException(NOTREG % self.username) else: if self.username: self.client.write(self.username) else: self.client.write("guest") got = yield from self.client.read_until( "Press return", "You are connected as a guest", "If it is yours, type the password.", "guest connections have been prevented", "nobody from your site may login without an account.") # got = 3 if got == 2: raise LogOnException(REGISTERED % self.username) elif got == 3 or got == 4: raise LogOnException(PREVENTED) self.client.write("") while True: line = yield from self.client.readline() if "Invalid password" in line: raise LogOnException(BADPAS) elif "is already logged in" in line: raise LogOnException(ALREADYIN % self.username) match = re.search( "\*\*\*\* Starting FICS session as " + "(%s)%s \*\*\*\*" % (ic.NAMES_RE, ic.TITLES_RE), line) if match: self.username = match.groups()[0] break # USCN specific line match = re.search("Created temporary login '(%s)'" % ic.NAMES_RE, line) if match: self.username = match.groups()[0] break match = re.search("For a list of events, click here:", line) if match: break # ICC specific line match = re.search("help anonymous", line) if match: break match = re.search("This is the admin message of the day", line) if match: break self.emit('connectingMsg', _("Setting up environment")) lines = yield from self.client.readuntil(b"ics%") self._post_connect_hook(lines) self.FatICS = self.client.FatICS self.USCN = self.client.USCN self.ICC = self.client.ICC self.client.name = self.username self.client = PredictionsTelnet(self.client, self.predictions, self.reply_cmd_dict, self.replay_dg_dict, self.replay_cn_dict) self.client.lines.line_prefix = "aics%" if self.ICC else "fics%" if not self.USCN and not self.ICC: self.client.run_command("iset block 1") self.client.lines.block_mode = True if self.ICC: self.client.run_command("set level1 5") self.client.run_command("set prompt 0") self.client.lines.datagram_mode = True ic.GAME_TYPES_BY_SHORT_FICS_NAME["B"] = ic.GAME_TYPES["bullet"] else: ic.GAME_TYPES_BY_SHORT_FICS_NAME["B"] = ic.GAME_TYPES["bughouse"] self.client.run_command("iset defprompt 1") self.client.run_command("iset ms 1") self._start_managers(lines) self.connecting = False self.connected = True self.emit("connected") @asyncio.coroutine def keep_alive(): while self.isConnected(): self.client.run_command("date") yield from asyncio.sleep(30 * 60) self.keep_alive_task = asyncio.async(keep_alive()) except CanceledException as err: log.info("FICSConnection._connect: %s" % repr(err), extra={"task": (self.host, "raw")}) finally: self.connecting = False
def _connect (self): self.connecting = True self.emit("connecting") try: self.client = TimeSeal() self.emit('connectingMsg', _("Connecting to server")) for i, port in enumerate(self.ports): log.debug("Trying port %d" % port, extra={"task": (self.host, "raw")}) try: self.client.open(self.host, port) except socket.error as e: log.debug("Failed to open port %d %s" % (port, e), extra={"task": (self.host, "raw")}) if i+1 == len(self.ports): raise else: continue else: break self.client.read_until("login: "******"Logging on to server")) # login with registered handle if self.password: print(self.username, file=self.client) got = self.client.read_until("password:"******"enter the server as", "Try again.") if got == 0: self.client.sensitive = True print(self.password, file=self.client) self.client.sensitive = False # No such name elif got == 1: raise LogOnException(NOTREG % self.username) # Bad name elif got == 2: raise LogOnException(NOTREG % self.username) else: if self.username: print(self.username, file=self.client) else: print("guest", file=self.client) got = self.client.read_until("Press return", "If it is yours, type the password.") if got == 1: raise LogOnException(REGISTERED % self.username) print(file=self.client) while True: line = self.client.readline() if "Invalid password" in line: raise LogOnException(BADPAS) elif "is already logged in" in line: raise LogOnException(ALREADYIN % self.username) match = re.search("\*\*\*\* Starting FICS session as " + "(%s)%s \*\*\*\*" % (NAMES_RE, TITLES_RE), line) if match: self.username = match.groups()[0] break self.emit('connectingMsg', _("Setting up environment")) lines = self.client.readuntil(b"ics%") self._post_connect_hook(lines) self.FatICS = self.client.FatICS self.client.name = self.username self.client = PredictionsTelnet(self.client, self.predictions, self.reply_cmd_dict) self.client.lines.line_prefix = "fics%" self.client.run_command("iset block 1") self.client.lines.block_mode = True self.client.run_command("iset defprompt 1") self.client.run_command("iset ms 1") self.client.run_command("set seek 0") self._start_managers() self.connecting = False self.connected = True self.emit("connected") def keep_alive(): last = time.time() while self.isConnected(): if time.time()-last > 59*60: self.client.run_command("date") last = time.time() time.sleep(30) t = threading.Thread(target=keep_alive, name=fident(keep_alive)) t.daemon = True t.start() except CanceledException as e: log.info("FICSConnection._connect: %s" % repr(e), extra={"task": (self.host, "raw")}) finally: self.connecting = False
def parseLine(self, proc): while True: line = yield from wait_signal(proc, 'line') if not line: break else: line = line[1] parts = line.split() if not parts: continue # Initializing if parts[0] == "id": if parts[1] == "name": self.ids[parts[1]] = " ".join(parts[2:]) self.setName(self.ids["name"]) continue if parts[0] == "uciok": self.emit("readyForOptions") continue if parts[0] == "readyok": self.emit("readyForMoves") continue # Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in range(2, len(parts) + 1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last + 1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) elif key == "type" and value == "string": dic[key] = "text" else: dic[key] = value last = i if varlist: dic["choices"] = varlist if "name" in dic: self.options[dic["name"]] = dic continue # A Move if self.mode == NORMAL and parts[0] == "bestmove": self.needBestmove = False self.bestmove_event.set() self.__sendQueuedGo() if self.ignoreNext: log.debug( "__parseLine: line='%s' self.ignoreNext==True, returning" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.readyForStop = True continue movestr = parts[1] if not self.waitingForMove: log.warning("__parseLine: self.waitingForMove==False, ignoring move=%s" % movestr, extra={"task": self.defname}) self.pondermove = None continue self.waitingForMove = False try: move = parseAny(self.board, movestr) except ParsingError: self.invalid_move = movestr log.info( "__parseLine: ParsingError engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error("__parseLine: move=%s didn't validate, putting 'del' \ in returnQueue. self.board=%s" % ( repr(move), self.board), extra={"task": self.defname}) self.invalid_move = movestr self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue self._recordMove(self.board.move(move), move, self.board) log.debug("__parseLine: applied move=%s to self.board=%s" % ( move, self.board), extra={"task": self.defname}) if self.ponderOn: self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAny(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.queue.put_nowait(move) log.debug("__parseLine: put move=%s into self.queue=%s" % ( move, self.queue), extra={"task": self.defname}) continue # An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: multipv = 1 if "multipv" in parts: multipv = int(parts[parts.index("multipv") + 1]) scoretype = parts[parts.index("score") + 1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score") + 2]) if scoretype == 'mate': # print >> self.engine, "stop" if score != 0: sign = score / abs(score) score = sign * (MATE_VALUE - abs(score)) movstrs = parts[parts.index("pv") + 1:] if "depth" in parts: depth = parts[parts.index("depth") + 1] else: depth = "" if "nps" in parts: nps = parts[parts.index("nps") + 1] else: nps = "" if multipv <= len(self.analysis): self.analysis[multipv - 1] = (self.board.ply, movstrs, score, depth, nps) self.emit("analyze", self.analysis) continue # An Analyzer bestmove if self.mode != NORMAL and parts[0] == "bestmove": log.debug("__parseLine: processing analyzer bestmove='%s'" % line.strip(), extra={"task": self.defname}) self.needBestmove = False self.bestmove_event.set() if parts[1] == "(none)": self.emit("analyze", []) else: self.__sendQueuedGo(sendlast=True) continue # Stockfish complaining it received a 'stop' without a corresponding 'position..go' if line.strip() == "Unknown command: stop": log.debug("__parseLine: processing '%s'" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.needBestmove = False self.readyForStop = False self.__sendQueuedGo() continue
def __parseLine (self, line): if line[0:1] == "#": # Debug line which we shall ignore as specified in CECPv2 specs return # log.debug("__parseLine: line=\"%s\"\n" % line.strip(), self.defname) parts = whitespaces.split(line.strip()) if parts[0] == "pong": self.lastpong = int(parts[1]) return # Illegal Move if parts[0].lower().find("illegal") >= 0: log.warn("__parseLine: illegal move: line=\"%s\", board=%s" \ % (line.strip(), self.board), self.defname) if parts[-2] == "sd" and parts[-1].isdigit(): print >> self.engine, "depth", parts[-1] return # A Move (Perhaps) if self.board: if parts[0] == "move": movestr = parts[1] # Old Variation elif d_plus_dot_expr.match(parts[0]) and parts[1] == "...": movestr = parts[2] else: movestr = False if movestr: log.debug("__parseLine: acquiring self.boardLock\n", self.defname) self.waitingForMove = False self.readyForMoveNowCommand = False self.boardLock.acquire() try: if self.engineIsInNotPlaying: # If engine was set in pause just before the engine sent its # move, we ignore it. However the engine has to know that we # ignored it, and thus we step it one back log.info("__parseLine: Discarding engine's move: %s\n" % movestr, self.defname) print >> self.engine, "undo" return else: try: move = parseAny(self.board, movestr) except ParsingError, e: self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return if validate(self.board, move): self.board = None self.returnQueue.put(move) return self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) return finally: log.debug("__parseLine(): releasing self.boardLock\n", self.defname) self.boardLock.release() self.movecon.acquire() self.movecon.notifyAll() self.movecon.release() # Analyzing if self.engineIsInNotPlaying: if parts[:4] == ["0","0","0","0"]: # Crafty doesn't analyze until it is out of book print >> self.engine, "book off" return match = anare.match(line) if match: score, moves = match.groups() if "mat" in score.lower() or "#" in moves: # Will look either like -Mat 3 or Mat3 scoreval = MATE_VALUE if score.startswith('-'): scoreval = -scoreval else: scoreval = int(score) mvstrs = movere.findall(moves) try: moves = listToMoves (self.board, mvstrs, type=None, validate=True, ignoreErrors=False) except: # Errors may happen when parsing "old" lines from # analyzing engines, which haven't yet noticed their new tasks log.debug('Ignored an "old" line from analyzer: %s\n' % mvstrs, self.defname) return # Don't emit if we weren't able to parse moves, or if we have a move # to kill the opponent king - as it confuses many engines if moves and not self.board.board.opIsChecked(): #self.emit("analyze", [(moves, scoreval)]) return # Offers draw if parts[0:2] == ["offer", "draw"]: #self.emit("accept", Offer(DRAW_OFFER)) return # Resigns if parts[0] == "resign" or \ (parts[0] == "tellics" and parts[1] == "resign"): # buggy crafty # Previously: if "resign" in parts, # however, this is too generic, since "hint", "bk", # "feature option=.." and possibly other, future CECPv2 # commands can validly contain the word "resign" without this # being an intentional resign offer. #self.emit("offer", Offer(RESIGNATION)) return #if parts[0].lower() == "error": # return #Tell User Error if parts[0] == "tellusererror": # Create a non-modal non-blocking message dialog with the error: dlg = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, message_format=None) # Use the engine name if already known, otherwise the defname: displayname = self.name if not displayname: displayname = self.defname # Compose the dialog text: dlg.set_markup(gobject.markup_escape_text(_("The engine %s reports an error:") % displayname) + "\n\n" + gobject.markup_escape_text(" ".join(parts[1:]))) # handle response signal so the "Close" button works: dlg.connect("response", lambda dlg, x: dlg.destroy()) dlg.show_all() return # Tell Somebody if parts[0][:4] == "tell" and \ parts[0][4:] in ("others", "all", "ics", "icsnoalias"): log.info("Ignoring tell %s: %s\n" % (parts[0][4:], " ".join(parts[1:]))) return if "feature" in parts: # Some engines send features after done=1, so we will iterate after done=1 too done1 = False # We skip parts before 'feature', as some engines give us lines like # White (1) : feature setboard=1 analyze...e="GNU Chess 5.07" done=1 parts = parts[parts.index("feature"):] for i, pair in enumerate(parts[1:]): # As "parts" is split with no thoughs on quotes or double quotes # we need to do some extra handling. if pair.find("=") < 0: continue key, value = pair.split("=",1) if value[0] in ('"',"'") and value[-1] in ('"',"'"): value = value[1:-1] # If our pair was unfinished, like myname="GNU, we search the # rest of the pairs for a quotating mark. elif value[0] in ('"',"'"): rest = value[1:] + " " + " ".join(parts[2+i:]) i = rest.find('"') j = rest.find("'") if i + j == -2: log.warn("Missing endquotation in %s feature", self.defname) value = rest elif min(i, j) != -1: value = rest[:min(i, j)] else: l = max(i, j) value = rest[:l] elif value.isdigit(): value = int(value) if key in self.supported_features: print >> self.engine, "accepted %s" % key else: print >> self.engine, "rejected %s" % key if key == "done": if value == 1: done1 = True continue elif value == 0: log.info("Adds %d seconds timeout\n" % TIME_OUT_SECOND, self.defname) # This'll buy you some more time self.timeout = time.time()+TIME_OUT_SECOND self.returnQueue.put("not ready") return if key == "smp" and value == 1: self.options["cores"] = {"name": "cores", "type": "spin", "default": 1, "min": 1, "max": 64} elif key == "memory" and value == 1: self.options["memory"] = {"name": "memory", "type": "spin", "default": 32, "min": 1, "max": 4096} elif key == "option" and key != "done": option = self.__parse_option(value) self.options[option["name"]] = option else: self.features[key] = value if key == "myname" and not self.name: self.setName(value) if done1: # Start a new game before using the engine: # (CECPv2 engines) print >> self.engine, "new" # We are now ready for play: #self.emit("readyForOptions") #self.emit("readyForMoves") self.returnQueue.put("ready") # A hack to get better names in protover 1. # Unfortunately it wont work for now, as we don't read any lines from # protover 1 engines. When should we stop? if self.protover == 1: if self.defname[0] in ''.join(parts): basis = self.defname[0] name = ' '.join(itertools.dropwhile(lambda part: basis not in part, parts)) self.features['myname'] = name if not self.name: self.setName(name) def __parse_option(self, option): if " -check " in option: name, value = option.split(" -check ") return {"type": "check", "name": name, "default": bool(int(value))} elif " -spin " in option: name, value = option.split(" -spin ") defv, minv, maxv = value.split() return {"type": "spin", "name": name, "default": int(defv), "min": int(minv), "max": int(maxv)} elif " -slider " in option: name, value = option.split(" -slider ") defv, minv, maxv = value.split() return {"type": "spin", "name": name, "default": int(defv), "min": int(minv), "max": int(maxv)} elif " -string " in option: name, value = option.split(" -string ") return {"type": "text", "name": name, "default": value} elif " -file " in option: name, value = option.split(" -file ") return {"type": "text", "name": name, "default": value} elif " -path " in option: name, value = option.split(" -path ") return {"type": "text", "name": name, "default": value} elif " -combo " in option: name, value = option.split(" -combo ") return {"type": "combo", "name": name, "default": value} elif " -button" in option: pos = option.find(" -button") return {"type": "button", "name": option[:pos]} elif " -save" in option: pos = option.find(" -save") return {"type": "button", "name": option[:pos]} elif " -reset" in option: pos = option.find(" -reset") return {"type": "button", "name": option[:pos]} #=========================================================================== # Info #=========================================================================== def canAnalyze (self): assert self.ready, "Still waiting for done=1" return self.features["analyze"] def maxAnalysisLines (self): return 1 def requestMultiPV (self, setting): return 1 def isAnalyzing (self): return self.mode in (ANALYZING, INVERSE_ANALYZING) def __repr__ (self): if self.name: return self.name return self.features["myname"]
def extendlog(self, messages): [log.info(m+"\n") for m in messages] self.log.extend(messages) del self.log[:-10]
def parseLine(self, proc): while True: line = yield from wait_signal(proc, 'line') if not line: break else: line = line[1] parts = line.split() if not parts: continue # Initializing if parts[0] == "id": if parts[1] == "name": self.ids[parts[1]] = " ".join(parts[2:]) self.setName(self.ids["name"]) continue if parts[0] == "uciok": self.emit("readyForOptions") continue if parts[0] == "readyok": self.emit("readyForMoves") continue # Options parsing if parts[0] == "option": dic = {} last = 1 varlist = [] for i in range(2, len(parts) + 1): if i == len(parts) or parts[i] in OPTKEYS: key = parts[last] value = " ".join(parts[last + 1:i]) if "type" in dic and dic["type"] in TYPEDIC: value = TYPEDIC[dic["type"]](value) if key == "var": varlist.append(value) elif key == "type" and value == "string": dic[key] = "text" else: dic[key] = value last = i if varlist: dic["choices"] = varlist if "name" in dic: self.options[dic["name"]] = dic continue # A Move if self.mode == NORMAL and parts[0] == "bestmove": self.needBestmove = False self.bestmove_event.set() self.__sendQueuedGo() if self.ignoreNext: log.debug( "__parseLine: line='%s' self.ignoreNext==True, returning" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.readyForStop = True continue movestr = parts[1] if not self.waitingForMove: log.warning( "__parseLine: self.waitingForMove==False, ignoring move=%s" % movestr, extra={"task": self.defname}) self.pondermove = None continue self.waitingForMove = False try: move = parseAny(self.board, movestr) except ParsingError: self.invalid_move = movestr log.info( "__parseLine: ParsingError engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue if not validate(self.board, move): # This is critical. To avoid game stalls, we need to resign on # behalf of the engine. log.error( "__parseLine: move=%s didn't validate, putting 'del' \ in returnQueue. self.board=%s" % (repr(move), self.board), extra={"task": self.defname}) self.invalid_move = movestr self.end( WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue self._recordMove(self.board.move(move), move, self.board) log.debug("__parseLine: applied move=%s to self.board=%s" % (move, self.board), extra={"task": self.defname}) if self.ponderOn: self.pondermove = None # An engine may send an empty ponder line, simply to clear. if len(parts) == 4: # Engines don't always check for everything in their # ponders. Hence we need to validate. # But in some cases, what they send may not even be # correct AN - specially in the case of promotion. try: pondermove = parseAny(self.board, parts[3]) except ParsingError: pass else: if validate(self.board, pondermove): self.pondermove = pondermove self._startPonder() self.queue.put_nowait(move) log.debug("__parseLine: put move=%s into self.queue=%s" % (move, self.queue), extra={"task": self.defname}) continue # An Analysis if self.mode != NORMAL and parts[0] == "info" and "pv" in parts: multipv = 1 if "multipv" in parts: multipv = int(parts[parts.index("multipv") + 1]) scoretype = parts[parts.index("score") + 1] if scoretype in ('lowerbound', 'upperbound'): score = None else: score = int(parts[parts.index("score") + 2]) if scoretype == 'mate': # print >> self.engine, "stop" if score != 0: sign = score / abs(score) score = sign * (MATE_VALUE - abs(score)) movstrs = parts[parts.index("pv") + 1:] if "depth" in parts: depth = parts[parts.index("depth") + 1] else: depth = "" if "nps" in parts: nps = parts[parts.index("nps") + 1] else: nps = "" if multipv <= len(self.analysis): self.analysis[multipv - 1] = (self.board.ply, movstrs, score, depth, nps) self.emit("analyze", self.analysis) continue # An Analyzer bestmove if self.mode != NORMAL and parts[0] == "bestmove": log.debug( "__parseLine: processing analyzer bestmove='%s'" % line.strip(), extra={"task": self.defname}) self.needBestmove = False self.bestmove_event.set() if parts[1] == "(none)": self.emit("analyze", []) else: self.__sendQueuedGo(sendlast=True) continue # Stockfish complaining it received a 'stop' without a corresponding 'position..go' if line.strip() == "Unknown command: stop": log.debug("__parseLine: processing '%s'" % line.strip(), extra={"task": self.defname}) self.ignoreNext = False self.needBestmove = False self.readyForStop = False self.__sendQueuedGo() continue
def do_import(self, filename, info=None, progressbar=None): self.progressbar = progressbar orig_filename = filename count_source = self.conn.execute( self.count_source.where(source.c.name == orig_filename)).scalar() if count_source > 0: log.info("%s is already imported" % filename) return # collect new names not in they dict yet self.event_data = [] self.site_data = [] self.player_data = [] self.annotator_data = [] self.source_data = [] # collect new games and commit them in big chunks for speed self.game_data = [] self.tag_game_data = [] if filename.startswith("http"): filename = download_file(filename, progressbar=progressbar) if filename is None: return else: if not os.path.isfile(filename): log.info("Can't open %s" % filename) return if filename.lower().endswith(".zip") and zipfile.is_zipfile(filename): with zipfile.ZipFile(filename, "r") as zf: path = os.path.dirname(filename) files = [ os.path.join(path, f) for f in zf.namelist() if f.lower().endswith(".pgn") ] zf.extractall(path) else: files = [filename] for pgnfile in files: base_offset = self.chessfile.size if self.append_pgn else 0 basename = os.path.basename(pgnfile) if progressbar is not None: GLib.idle_add(progressbar.set_text, _("Reading %s ..." % basename)) else: log.info("Reading %s ..." % pgnfile) size = os.path.getsize(pgnfile) handle = protoopen(pgnfile) # estimated game count all_games = max(size / 840, 1) get_id = self.get_id # use transaction to avoid autocommit slowness # and to let undo importing (rollback) if self.cancel was set trans = self.conn.begin() try: i = 0 for tags in read_games(handle): if not tags: log.info("Empty game #%s" % (i + 1)) continue if self.cancel: trans.rollback() return fenstr = tags["FEN"] variant = tags["Variant"] if variant: if "fischer" in variant.lower() or "960" in variant: variant = "Fischerandom" else: variant = variant.lower().capitalize() # Fixes for some non statndard Chess960 .pgn if fenstr and variant == "Fischerandom": parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) if variant: if variant not in name2variant: log.info("Unknown variant: %s" % variant) continue variant = name2variant[variant].variant if variant == NORMALCHESS: # lichess uses tag [Variant "Standard"] variant = 0 else: variant = 0 if basename == "eco.pgn": white = tags["Opening"] black = tags["Variation"] else: white = tags["White"] black = tags["Black"] event_id = get_id(tags["Event"], event, EVENT) site_id = get_id(tags["Site"], site, SITE) date = tags["Date"] game_round = tags['Round'] white_id = get_id(white, player, PLAYER) black_id = get_id(black, player, PLAYER) result = tags["Result"] if result in pgn2Const: result = pgn2Const[result] else: result = RUNNING white_elo = tags['WhiteElo'] black_elo = tags['BlackElo'] time_control = tags["TimeControl"] eco = tags["ECO"][:3] fen = tags["FEN"] board_tag = int(tags["Board"]) if "Board" in tags else 0 annotator_id = get_id(tags["Annotator"], annotator, ANNOTATOR) source_id = get_id(orig_filename, source, SOURCE, info=info) ply_count = tags["PlyCount"] if "PlyCount" in tags else 0 offset = base_offset + int(tags["offset"]) self.game_data.append({ 'offset': offset, 'offset8': (offset >> 3) << 3, 'event_id': event_id, 'site_id': site_id, 'date': date, 'round': game_round, 'white_id': white_id, 'black_id': black_id, 'result': result, 'white_elo': white_elo, 'black_elo': black_elo, 'ply_count': ply_count, 'eco': eco, 'fen': fen, 'variant': variant, 'board': board_tag, 'time_control': time_control, 'annotator_id': annotator_id, 'source_id': source_id, }) for tag in tags: if tag not in dedicated_tags and tag not in other_game_tags and tags[ tag]: self.tag_game_data.append({ 'game_id': self.next_id[GAME], 'tag_name': tag, 'tag_value': tags[tag], }) self.next_id[GAME] += 1 i += 1 if len(self.game_data) >= self.CHUNK: if self.event_data: self.conn.execute(self.ins_event, self.event_data) self.event_data = [] if self.site_data: self.conn.execute(self.ins_site, self.site_data) self.site_data = [] if self.player_data: self.conn.execute(self.ins_player, self.player_data) self.player_data = [] if self.annotator_data: self.conn.execute(self.ins_annotator, self.annotator_data) self.annotator_data = [] if self.source_data: self.conn.execute(self.ins_source, self.source_data) self.source_data = [] if self.tag_game_data: self.conn.execute(self.ins_tag_game, self.tag_game_data) self.tag_game_data = [] self.conn.execute(self.ins_game, self.game_data) self.game_data = [] if progressbar is not None: GLib.idle_add(progressbar.set_fraction, i / float(all_games)) GLib.idle_add( progressbar.set_text, _("%(counter)s game headers from %(filename)s imported" % ({ "counter": i, "filename": basename }))) else: log.info("From %s imported %s" % (pgnfile, i)) if self.event_data: self.conn.execute(self.ins_event, self.event_data) self.event_data = [] if self.site_data: self.conn.execute(self.ins_site, self.site_data) self.site_data = [] if self.player_data: self.conn.execute(self.ins_player, self.player_data) self.player_data = [] if self.annotator_data: self.conn.execute(self.ins_annotator, self.annotator_data) self.annotator_data = [] if self.source_data: self.conn.execute(self.ins_source, self.source_data) self.source_data = [] if self.tag_game_data: self.conn.execute(self.ins_tag_game, self.tag_game_data) self.tag_game_data = [] if self.game_data: self.conn.execute(self.ins_game, self.game_data) self.game_data = [] if progressbar is not None: GLib.idle_add(progressbar.set_fraction, i / float(all_games)) GLib.idle_add( progressbar.set_text, _("%(counter)s game headers from %(filename)s imported" % ({ "counter": i, "filename": basename }))) else: log.info("From %s imported %s" % (pgnfile, i)) trans.commit() if self.append_pgn: # reopen database to write self.db_handle.close() with protosave(self.chessfile.path, self.append_pgn) as self.db_handle: log.info("Append from %s to %s" % (pgnfile, self.chessfile.path)) handle.seek(0) self.db_handle.writelines(handle) handle.close() if self.chessfile.scoutfish is not None: # create new .scout from pgnfile we are importing from pychess.Savers.pgn import scoutfish_path args = [ scoutfish_path, "make", pgnfile, "%s" % base_offset ] output = subprocess.check_output( args, stderr=subprocess.STDOUT).decode() # append it to our existing one if output.find("Processing...done") > 0: old_scout = self.chessfile.scoutfish.db new_scout = os.path.splitext(pgnfile)[0] + '.scout' with open(old_scout, "ab") as file1, open(new_scout, "rb") as file2: file1.write(file2.read()) self.chessfile.handle = protoopen(self.chessfile.path) except SQLAlchemyError as e: trans.rollback() log.info("Importing %s failed! \n%s" % (pgnfile, e))
def parseLine(self, proc): while True: line = yield from wait_signal(proc, 'line') if not line: break else: line = line[1] if line[0:1] == "#": # Debug line which we shall ignore as specified in CECPv2 specs continue # log.debug("__parseLine: line=\"%s\"" % line.strip(), extra={"task":self.defname}) parts = whitespaces.split(line.strip()) if parts[0] == "pong": self.lastpong = int(parts[1]) continue # Illegal Move if parts[0].lower().find("illegal") >= 0: log.warning("__parseLine: illegal move: line=\"%s\", board=%s" % ( line.strip(), self.board), extra={"task": self.defname}) if parts[-2] == "sd" and parts[-1].isdigit(): print("depth", parts[-1], file=self.engine) continue # A Move (Perhaps) if self.board: if parts[0] == "move": movestr = parts[1] # Old Variation elif d_plus_dot_expr.match(parts[0]) and parts[1] == "...": movestr = parts[2] else: movestr = False if movestr: self.waitingForMove = False self.readyForMoveNowCommand = False if self.engineIsInNotPlaying: # If engine was set in pause just before the engine sent its # move, we ignore it. However the engine has to know that we # ignored it, and thus we step it one back log.info("__parseLine: Discarding engine's move: %s" % movestr, extra={"task": self.defname}) print("undo", file=self.engine) continue else: try: move = parseAny(self.board, movestr) except ParsingError: self.invalid_move = movestr log.info( "__parseLine: ParsingError engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue if validate(self.board, move): self.board = None self.queue.put_nowait(move) continue else: self.invalid_move = movestr log.info( "__parseLine: can't validate engine move: %s %s" % (movestr, self.board), extra={"task": self.defname}) self.end(WHITEWON if self.board.color == BLACK else BLACKWON, WON_ADJUDICATION) continue # Analyzing if self.engineIsInNotPlaying: if parts[:4] == ["0", "0", "0", "0"]: # Crafty doesn't analyze until it is out of book print("book off", file=self.engine) continue match = anare.match(line) if match: depth, score, moves = match.groups() if "mat" in score.lower() or "#" in moves: # Will look either like -Mat 3 or Mat3 scoreval = MATE_VALUE if score.startswith('-'): scoreval = -scoreval else: scoreval = int(score) mvstrs = movere.findall(moves) if mvstrs: self.emit("analyze", [(mvstrs, scoreval, depth.strip())]) continue # Offers draw if parts[0:2] == ["offer", "draw"]: self.emit("accept", Offer(DRAW_OFFER)) continue # Resigns if parts[0] == "resign" or \ (parts[0] == "tellics" and parts[1] == "resign"): # buggy crafty # Previously: if "resign" in parts, # however, this is too generic, since "hint", "bk", # "feature option=.." and possibly other, future CECPv2 # commands can validly contain the word "resign" without this # being an intentional resign offer. self.emit("offer", Offer(RESIGNATION)) continue # if parts[0].lower() == "error": # continue # Tell User Error if parts[0] == "tellusererror": # We don't want to see our stop analyzer hack as an error message if "8/8/8/8/8/8/8/8" in "".join(parts[1:]): continue # Create a non-modal non-blocking message dialog with the error: dlg = Gtk.MessageDialog(parent=None, flags=0, type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.CLOSE, message_format=None) # Use the engine name if already known, otherwise the defname: displayname = self.name if not displayname: displayname = self.defname # Compose the dialog text: dlg.set_markup(GObject.markup_escape_text(_( "The engine %s reports an error:") % displayname) + "\n\n" + GObject.markup_escape_text(" ".join(parts[1:]))) # handle response signal so the "Close" button works: dlg.connect("response", lambda dlg, x: dlg.destroy()) dlg.show_all() continue # Tell Somebody if parts[0][:4] == "tell" and \ parts[0][4:] in ("others", "all", "ics", "icsnoalias"): log.info("Ignoring tell %s: %s" % (parts[0][4:], " ".join(parts[1:]))) continue if "feature" in parts: # Some engines send features after done=1, so we will iterate after done=1 too done1 = False # We skip parts before 'feature', as some engines give us lines like # White (1) : feature setboard=1 analyze...e="GNU Chess 5.07" done=1 parts = parts[parts.index("feature"):] for i, pair in enumerate(parts[1:]): # As "parts" is split with no thoughs on quotes or double quotes # we need to do some extra handling. if pair.find("=") < 0: continue key, value = pair.split("=", 1) if key not in self.features: continue if value.startswith('"') and value.endswith('"'): value = value[1:-1] # If our pair was unfinished, like myname="GNU, we search the # rest of the pairs for a quotating mark. elif value[0] == '"': rest = value[1:] + " " + " ".join(parts[2 + i:]) j = rest.find('"') if j == -1: log.warning("Missing endquotation in %s feature", extra={"task": self.defname}) value = rest else: value = rest[:j] elif value.isdigit(): value = int(value) if key in self.supported_features: print("accepted %s" % key, file=self.engine) else: print("rejected %s" % key, file=self.engine) if key == "done": if value == 1: done1 = True continue elif value == 0: log.info("Adds %d seconds timeout" % TIME_OUT_SECOND, extra={"task": self.defname}) # This'll buy you some more time self.queue.put_nowait("not ready") break if key == "smp" and value == 1: self.options["cores"] = {"name": "cores", "type": "spin", "default": 1, "min": 1, "max": 64} elif key == "memory" and value == 1: self.options["memory"] = {"name": "memory", "type": "spin", "default": 32, "min": 1, "max": 4096} elif key == "option" and key != "done": option = self.__parse_option(value) self.options[option["name"]] = option else: self.features[key] = value if key == "myname" and not self.name: self.setName(value) if done1: # Start a new game before using the engine: # (CECPv2 engines) print("new", file=self.engine) # We are now ready for play: self.emit("readyForOptions") self.emit("readyForMoves") self.queue.put_nowait("ready") # A hack to get better names in protover 1. # Unfortunately it wont work for now, as we don't read any lines from # protover 1 engines. When should we stop? if self.protover == 1: if self.defname[0] in ''.join(parts): basis = self.defname[0] name = ' '.join(itertools.dropwhile( lambda part: basis not in part, parts)) self.features['myname'] = name if not self.name: self.setName(name)