Esempio n. 1
0
    def all_whatcher(self):
        while True:
            yield from wait_signal(self.discoverer, "all_engines_discovered")
            break

        self.finished = True
        self.close()
Esempio n. 2
0
    def all_whatcher(self):
        while True:
            yield from wait_signal(self.discoverer, "all_engines_discovered")
            break

        self.finished = True
        self.close()
Esempio n. 3
0
 def parse_line(self, proc):
     while True:
         line = yield from wait_signal(proc, 'line')
         if line:
             print('  parse_line:', line[1])
         else:
             print("no more lines")
             break
Esempio n. 4
0
 def parse_line(self, proc):
     while True:
         line = yield from wait_signal(proc, 'line')
         if line:
             print('  parse_line:', line[1])
         else:
             print("no more lines")
             break
Esempio n. 5
0
    def discovered_whatcher(self):
        while True:
            if self.finished:
                return

            _discoverer, binname, _xmlenginevalue = yield from wait_signal(self.discoverer, "engine_discovered")

            if binname in self.nameToBar:
                bar = self.nameToBar[binname]
                bar.props.fraction = 1
Esempio n. 6
0
    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)
Esempio n. 7
0
    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
Esempio n. 8
0
    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.__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
Esempio n. 9
0
    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, time, nodes, 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)

                        nps = str(
                            int(int(nodes) /
                                (int(time) / 100))) if int(time) > 0 else ""

                        mvstrs = movere.findall(moves)
                        if mvstrs:
                            self.emit("analyze",
                                      [(self.board.ply, mvstrs, scoreval,
                                        depth.strip(), nps)])

                        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(mainwindow(),
                                            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)