def _bestmove(self, arg): tokens = arg.split(None, 2) self.bestmove = None if tokens[0] != "(none)": try: self.bestmove = self.board.parse_uci(tokens[0]) except ValueError: LOGGER.exception("exception parsing bestmove") self.ponder = None if self.bestmove is not None and len(tokens) >= 3 and tokens[ 1] == "ponder" and tokens[2] != "(none)": # The ponder move must be legal after the bestmove. Generally, we # trust the engine on this. But we still have to convert # non-UCI_Chess960 castling moves. try: self.ponder = chess.Move.from_uci(tokens[2]) if self.ponder.from_square in [ chess.E1, chess.E8 ] and self.ponder.to_square in [ chess.C1, chess.C8, chess.G1, chess.G8 ]: # Make a copy of the board to avoid race conditions. board = self.board.copy(stack=False) board.push(self.bestmove) self.ponder = board.parse_uci(tokens[2]) except ValueError: LOGGER.exception("exception parsing bestmove ponder") self.ponder = None self.bestmove_received.set() for info_handler in self.info_handlers: info_handler.on_bestmove(self.bestmove, self.ponder)
def handle_float_token(token, fn): try: floatval = float(token) except ValueError: LOGGER.exception("exception parsing float token from info: %r", arg) for info_handler in self.info_handlers: fn(info_handler, floatval)
def _pong(self, pong_arg): try: pong_num = int(pong_arg) except ValueError: LOGGER.exception("exception parsing pong") if self.ping_num == pong_num: self.pong.set() with self.pong_received: self.pong_received.notify_all()
def try_move(board, move): try: move = board.push_uci(move) except ValueError: try: move = board.push_san(move) except ValueError: LOGGER.exception("exception parsing pv") return None return move
def handle_move_token(token, fn): try: move = chess.Move.from_uci(token) except ValueError: LOGGER.exception("exception parsing move token from info: %r", arg) return for info_handler in self.info_handlers: fn(info_handler, move)
def handle_integer_token(token, fn): try: intval = int(token) except ValueError: LOGGER.exception( "exception parsing integer token from info: %r", arg) return for info_handler in self.info_handlers: fn(info_handler, intval)
def set_feature(self, key, value): if key == "egt": for egt_type in value.split(","): self._features["egt"].append(egt_type) else: try: value = int(value) except ValueError: pass try: self._features[key] = value except KeyError: LOGGER.exception("exception looking up feature")
def _move(self, arg): self.move = None try: self.move = self.board.parse_uci(arg) except ValueError: try: self.move = self.board.parse_san(arg) except ValueError: LOGGER.exception("exception parsing move") self.move_received.set() if self.draw_handler: self.draw_handler.clear_offer() self.engine_offered_draw = False for post_handler in self.post_handlers: post_handler.on_move(self.move)
def get(self, key): try: return self._features[key] except KeyError: LOGGER.exception("exception looking up feature")
def set_option(self, key, value): try: self._features["option"][key] = value except KeyError: LOGGER.exception("exception looking up option")
def get_option(self, key): try: return self._features["option"][key] except KeyError: LOGGER.exception("exception looking up option")
def _option(self, arg): current_parameter = None name = [] type = [] default = [] min = None max = None current_var = None var = [] for token in arg.split(" "): if token == "name" and not name: current_parameter = "name" elif token == "type" and not type: current_parameter = "type" elif token == "default" and not default: current_parameter = "default" elif token == "min" and min is None: current_parameter = "min" elif token == "max" and max is None: current_parameter = "max" elif token == "var": current_parameter = "var" if current_var is not None: var.append(" ".join(current_var)) current_var = [] elif current_parameter == "name": name.append(token) elif current_parameter == "type": type.append(token) elif current_parameter == "default": default.append(token) elif current_parameter == "var": current_var.append(token) elif current_parameter == "min": try: min = int(token) except ValueError: LOGGER.exception("exception parsing option min") elif current_parameter == "max": try: max = int(token) except ValueError: LOGGER.exception("exception parsing option max") if current_var is not None: var.append(" ".join(current_var)) type = " ".join(type) default = " ".join(default) if type == "check": if default == "true": default = True elif default == "false": default = False else: default = None elif type == "spin": try: default = int(default) except ValueError: LOGGER.exception("exception parsing option spin default") default = None option = Option(" ".join(name), type, default, min, max, var) self.options[option.name] = option
def _info(self, arg): if not self.info_handlers: return # Notify info handlers of start. for info_handler in self.info_handlers: info_handler.pre_info(arg) # Initialize parser state. board = None pv = None score_kind = None score_cp = None score_mate = None score_lowerbound = False score_upperbound = False refutation_move = None refuted_by = [] currline_cpunr = None currline_moves = [] string = [] def end_of_parameter(): # Parameters with variable length can only be handled when the # next parameter starts or at the end of the line. if pv is not None: for info_handler in self.info_handlers: info_handler.pv(pv) if score_cp is not None or score_mate is not None: for info_handler in self.info_handlers: info_handler.score(score_cp, score_mate, score_lowerbound, score_upperbound) if refutation_move is not None: if refuted_by: for info_handler in self.info_handlers: info_handler.refutation(refutation_move, refuted_by) else: for info_handler in self.info_handlers: info_handler.refutation(refutation_move, None) if currline_cpunr is not None: for info_handler in self.info_handlers: info_handler.currline(currline_cpunr, currline_moves) def handle_integer_token(token, fn): try: intval = int(token) except ValueError: LOGGER.exception( "exception parsing integer token from info: %r", arg) return for info_handler in self.info_handlers: fn(info_handler, intval) def handle_float_token(token, fn): try: floatval = float(token) except ValueError: LOGGER.exception("exception parsing float token from info: %r", arg) for info_handler in self.info_handlers: fn(info_handler, floatval) def handle_move_token(token, fn): try: move = chess.Move.from_uci(token) except ValueError: LOGGER.exception("exception parsing move token from info: %r", arg) return for info_handler in self.info_handlers: fn(info_handler, move) # Find multipv parameter first. if "multipv" in arg: current_parameter = None for token in arg.split(" "): if token == "string": break if current_parameter == "multipv": handle_integer_token( token, lambda handler, val: handler.multipv(val)) current_parameter = token # Parse all other parameters. current_parameter = None for token in arg.split(" "): if current_parameter == "string": string.append(token) elif not token: # Ignore extra spaces. Those can not be directly discarded, # because they may occur in the string parameter. pass elif token in [ "depth", "seldepth", "time", "nodes", "pv", "multipv", "score", "currmove", "currmovenumber", "hashfull", "nps", "tbhits", "cpuload", "refutation", "currline", "ebf", "string" ]: end_of_parameter() current_parameter = token pv = None score_kind = None score_mate = None score_cp = None score_lowerbound = False score_upperbound = False refutation_move = None refuted_by = [] currline_cpunr = None currline_moves = [] if current_parameter == "pv": pv = [] if current_parameter in ["refutation", "pv", "currline"]: board = self.board.copy(stack=False) elif current_parameter == "depth": handle_integer_token(token, lambda handler, val: handler.depth(val)) elif current_parameter == "seldepth": handle_integer_token( token, lambda handler, val: handler.seldepth(val)) elif current_parameter == "time": handle_integer_token(token, lambda handler, val: handler.time(val)) elif current_parameter == "nodes": handle_integer_token(token, lambda handler, val: handler.nodes(val)) elif current_parameter == "pv": try: pv.append(board.push_uci(token)) except ValueError: LOGGER.exception( "exception parsing pv from info: %r, position at root: %s", arg, self.board.fen()) elif current_parameter == "multipv": # Ignore multipv. It was already parsed before anything else. pass elif current_parameter == "score": if token in ["cp", "mate"]: score_kind = token elif token == "lowerbound": score_lowerbound = True elif token == "upperbound": score_upperbound = True elif score_kind == "cp": try: score_cp = int(token) except ValueError: LOGGER.exception( "exception parsing score cp value from info: %r", arg) elif score_kind == "mate": try: score_mate = int(token) except ValueError: LOGGER.exception( "exception parsing score mate value from info: %r", arg) elif current_parameter == "currmove": handle_move_token(token, lambda handler, val: handler.currmove(val)) elif current_parameter == "currmovenumber": handle_integer_token( token, lambda handler, val: handler.currmovenumber(val)) elif current_parameter == "hashfull": handle_integer_token( token, lambda handler, val: handler.hashfull(val)) elif current_parameter == "nps": handle_integer_token(token, lambda handler, val: handler.nps(val)) elif current_parameter == "tbhits": handle_integer_token(token, lambda handler, val: handler.tbhits(val)) elif current_parameter == "cpuload": handle_integer_token(token, lambda handler, val: handler.cpuload(val)) elif current_parameter == "refutation": try: if refutation_move is None: refutation_move = board.push_uci(token) else: refuted_by.append(board.push_uci(token)) except ValueError: LOGGER.exception( "exception parsing refutation from info: %r, position at root: %s", arg, self.board.fen()) elif current_parameter == "currline": try: if currline_cpunr is None: currline_cpunr = int(token) else: currline_moves.append(board.push_uci(token)) except ValueError: LOGGER.exception( "exception parsing currline from info: %r, position at root: %s", arg, self.board.fen()) elif current_parameter == "ebf": handle_float_token(token, lambda handler, val: handler.ebf(val)) end_of_parameter() if string: for info_handler in self.info_handlers: info_handler.string(" ".join(string)) # Notify info handlers of end. for info_handler in self.info_handlers: info_handler.post_info()