def all_whatcher(self): while True: yield from gbulb.wait_signal(self.discoverer, "all_engines_discovered") break self.finished = True self.close()
def parse_line(self, proc): while True: line = yield from gbulb.wait_signal(proc, 'line') if line: print(' parse_line:', line[1]) else: print("no more lines") break
def discovered_whatcher(self): while True: if self.finished: return _discoverer, binname, _xmlenginevalue = yield from gbulb.wait_signal(self.discoverer, "engine_discovered") if binname in self.nameToBar: bar = self.nameToBar[binname] bar.props.fraction = 1
def parseLine(self, proc): while True: line = yield from gbulb.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 self.options[dic["name"]] = dic continue # A Move if self.mode == NORMAL and parts[0] == "bestmove": self.needBestmove = False 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 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 movstrs = parts[parts.index("pv") + 1:] if "depth" in parts: depth = parts[parts.index("depth") + 1] else: depth = "" if multipv <= len(self.analysis): self.analysis[multipv - 1] = (movstrs, score, depth) 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.__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, proc): while True: line = yield from gbulb.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)