コード例 #1
0
 def kill (self, reason):
     """ Kills the engine, starting with the 'stop' and 'quit' commands, then
         trying sigterm and eventually sigkill.
         Returns the exitcode, or if engine have already been killed, the
         method returns None """
     if self.connected:
         self.connected = False
         try:
             try:
                 print >> self.engine, "stop"
                 print >> self.engine, "quit"
                 self.returnQueue.put("del")
                 return self.engine.gentleKill()
             
             except OSError, e:
                 # No need to raise on a hang up error, as the engine is dead
                 # anyways
                 if e.errno == 32:
                     log.warn("Hung up Error", self.defname)
                     return e.errno
                 else: raise
         
         finally:
             # Clear the analyzed data, if any
             self.emit("analyze", [], None)
コード例 #2
0
ファイル: engineNest.py プロジェクト: btrent/knave
    def __clean(self, rundata, engine):
        """ Grab the engine from the backup and attach the attributes
            from rundata. The update engine is ready for discovering.
        """
        
        vmpath, path = rundata
        
        with open(path) as f:
            md5sum = md5(f.read()).hexdigest()
        
        ######
        # Find the backup engine
        ######
        try:
            backup_engine = (c for c in backup if c["name"] == engine["name"]).next()
            engine["country"] = backup_engine["country"]
        except StopIteration:
            log.warn("Engine '%s' has not been tested and verified to work with PyChess\n" % \
                engine.get('name'))
            engine['recheck'] = True

        ######
        # Clean it
        ######
        engine['command'] = path
        engine['md5'] = md5sum
        if vmpath is not None:
            engine['vm_command'] = vmpath
コード例 #3
0
ファイル: Main.py プロジェクト: btrent/knave
 def on_drag_received (wi, context, x, y, selection, target_type, timestamp):
     uri = selection.data.strip()
     uris = uri.split()
     if len(uris) > 1:
         log.warn("%d files were dropped. Only loading the first" % len(uris))
     uri = uris[0]
     newGameDialog.LoadFileExtension.run(uri)
コード例 #4
0
    def kill(self, reason):
        """ Kills the engine, starting with the 'stop' and 'quit' commands, then
            trying sigterm and eventually sigkill.
            Returns the exitcode, or if engine have already been killed, the
            method returns None """
        if self.connected:
            self.connected = False
            try:
                try:
                    print >> self.engine, "stop"
                    print >> self.engine, "quit"
                    self.returnQueue.put("del")
                    return self.engine.gentleKill()

                except OSError, e:
                    # No need to raise on a hang up error, as the engine is dead
                    # anyways
                    if e.errno == 32:
                        log.warn("Hung up Error", self.defname)
                        return e.errno
                    else:
                        raise

            finally:
                # Clear the analyzed data, if any
                self.emit("analyze", [], None)
コード例 #5
0
ファイル: ThreadPool.py プロジェクト: btrent/knave
 def start (self, func, *args, **kw):
     self.lock.acquire()
     
     try:
         a = self.queue.get_nowait()
     except Queue.Empty:
         if self.threads < maxThreads:
             self.threads += 1
             a = self.Worker(self.queue)
             a.setDaemon(True)
             a.start()
         else:
             a = self.queue.get(timeout=5)
             from pychess.System.Log import log
             log.warn("Couldn't get a thread after 5s")
             a = self.queue.get()
     
     a.func = lambda: func(*args, **kw)
     a.name = self._getThreadName(a, func)
     
     a.wcond.acquire()
     a.wcond.notify()
     a.wcond.release()
     
     self.lock.release()
コード例 #6
0
 def onChannelLogLine(self, match):
     if not self.currentLogChannel:
         log.warn("Recieved log line before channel was set")
         return
     h, m, s, handle, text = match.groups()
     time = self.convTime(int(h), int(m), int(s))
     text = self.entityDecode(text)
     self.emit("channelLog", self.currentLogChannel, time, handle, text)
コード例 #7
0
ファイル: engineNest.py プロジェクト: btrent/knave
 def __discoverE (self, engine):
     subproc = self.initEngine (engine, BLACK)
     try:
         subproc.connect('readyForOptions', self.__discoverE2, engine)
         subproc.prestart() # Sends the 'start line'
         subproc.start()
     except SubProcessError, e:
         log.warn("Engine %s failed discovery: %s" % (engine["name"],e))
コード例 #8
0
 def onChannelLogLine(self, match):
     if not self.currentLogChannel:
         log.warn("Recieved log line before channel was set")
         return
     h, m, s, handle, text = match.groups()
     time = self.convTime(int(h), int(m), int(s))
     text = self.entityDecode(text)
     self.emit("channelLog", self.currentLogChannel, time, handle, text)
コード例 #9
0
 def answer (message):
     try:
         data = urlopen("http://www.pandorabots.com/pandora/talk?botid=8d034368fe360895",
                        urlencode({"message":message, "botcust2":"x"})).read()
     except IOError, e:
         log.warn("Couldn't answer message from online bot: '%s'\n" % e,
                  self.defname)
         return
コード例 #10
0
ファイル: CECPEngine.py プロジェクト: btrent/knave
 def setOptionVariant (self, variant):
     if self.features["variants"] is None:
         log.warn("setOptionVariant: engine doesn't support variants\n", self.defname)
         return
     
     if variant in variants.values() and not variant.standard_rules:
         assert variant.cecp_name in self.features["variants"], \
                 "%s doesn't support %s variant" % (self, variant.cecp_name)
         self.optionQueue.append("variant %s" % variant.cecp_name)
コード例 #11
0
def load_icon(size, *alternatives):
    alternatives = list(alternatives)
    name = alternatives.pop(0)
    try:
        return it.load_icon(name, size, ICON_LOOKUP_USE_BUILTIN)
    except:
        if alternatives:
            return load_icon(size, *alternatives)
        log.warn("no %s icon in icon-theme-gnome" % name)
コード例 #12
0
 def setOption (self, key, value):
     """ Set an option, which will be sent to the engine, after the
         'readyForOptions' signal has passed.
         If you want to know the possible options, you should go to
         engineDiscoverer or use the getOption, getOptions and hasOption
         methods, while you are in your 'readyForOptions' signal handler """ 
     if self.readyMoves:
         log.warn("Options set after 'readyok' are not sent to the engine", self.defname)
     self.optionsToBeSent[key] = value
コード例 #13
0
 def setOption(self, key, value):
     """ Set an option, which will be sent to the engine, after the
         'readyForOptions' signal has passed.
         If you want to know the possible options, you should go to
         engineDiscoverer or use the getOption, getOptions and hasOption
         methods, while you are in your 'readyForOptions' signal handler """
     if self.readyMoves:
         log.warn("Options set after 'readyok' are not sent to the engine",
                  self.defname)
     self.optionsToBeSent[key] = value
コード例 #14
0
ファイル: uistuff.py プロジェクト: btrent/knave
 def setFromConf ():
     try:
         v = conf.getStrict(key)
     except TypeError:
         log.warn("uistuff.keep.setFromConf: Key '%s' from conf had the wrong type '%s', ignored" % \
                  (key, type(conf.getStrict(key))))
         if first_value != None:
             conf.set(key, first_value)
         else: conf.set(key, get_value())
     else:
         set_value(v)
コード例 #15
0
ファイル: ping.py プロジェクト: btrent/knave
 def __handleDead (self, subprocess):
     if self.deadCount < self.restartsOnDead:
         log.warn("Pinger died and restarted (%d/%d)\n" %
                  (self.deadCount+1, self.restartsOnDead),
                  self.subproc.defname)
         self.stop()
         self.start()
         self.deadCount += 1
     else:
         #self.emit("error", _("Died"))
         self.stop()
コード例 #16
0
 def answer(message):
     try:
         data = urlopen(
             "http://www.pandorabots.com/pandora/talk?botid=8d034368fe360895",
             urlencode({
                 "message": message,
                 "botcust2": "x"
             })).read()
     except IOError, e:
         log.warn("Couldn't answer message from online bot: '%s'\n" % e,
                  self.defname)
         return
コード例 #17
0
ファイル: engineNest.py プロジェクト: btrent/knave
 def __init__ (self):
     GObject.__init__(self)
     
     self.engines = []
     
     self.jsonpath = addUserConfigPrefix("engines.json")
     try:
         self._engines = json.load(open(self.jsonpath))
     except ValueError, e:
         log.warn("engineNest: Couldn\'t read engines.json, renamed it to .bak\n%s\n" % (self.jsonpath,e))
         os.rename(self.jsonpath, self.jsonpath+".bak")
         self._engines = deepcopy(backup)
コード例 #18
0
 def __handleDead(self, subprocess):
     if self.deadCount < self.restartsOnDead:
         log.warn(
             "Pinger died and restarted (%d/%d)\n" %
             (self.deadCount + 1, self.restartsOnDead),
             self.subproc.defname)
         self.stop()
         self.start()
         self.deadCount += 1
     else:
         self.emit("error", _("Died"))
         self.stop()
コード例 #19
0
 def setFromConf():
     try:
         v = conf.getStrict(key)
     except TypeError:
         log.warn("uistuff.keep.setFromConf: Key '%s' from conf had the wrong type '%s', ignored" % \
                  (key, type(conf.getStrict(key))))
         if first_value != None:
             conf.set(key, first_value)
         else:
             conf.set(key, get_value())
     else:
         set_value(v)
コード例 #20
0
    def _init (cls):
        def callback (widget, allocation):
            cls.widgets["enterGameNotationFrame"].set_size_request(
                    223, allocation.height-4)
        cls.widgets["enterGameNotationSidePanel"].connect_after("size-allocate", callback)

        flags = []
        if isInstalled():
            path = gettext.find("pychess")
        else:
            path = gettext.find("pychess", localedir=addDataPrefix("lang"))
        if path:
            loc = locale.getdefaultlocale()[0][-2:].lower()
            flags.append(addDataPrefix("flags/%s.png" % loc))

        flags.append(addDataPrefix("flags/us.png"))

        cls.ib = ImageButton(flags)
        cls.widgets["imageButtonDock"].add(cls.ib)
        cls.ib.show()

        cls.sourcebuffer = SourceBuffer()
        sourceview = SourceView(cls.sourcebuffer)
        cls.widgets["scrolledwindow6"].add(sourceview)
        sourceview.show()

        # Pgn format does not allow tabulator
        sourceview.set_insert_spaces_instead_of_tabs(True)
        sourceview.set_wrap_mode(gtk.WRAP_WORD)

        man = LanguageManager()
        # Init new version
        if hasattr(man.props, 'search_path'):
            path = os.path.join(getDataPrefix(),"gtksourceview-1.0/language-specs")
            man.props.search_path = man.props.search_path + [path]
            if 'pgn' in man.get_language_ids():
                lang = man.get_language('pgn')
                cls.sourcebuffer.set_language(lang)
            else:
                log.warn("Unable to load pgn syntax-highlighting.")
            cls.sourcebuffer.set_highlight_syntax(True)
        # Init old version
        else:
            os.environ["XDG_DATA_DIRS"] = getDataPrefix()+":/usr/share/"
            man = LanguageManager()
            for lang in man.get_available_languages():
                if lang.get_name() == "PGN":
                    cls.sourcebuffer.set_language(lang)
                    break
            else:
                log.warn("Unable to load pgn syntax-highlighting.")
            cls.sourcebuffer.set_highlight(True)
コード例 #21
0
    def __clean(self, rundata, engine):
        """ Grab the engine from the backup and attach the attributes from
            rundata. The 'new' engine is returned and ready for discovering.
        
            If engine doesn't exist in backup, an 'unsupported engine' warning
            is logged, and a new engine element is created for it
        """
        
        vmpath, path = rundata
        
        with open(path) as f:
            md5sum = md5(f.read()).hexdigest()
        
        ######
        # Find the backup engine
        ######
        try:
            engine2 = (c for c in self.backup.findall('engine')
                       if c.get('binname') == engine.get('binname')).next()
        except StopIteration:
            log.warn("Engine '%s' has not been tested and verified to work with PyChess\n" % \
                engine.get('binname'))
            engine2 = fromstring('<engine></engine>')
            engine2.set('binname', engine.get('binname'))
            engine2.set('protocol', engine.get('protocol'))
            if engine.get('protover'):
                engine2.set('protover', engine.get('protover'))
            engine2.set('recheck', 'true')

        # This doesn't work either. Dammit python
        # engine = any(c for c in self.backup.getchildren()
        #             if c.get('binname') == engine.get('binname'))
        # Waiting for etree 1.3 to get into python, before we can use xpath 
        # engine = self.backup.find('engine[@binname="%s"]' % engine.get('binname'))
        
        ######
        # Clean it
        ######
        engine2.append(fromstring('<path>%s</path>' % path))
        engine2.append(fromstring('<md5>%s</md5>' % md5sum))
        engine2.append(fromstring('<args/>'))
        
        if vmpath:
            vmbn = engine.find('vm').get('binname')
            engine2.append(fromstring('<vm binname="%s"/>' % vmbn))
            engine2.find('vm').append(fromstring('<path>%s</path>' % vmpath))
            engine2.find('vm').append(fromstring('<args/>'))
        
        return engine2
コード例 #22
0
ファイル: CECPEngine.py プロジェクト: btrent/knave
 def setOption (self, key, value):
     """ Set an option, which will be sent to the engine, after the
         'readyForOptions' signal has passed.
         If you want to know the possible options, you should go to
         engineDiscoverer or use the getOption, getOptions and hasOption
         methods, while you are in your 'readyForOptions' signal handler """ 
     if self.readyMoves:
         log.warn("Options set after 'readyok' are not sent to the engine", self.defname)
     if key == "cores":
         self.optionQueue.append("cores %s" % value)
     elif key == "memory":
         self.optionQueue.append("memory %s" % value)
     elif key.lower() == "ponder":
         self.__setPonder(value==1)
     else:
         self.optionQueue.append("option %s=%s" % (key, value))
コード例 #23
0
ファイル: egtb_k4it.py プロジェクト: btrent/knave
 def scoreAllMoves (self, board, probeSoft=False):
     global URL, expression, PROMOTION_FLAGS
     fen = board.asFen().split()[0] + " w - - 0 1"
     if (fen,board.color) in self.table:
         return self.table[(fen,board.color)]
     
     if probeSoft or not conf.get("online_egtb_check", True):
         return []
     
     # Request the page
     url = (URL + fen).replace(" ", "%20")
     try:
         f = urllib.urlopen(url)
     except IOError, e:
         log.warn("Unable to read endgame tablebase from the Internet: %s" % repr(e))
         return []
コード例 #24
0
def loadDialogWidget(widget,
                     widget_name,
                     config_number,
                     get_value_=None,
                     set_value_=None,
                     first_value=None):
    key = widget_name + "-" + str(config_number)

    if widget == None:
        raise AttributeError, "key '%s' isn't in widgets" % widget_name

    for class_, methods_ in METHODS:
        if isinstance(widget, class_):
            getter, setter, signal = methods_
            break
    else:
        if set_value_ == None:
            raise AttributeError, "I don't have any knowledge of type: '%s'" % widget

    if get_value_:
        get_value = lambda: get_value_(widget)
    else:
        get_value = getattr(widget, getter)

    if set_value_:
        set_value = lambda v: set_value_(widget, v)
    else:
        set_value = getattr(widget, setter)

    if conf.hasKey(key):
        try:
            v = conf.getStrict(key)
        except TypeError:
            log.warn("uistuff.loadDialogWidget: Key '%s' from conf had the wrong type '%s', ignored" % \
                     (key, type(conf.getStrict(key))))
            if first_value != None:
                conf.set(key, first_value)
            else:
                conf.set(key, get_value())
        else:
            set_value(v)
    elif first_value != None:
        conf.set(key, first_value)
        set_value(conf.getStrict(key))
    else:
        log.warn("Didn't load widget \"%s\": no conf value and no first_value arg" % \
                 widget_name)
コード例 #25
0
    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.warn("Chatpanel loaded with two local players")
        elif gamemodel.players[1].__type__ == LOCAL:
            self.player = gamemodel.players[1]
            self.opplayer = gamemodel.players[0]
        else:
            log.warn("Chatpanel loaded with no local players")
            self.chatView.hide()

        if hasattr(self, "player"):
            self.player.connect("messageRecieved", self.onMessageReieved)

        self.chatView.enable()
コード例 #26
0
 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.warn("Chatpanel loaded with two local players")
     elif gamemodel.players[1].__type__ == LOCAL:
         self.player = gamemodel.players[1]
         self.opplayer = gamemodel.players[0]
     else:
         log.warn("Chatpanel loaded with no local players")
         self.chatView.hide()
     
     if hasattr(self, "player"):
         self.player.connect("messageRecieved", self.onMessageReieved)
     
     self.chatView.enable()
コード例 #27
0
 def __init__ (self):
     GObject.__init__(self)
     
     self.backup = ET.ElementTree(fromstring(backup))
     self.xmlpath = addUserConfigPrefix("engines.xml")
     try:
         self.dom = ET.ElementTree(file=self.xmlpath)
         c = compareVersions(self.dom.getroot().get('version', default='0'), ENGINES_XML_API_VERSION)
         if c == -1:
             log.warn("engineNest: engines.xml is outdated. It will be replaced\n")
             self.dom = deepcopy(self.backup)
         elif c == 1:
             raise NotImplementedError, "engines.xml is of a newer date. In order" + \
                             "to run this version of PyChess it must first be removed"
     except ParseError, e:
         log.warn("engineNest: %s\n" % e)
         self.dom = deepcopy(self.backup)
コード例 #28
0
 def __startBlocking (self):
     if self.protover == 1:
         self.emit("readyForMoves")
     if self.protover == 2:
         try:
             r = self.returnQueue.get(True, max(self.timeout-time.time(),0))
             if r == "not ready":
                 # The engine has sent done=0, and parseLine has added more
                 # time to self.timeout
                 r = self.returnQueue.get(True, max(self.timeout-time.time(),0))
         except Queue.Empty:
             log.warn("Got timeout error\n", self.defname)
             self.emit("readyForOptions")
             self.emit("readyForMoves")
         else:
             if r == 'del':
                 raise PlayerIsDead
             assert r == "ready"
コード例 #29
0
ファイル: CECPEngine.py プロジェクト: btrent/knave
 def makeMove (self, board1, move, board2):
     """ Gets a move from the engine (for player engines).
         @param board1: The current board
         @param move: The last move made
         @param board2: The board before the last move was made
         @return: The move the engine decided to make
     """
     log.debug("makeMove: move=%s self.movenext=%s board1=%s board2=%s self.board=%s\n" % \
         (move, self.movenext, board1, board2, self.board), self.defname)
     assert self.readyMoves
     
     self.boardLock.acquire()
     try:
         if self.board == board1 or not board2 or self.movenext:
             self.board = board1
             self.__tellEngineToPlayCurrentColorAndMakeMove()
             self.movenext = False
         else:
             self.board = board1
             self.__usermove(board2, move)
             
             if self.engineIsInNotPlaying:
                 self.__tellEngineToPlayCurrentColorAndMakeMove()
     finally:
         self.boardLock.release()
     self.waitingForMove = True
     self.readyForMoveNowCommand = True
     
     # Parse outputs
     r = self.returnQueue.get()
     if r == "not ready":
         log.warn("Engine seems to be protover=2, but is treated as protover=1", self.defname)
         r = self.returnQueue.get()
     if r == "ready":
         r = self.returnQueue.get()
     if r == "del":
         raise PlayerIsDead, "Killed by foreign forces"
     if r == "int":
         raise TurnInterrupt
     
     self.waitingForMove = False
     self.readyForMoveNowCommand = False
     assert isinstance(r, Move), r
     return r
コード例 #30
0
def probeEndGameTable (board):
    
    fen = board.asFen().split()[0] + " w - - 0 1"
    if (fen,board.color) in table:
        return table[(fen,board.color)]
    
    # k4it has all 6-men tables except 5 vs. 1
    whites = bitLength(board.friends[WHITE])
    blacks = bitLength(board.friends[BLACK])
    if whites >= 5 or blacks >= 5 or whites+blacks >= 7:
        return []
    
    # Request the page
    url = (URL + fen).replace(" ", "%20")
    try:
        f = urllib.urlopen(url)
    except IOError, e:
        log.warn("Unable to read endgame tablebase from the Internet: %s" % repr(e))
        return []
コード例 #31
0
ファイル: uistuff.py プロジェクト: btrent/knave
def loadDialogWidget (widget, widget_name, config_number, get_value_=None,
                      set_value_=None, first_value=None):
    key = widget_name + "-" + str(config_number)
    
    if widget == None:
        raise AttributeError, "key '%s' isn't in widgets" % widget_name
    
    for class_, methods_ in METHODS:
        if isinstance(widget, class_):
            getter, setter, signal = methods_
            break
    else:
        if set_value_ == None:
            raise AttributeError, "I don't have any knowledge of type: '%s'" % widget
    
    if get_value_:
        get_value = lambda: get_value_(widget)
    else:
        get_value = getattr(widget, getter)

    if set_value_:
        set_value = lambda v: set_value_(widget, v)
    else:
        set_value = getattr(widget, setter)
    
    if conf.hasKey(key):
        try:
            v = conf.getStrict(key)
        except TypeError:
            log.warn("uistuff.loadDialogWidget: Key '%s' from conf had the wrong type '%s', ignored" % \
                     (key, type(conf.getStrict(key))))
            if first_value != None:
                conf.set(key, first_value)
            else: conf.set(key, get_value())
        else:
            set_value(v)
    elif first_value != None:
        conf.set(key, first_value)
        set_value(conf.getStrict(key))
    else:
        log.warn("Didn't load widget \"%s\": no conf value and no first_value arg" % \
                 widget_name)
コード例 #32
0
def probeEndGameTable(board):

    fen = board.asFen().split()[0] + " w - - 0 1"
    if (fen, board.color) in table:
        return table[(fen, board.color)]

    # k4it has all 6-men tables except 5 vs. 1
    whites = bitLength(board.friends[WHITE])
    blacks = bitLength(board.friends[BLACK])
    if whites >= 5 or blacks >= 5 or whites + blacks >= 7:
        return []

    # Request the page
    url = (URL + fen).replace(" ", "%20")
    try:
        f = urllib.urlopen(url)
    except IOError, e:
        log.warn("Unable to read endgame tablebase from the Internet: %s" %
                 repr(e))
        return []
コード例 #33
0
 def _set_active(self, active):
     if type(active) != int:
         raise TypeError
     if active == self._active: return
     if active >= len(self._items):
         log.warn("Tried to set combobox to %d, but it has only got %d items"
                  % (active, len(self._items)))
         return
     oldactive = self._active
     # take care the case when last used engine was uninstalled
     self._active = (active < len(self._items) and [active] or [1])[0]
     self.emit("changed", oldactive)
     text, icon = self._items[self._active]
     self.label.set_markup (self.markup[0] + text + self.markup[1])
     if icon != None:
         self.hbox.set_spacing(6)
         self.image.set_from_pixbuf(icon)
     else:
         self.hbox.set_spacing(0)
         self.image.clear()
コード例 #34
0
 def run (self):
     # List available engines
     for engine in self.dom.findall('engine'):
         ######
         # Find the known and installed engines on the system
         ######
         
         # Validate slightly
         if not engine.get("protocol") or not engine.get("binname"):
             log.warn("Engine '%s' lacks protocol/binname attributes. Skipping\n" % \
                      engine.get('binname'))
             continue
         
         # Look up
         rundata = self.__findRundata(engine)
         if not rundata:
             # Engine is not available on the system
             continue
         
         if self.__needClean(rundata, engine):
             engine2 = self.__clean(rundata, engine)
             if engine2 is None:
                 # No longer suported
                 continue
             self.dom.getroot().remove(engine)
             self.dom.getroot().append(engine2)
             engine = engine2
             engine.set('recheck', 'true')
         
         self._engines[engine.get("binname")] = engine
     
     ######
     # Save the xml
     ######
     def cb(self_, *args):
         try:
             with open(self.xmlpath, "w") as f:
                 self.dom.write(f)
         except IOError, e:
             log.error("Saving enginexml raised exception: %s\n" % \
                       ", ".join(str(a) for a in e.args))
コード例 #35
0
 def start (self, func, *args, **kw):
     self.lock.acquire()
     
     try:
         a = self.queue.get_nowait()
     except Queue.Empty:
         if self.threads < maxThreads:
             self.threads += 1
             a = self.Worker(self.queue)
             a.setDaemon(True)
             a.start()
         else:
             a = self.queue.get(timeout=5)
             from pychess.System.Log import log
             log.warn("Couldn't get a thread after 5s")
             a = self.queue.get()
     
     a.func = lambda: func(*args, **kw)
     a.name = self._getThreadName(a, func)
     a.wcond.acquire()
     a.wcond.notify()
     a.wcond.release()
     
     self.lock.release()
コード例 #36
0
ファイル: CECPEngine.py プロジェクト: btrent/knave
    def __startBlocking (self):
        if self.protover == 1:
            #self.emit("readyForMoves")
        if self.protover == 2:
            try:
                r = self.returnQueue.get(True, max(self.timeout-time.time(),0))
                if r == "not ready":
                    # The engine has sent done=0, and parseLine has added more
                    # time to self.timeout
                    r = self.returnQueue.get(True, max(self.timeout-time.time(),0))
            except Queue.Empty:
                log.warn("Got timeout error\n", self.defname)
                #self.emit("readyForOptions")
                #self.emit("readyForMoves")
            else:
                if r == 'del':
                    raise PlayerIsDead
                assert r == "ready"
    
    def __onReadyForOptions_before (self, self_):
        self.readyOptions = True
    
    def __onReadyForOptions (self, self_):
        # This is no longer needed
        #self.timeout = time.time()
        
        # We always want post turned on so the Engine Output sidebar can
        # show those things  -Jonas Thiem
        print >> self.engine, "post"
        
        for command in self.optionQueue:
            print >> self.engine, command
        
    def __onReadyForMoves (self, self_):
        # If we are an analyzer, this signal was already called in a different
        # thread, so we can safely block it.
        if self.mode in (ANALYZING, INVERSE_ANALYZING):
            if not self.board:
                self.board = Board(setup=True)
            self.__sendAnalyze(self.mode == INVERSE_ANALYZING)
        
        self.readyMoves = True
        semisynced(lambda s:None)(self)
    
    #===========================================================================
    #    Ending the game
    #===========================================================================
    
    @semisynced
    def end (self, status, reason):
        if self.connected:
            # We currently can't fillout the comment "field" as the repr strings
            # for reasons and statuses lies in Main.py
            # Creating Status and Reason class would solve this
            if status == DRAW:
                print >> self.engine, "result 1/2-1/2 {?}"
            elif status == WHITEWON:
                print >> self.engine, "result 1-0 {?}"
            elif status == BLACKWON:
                print >> self.engine, "result 0-1 {?}"
            else:
                print >> self.engine, "result * {?}"
            
            # Make sure the engine exits and do some cleaning
            self.kill(reason)
    
    def kill (self, reason):
        """ Kills the engine, starting with the 'quit' command, then sigterm and
            eventually sigkill.
            Returns the exitcode, or if engine have already been killed, returns
            None """
        if self.connected:
            self.connected = False
            try:
                try:
                    print >> self.engine, "quit"
                    self.returnQueue.put("del")
                    self.engine.gentleKill()
                
                except OSError, e:
                    # No need to raise on a hang up error, as the engine is dead
                    # anyways
                    if e.errno == 32:
                        log.warn("Hung up Error", self.defname)
                        return e.errno
                    else: raise
            
            finally:
                # Clear the analyzed data, if any
                #self.emit("analyze", [])
    
    #===========================================================================
    #    Send the player move updates
    #===========================================================================

    def setBoard (self, board):
        self.setBoardList([board], [])
    
    @semisynced
    def putMove (self, board1, move, board2):
        """ Sends the engine the last move made (for spectator engines).
            @param board1: The current board
            @param move: The last move made
            @param board2: The board before the last move was made
        """
        # If the spactator engine analyzing an older position, let it do
        if self.board != board2:
            return

        self.board = board1
        
        if not board2:
            self.__tellEngineToPlayCurrentColorAndMakeMove()
            self.movenext = False
            return
        
        if self.mode == INVERSE_ANALYZING:
            self.board = self.board.switchColor()
            self.__printColor()
            if self.engineIsInNotPlaying: print >> self.engine, "force"
        
        self.__usermove(board2, move)
        
        if self.mode == INVERSE_ANALYZING:
            if self.board.board.opIsChecked():
                # Many engines don't like positions able to take down enemy
                # king. Therefore we just return the "kill king" move
                # automaticaly
                #self.emit("analyze", [([getMoveKillingKing(self.board)], MATE_VALUE-1)])
                return
            self.__printColor()
            if self.engineIsInNotPlaying: print >> self.engine, "force"
    
    def makeMove (self, board1, move, board2):
        """ Gets a move from the engine (for player engines).
            @param board1: The current board
            @param move: The last move made
            @param board2: The board before the last move was made
            @return: The move the engine decided to make
        """
        log.debug("makeMove: move=%s self.movenext=%s board1=%s board2=%s self.board=%s\n" % \
            (move, self.movenext, board1, board2, self.board), self.defname)
        assert self.readyMoves
        
        self.boardLock.acquire()
        try:
            if self.board == board1 or not board2 or self.movenext:
                self.board = board1
                self.__tellEngineToPlayCurrentColorAndMakeMove()
                self.movenext = False
            else:
                self.board = board1
                self.__usermove(board2, move)
                
                if self.engineIsInNotPlaying:
                    self.__tellEngineToPlayCurrentColorAndMakeMove()
        finally:
            self.boardLock.release()
        self.waitingForMove = True
        self.readyForMoveNowCommand = True
        
        # Parse outputs
        r = self.returnQueue.get()
        if r == "not ready":
            log.warn("Engine seems to be protover=2, but is treated as protover=1", self.defname)
            r = self.returnQueue.get()
        if r == "ready":
            r = self.returnQueue.get()
        if r == "del":
            raise PlayerIsDead, "Killed by foreign forces"
        if r == "int":
            raise TurnInterrupt
        
        self.waitingForMove = False
        self.readyForMoveNowCommand = False
        assert isinstance(r, Move), r
        return r
    
    @semisynced
    def updateTime (self, secs, opsecs):
        if self.features["time"]:
            print >> self.engine, "time", int(secs*100*self.timeHandicap)
            print >> self.engine, "otim", int(opsecs*100)
    
    #===========================================================================
    #    Standard options
    #===========================================================================
    
    def setOptionAnalyzing (self, mode):
        self.mode = mode
    
    def setOptionInitialBoard (self, model):
        # We don't use the optionQueue here, as set board prints a whole lot of
        # stuff. Instead we just call it, and let semisynced handle the rest.
        self.setBoardList(model.boards[:], model.moves[:])
    
    @semisynced
    def setBoardList (self, boards, moves):
        # Notice: If this method is to be called while playing, the engine will
        # need 'new' and an arrangement similar to that of 'pause' to avoid
        # the current thought move to appear
        
        self.boardLock.acquire()
        try:
            if self.mode == INVERSE_ANALYZING:
                self.board = self.board.switchColor()
                self.__printColor()
            
            self.__tellEngineToStopPlayingCurrentColor()
            
            self.__setBoard(boards[0])
            
            self.board = boards[-1]
            for board, move in zip(boards[:-1], moves):
                self.__usermove(board, move)
            
            if self.mode in (ANALYZING, INVERSE_ANALYZING):
                self.board = boards[-1]
            
            if self.mode == INVERSE_ANALYZING:
                self.board = self.board.switchColor()
                self.__printColor()
                if self.engineIsInNotPlaying:
                    print >> self.engine, "force"
            
            # The called of setBoardList will have to repost/analyze the
            # analyzer engines at this point.
        finally:
            self.boardLock.release()
    
    def setOptionVariant (self, variant):
        if self.features["variants"] is None:
            log.warn("setOptionVariant: engine doesn't support variants\n", self.defname)
            return
        
        if variant in variants.values() and not variant.standard_rules:
            assert variant.cecp_name in self.features["variants"], \
                    "%s doesn't support %s variant" % (self, variant.cecp_name)
            self.optionQueue.append("variant %s" % variant.cecp_name)
    
        #==================================================#
        #    Strength system                               #
        #==================================================#
        #          Strength  Depth  Ponder  Time handicap  #
        #          1         1      o       1,258%         #
        #          2         2      o       1,584%         #
        #          3         3      o       1.995%         #
        #                                                  #
        #         19         o      x       79,43%         #
        #         20         o      x       o              #
        #==================================================#
    
    def setOptionStrength (self, strength, forcePonderOff):
        self.strength = strength
        
        if strength <= 19:
            self.__setTimeHandicap(0.01 * 10**(strength/10.))
        
        if strength <= 18:
            self.__setDepth(strength)

        # Crafty ofers 100 skill levels
        if "crafty" in self.features["myname"].lower() and strength <= 19:
            self.optionQueue.append("skill %s" % strength*5)
        
        self.__setPonder(strength >= 19 and not forcePonderOff)
        
        if strength == 20:
            self.optionQueue.append("egtb")
        else:
            self.optionQueue.append("random")
    
    def __setDepth (self, depth):
        self.optionQueue.append("sd %d" % depth)
    
    def __setTimeHandicap (self, timeHandicap):
        self.timeHandicap = timeHandicap
    
    def __setPonder (self, ponder):
        if ponder:
            self.optionQueue.append("hard")
        else:
            self.optionQueue.append("hard")
            self.optionQueue.append("easy")
    
    def setOptionTime (self, secs, gain):
        # Notice: In CECP we apply time handicap in updateTime, not in
        #         setOptionTime. 
        
        minutes = int(secs / 60)
        secs = int(secs % 60)
        s = str(minutes)
        if secs:
            s += ":" + str(secs)
        
        self.optionQueue.append("level 0 %s %d" % (s, gain))

    #===========================================================================
    #    Option handling
    #===========================================================================
    
    def setOption (self, key, value):
        """ Set an option, which will be sent to the engine, after the
            'readyForOptions' signal has passed.
            If you want to know the possible options, you should go to
            engineDiscoverer or use the getOption, getOptions and hasOption
            methods, while you are in your 'readyForOptions' signal handler """ 
        if self.readyMoves:
            log.warn("Options set after 'readyok' are not sent to the engine", self.defname)
        if key == "cores":
            self.optionQueue.append("cores %s" % value)
        elif key == "memory":
            self.optionQueue.append("memory %s" % value)
        elif key.lower() == "ponder":
            self.__setPonder(value==1)
        else:
            self.optionQueue.append("option %s=%s" % (key, value))
    
    #===========================================================================
    #    Interacting with the player
    #===========================================================================
    
    @semisynced
    def pause (self):
        """ Pauses engine using the "pause" command if available. Otherwise put
            engine in force mode. By the specs the engine shouldn't ponder in
            force mode, but some of them do so anyways. """
        
        log.debug("pause: self=%s\n" % self, self.defname)
        self.engine.pause()
        return
        
        if self.mode in (ANALYZING, INVERSE_ANALYZING):
            return
        if self.features["pause"]:
            print >> self.engine, "pause"
        elif self.board:
            self.__tellEngineToStopPlayingCurrentColor()
            self._blockTillMove()
    
    @semisynced
    def resume (self):
        log.debug("resume: self=%s\n" % self, self.defname)
        self.engine.resume()
        return
        
        if self.mode not in (ANALYZING, INVERSE_ANALYZING):
            if self.features["pause"]:
                print "features resume"
                print >> self.engine, "resume"
            elif self.board:
                print "go resume"
                self.__tellEngineToPlayCurrentColorAndMakeMove()
    
    @semisynced
    def hurry (self):
        log.debug("hurry: self.waitingForMove=%s self.readyForMoveNowCommand=%s\n" % \
            (self.waitingForMove, self.readyForMoveNowCommand), self.defname)
        if self.waitingForMove and self.readyForMoveNowCommand:
            self.__tellEngineToMoveNow()
            self.readyForMoveNowCommand = False
    
    @semisynced
    def spectatorUndoMoves (self, moves, gamemodel):
        log.debug("spectatorUndoMoves: moves=%s gamemodel.ply=%s gamemodel.boards[-1]=%s self.board=%s\n" % \
            (moves, gamemodel.ply, gamemodel.boards[-1], self.board), self.defname)
        if self.mode == INVERSE_ANALYZING:
            self.board = self.board.switchColor()
            self.__printColor()
            if self.engineIsInNotPlaying: print >> self.engine, "force"
        
        for i in xrange(moves):
            print >> self.engine, "undo"
        
        self.board = gamemodel.boards[-1]
        
        if self.mode == INVERSE_ANALYZING:
            self.board = self.board.switchColor()
            self.__printColor()
            if self.engineIsInNotPlaying: print >> self.engine, "force"
    
    @semisynced
    def playerUndoMoves (self, moves, gamemodel):
        log.debug("playerUndoMoves: moves=%s gamemodel.ply=%s gamemodel.boards[-1]=%s self.board=%s\n" % \
            (moves, gamemodel.ply, gamemodel.boards[-1], self.board), self.defname)
        
        if gamemodel.curplayer != self and moves % 2 == 1:
            # Interrupt if we were searching, but should no longer do so
            self.returnQueue.put("int")
        
        self.__tellEngineToStopPlayingCurrentColor()
        if self.board and gamemodel.status in UNFINISHED_STATES:
            log.debug("playerUndoMoves: self.__tellEngineToMoveNow(), self._blockTillMove()\n")
            self.__tellEngineToMoveNow()
            self._blockTillMove()
        
        for i in xrange(moves):
            print >> self.engine, "undo"
        
        if gamemodel.curplayer == self:
            self.board = gamemodel.boards[-1]
            self.__tellEngineToPlayCurrentColorAndMakeMove()
        else:
            self.board = None
    
    #===========================================================================
    #    Offer handling
    #===========================================================================
    
    def offer (self, offer):
        if offer.type == DRAW_OFFER:
            if self.features["draw"]:
                print >> self.engine, "draw"
        else:
            #self.emit("accept", offer)
    
    def offerError (self, offer, error):
        if self.features["draw"]:
            # We don't keep track if engine draws are offers or accepts. We just
            # Always assume they are accepts, and if they are not, we get this
            # error and emit offer instead
            if offer.type == DRAW_OFFER and error == ACTION_ERROR_NONE_TO_ACCEPT:
                #self.emit("offer", Offer(DRAW_OFFER))
    
    #===========================================================================
    #    Internal
    #===========================================================================
    
    def __usermove (self, board, move):
        if self.features["usermove"]:
            self.engine.write("usermove ")
        
        if self.features["san"]:
            print >> self.engine, toSAN(board, move)
        else:
            cn = CASTLE_KK
            if board.variant == FISCHERRANDOMCHESS:
                cn = CASTLE_SAN
            print >> self.engine, toAN(board, move, short=True, castleNotation=cn)
    
    def __tellEngineToMoveNow (self):
        if self.features["sigint"]:
            self.engine.sigint()
        print >> self.engine, "?"
    
    def __tellEngineToStopPlayingCurrentColor (self):
        print >> self.engine, "force"
        self.engineIsInNotPlaying = True
    
    def __tellEngineToPlayCurrentColorAndMakeMove (self):
        self.__printColor()
        print >> self.engine, "go"
        self.engineIsInNotPlaying = False
    
    def __sendAnalyze (self, inverse=False):
        self.__tellEngineToStopPlayingCurrentColor()
        
        if inverse:
            self.board = self.board.setColor(1-self.color)
            self.__printColor()
            if self.engineIsInNotPlaying: print >> self.engine, "force"
            self.mode = INVERSE_ANALYZING
        else:
            self.mode = ANALYZING
        
        print >> self.engine, "post"
        print >> self.engine, "analyze"
        
        # workaround for crafty not sending analysis after it has found a mating line
        # http://code.google.com/p/pychess/issues/detail?id=515
        if "crafty" in self.features["myname"].lower():
            print >> self.engine, "noise 0"
    
    def __printColor (self):
        if self.features["colors"] or self.mode == INVERSE_ANALYZING:
            if self.board.color == WHITE:
                print >> self.engine, "white"
            else: print >> self.engine, "black"
    
    def __setBoard (self, board):
        if self.features["setboard"]:
            self.__tellEngineToStopPlayingCurrentColor()
            fen = board.asFen()
            if self.mode == INVERSE_ANALYZING:
                # Some engine doesn't support feature "colors" (f.e: TJchess)
                # so "black" and "white" command doesn't change the side to move
                fen_arr = fen.split()
                if self.board.color == WHITE:
                    if fen_arr[1] == "b":
                        fen_arr[1] = "w"
                        fen = " ".join(fen_arr)
                else:
                    if fen_arr[1] == "w":
                        fen_arr[1] = "b"
                        fen = " ".join(fen_arr)
            print >> self.engine, "setboard", fen
        else:
            # Kludge to set black to move, avoiding the troublesome and now
            # deprecated "black" command. - Equal to the one xboard uses
            self.__tellEngineToStopPlayingCurrentColor()
            if board.color == BLACK:
                print >> self.engine, "a2a3"
            print >> self.engine, "edit"
            print >> self.engine, "#"
            for color in WHITE, BLACK:
                for y, row in enumerate(board.data):
                    for x, piece in enumerate(row):
                        if not piece or piece.color != color:
                            continue
                        sign = reprSign[piece.sign]
                        cord = repr(Cord(x,y))
                        print >> self.engine, sign+cord
                print >> self.engine, "c"
            print >> self.engine, "."
    
    def _blockTillMove (self):
        saved_state = self.boardLock._release_save()
        log.debug("_blockTillMove(): acquiring self.movecon lock\n", self.defname)
        self.movecon.acquire()
        log.debug("_blockTillMove(): self.movecon acquired\n", self.defname)
        try:
            log.debug("_blockTillMove(): doing self.movecon.wait\n", self.defname)
            self.movecon.wait()
        finally:
            log.debug("_blockTillMove(): releasing self.movecon..\n", self.defname)
            self.movecon.release()
            self.boardLock._acquire_restore(saved_state)
    
    #===========================================================================
    #    Parsing
    #===========================================================================
    
    def parseLines (self, engine, lines):
        for line in lines:
            self.__parseLine(line)
    
    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"]
コード例 #37
0
     
     return engine
 
 def __discoverE (self, engine):
     subproc = self.initEngine (engine, BLACK)
     try:
         subproc.connect('readyForOptions', self.__discoverE2, engine)
         subproc.prestart() # Sends the 'start line'
         subproc.start()
     except SubProcessError, e:
         log.warn("Engine %s failed discovery: %s" % (engine.get('binname'),e))
         self.emit("engine_failed", engine.get('binname'), engine)
     except PlayerIsDead, e:
         # Check if the player died after engine_discovered by our own hands
         if not self.toBeRechecked[engine]:
             log.warn("Engine %s failed discovery: %s" % (engine.get('binname'),e))
             self.emit("engine_failed", engine.get('binname'), engine)
 
 def __discoverE2 (self, subproc, engine):
     if engine.get("protocol") == "uci":
         fresh = self.__fromUCIProcess(subproc)
     elif engine.get("protocol") == "cecp":
         fresh = self.__fromCECPProcess(subproc)
     mergeElements(engine, fresh)
     
     exitcode = subproc.kill(UNKNOWN_REASON)
     if exitcode:
         log.debug("Engine failed %s\n" % self.getName(engine))
         self.emit("engine_failed", engine.get('binname'), engine)
         return
     
コード例 #38
0
                
                if result.startswith("Win"):
                    if color == WHITE:
                        state = (WHITEWON, steps)
                    else: state = (BLACKWON, steps)
                elif result.startswith("Lose"):
                    if color == WHITE:
                        state = (BLACKWON, steps)
                    else: state = (WHITEWON, steps)
                
                moves.append( (move,state,steps) )
            
            if moves:
                table[(fen,color)] = moves
            elif color == board.color and board.opIsChecked():
                log.warn("Asked endgametable for a won position: %s" % fen)
            elif color == board.color:
                log.warn("Couldn't get %s data for position %s.\nData was: %s" %
                         (reprColor[color], fen, repr(data)))
        except (KeyError, ValueError):
            log.warn("Couldn't parse %s data for position %s.\nData was: %s" %
                     (reprColor[color], fen, repr(data)))
            table[(fen, color)] = [] # Don't try again.
    
    if (fen,board.color) in table:
        return table[(fen,board.color)]
    return []

if __name__ == "__main__":
    from pychess.Utils.lutils.LBoard import LBoard
    from pychess.Utils.lutils.lmove import listToSan
コード例 #39
0
ファイル: CECPEngine.py プロジェクト: btrent/knave
    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"]
コード例 #40
0
                    if color == WHITE:
                        state = (WHITEWON, steps)
                    else:
                        state = (BLACKWON, steps)
                elif result.startswith("Lose"):
                    if color == WHITE:
                        state = (BLACKWON, steps)
                    else:
                        state = (WHITEWON, steps)

                moves.append((move, state, steps))

            if moves:
                table[(fen, color)] = moves
            elif color == board.color and board.opIsChecked():
                log.warn("Asked endgametable for a won position: %s" % fen)
            elif color == board.color:
                log.warn(
                    "Couldn't get %s data for position %s.\nData was: %s" %
                    (reprColor[color], fen, repr(data)))
        except (KeyError, ValueError):
            log.warn("Couldn't parse %s data for position %s.\nData was: %s" %
                     (reprColor[color], fen, repr(data)))
            table[(fen, color)] = []  # Don't try again.

    if (fen, board.color) in table:
        return table[(fen, board.color)]
    return []


if __name__ == "__main__":
コード例 #41
0
    def __parseLine (self, line):
        if not self.connected: return
        parts = line.split()
        if not parts: return
        
        #---------------------------------------------------------- Initializing
        if parts[0] == "id":
            self.ids[parts[1]] = " ".join(parts[2:])
            return
        
        if parts[0] == "uciok":
            self.emit("readyForOptions")
            return
        
        if parts[0] == "readyok":
            self.emit("readyForMoves")
            return
        
        #------------------------------------------------------- Options parsing
        if parts[0] == "option":
            dic = {}
            last = 1
            varlist = []
            for i in xrange (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)
                    else:
                        dic[key] = value
                        
                    last = i
            if varlist:
                dic["vars"] = varlist
            
            name = dic["name"]
            del dic["name"]
            self.options[name] = dic
            return
        
        #---------------------------------------------------------------- A Move
        if self.mode == NORMAL and parts[0] == "bestmove":
            with self.moveLock:
                self.needBestmove = False
                self.__sendQueuedGo()
                
                if self.ignoreNext:
                    log.debug("__parseLine: line='%s' self.ignoreNext==True, returning\n" % \
                        line.strip(), self.defname)
                    self.ignoreNext = False
                    self.readyForStop = True
                    return
                
                if not self.waitingForMove:
                    log.warn("__parseLine: self.waitingForMove==False, ignoring move=%s\n" % \
                        parts[1], self.defname)
                    self.pondermove = None
                    return
                self.waitingForMove = False
                
                move = parseAN(self.board, parts[1])
                
                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\n" % \
                        (repr(move), self.board), self.defname)
                    self.returnQueue.put('del')
                    return
                
                self.board = self.board.move(move)
                log.debug("__parseLine: applied move=%s to self.board=%s\n" % \
                    (move, self.board), self.defname)
                
                if self.getOption('Ponder'):
                    self.pondermove = None
                    # An engine may send an empty ponder line, simply to clear.
                    if len(parts) == 4 and self.board:
                        # 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 = parseAN(self.board, parts[3])
                        except ParsingError:
                            pass
                        else:
                            if validate(self.board, pondermove):
                                self.pondermove = pondermove
                                self._startPonder()
                
                self.returnQueue.put(move)
                log.debug("__parseLine: put move=%s into self.returnQueue=%s\n" % \
                    (move, self.returnQueue.queue), self.defname)
                return
        
        #----------------------------------------------------------- An Analysis
        if self.mode != NORMAL and parts[0] == "info" and "pv" in parts:
            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"
                    sign = score/abs(score)
                    score = sign * (MATE_VALUE-abs(score))
            
            movstrs = parts[parts.index("pv")+1:]
            try:
                moves = listToMoves (self.board, movstrs, AN, validate=True, ignoreErrors=False)
            except ParsingError, e:
                # ParsingErrors may happen when parsing "old" lines from
                # analyzing engines, which haven't yet noticed their new tasks
                log.debug("__parseLine: Ignored (%s) from analyzer: ParsingError%s\n" % \
                    (' '.join(movstrs),e), self.defname)
                return
            
            self.emit("analyze", moves, score)
            return
コード例 #42
0
    def __parseLine(self, line):
        if not self.connected: return
        parts = line.split()
        if not parts: return

        #---------------------------------------------------------- Initializing
        if parts[0] == "id":
            self.ids[parts[1]] = " ".join(parts[2:])
            return

        if parts[0] == "uciok":
            self.emit("readyForOptions")
            return

        if parts[0] == "readyok":
            self.emit("readyForMoves")
            return

        #------------------------------------------------------- Options parsing
        if parts[0] == "option":
            dic = {}
            last = 1
            varlist = []
            for i in xrange(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)
                    else:
                        dic[key] = value

                    last = i
            if varlist:
                dic["vars"] = varlist

            name = dic["name"]
            del dic["name"]
            self.options[name] = dic
            return

        #---------------------------------------------------------------- A Move
        if self.mode == NORMAL and parts[0] == "bestmove":
            with self.moveLock:
                self.needBestmove = False
                self.__sendQueuedGo()

                if self.ignoreNext:
                    log.debug("__parseLine: line='%s' self.ignoreNext==True, returning\n" % \
                        line.strip(), self.defname)
                    self.ignoreNext = False
                    self.readyForStop = True
                    return

                if not self.waitingForMove:
                    log.warn("__parseLine: self.waitingForMove==False, ignoring move=%s\n" % \
                        parts[1], self.defname)
                    self.pondermove = None
                    return
                self.waitingForMove = False

                move = parseAN(self.board, parts[1])

                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\n" % \
                        (repr(move), self.board), self.defname)
                    self.returnQueue.put('del')
                    return

                self.board = self.board.move(move)
                log.debug("__parseLine: applied move=%s to self.board=%s\n" % \
                    (move, self.board), self.defname)

                if self.getOption('Ponder'):
                    self.pondermove = None
                    # An engine may send an empty ponder line, simply to clear.
                    if len(parts) == 4 and self.board:
                        # 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 = parseAN(self.board, parts[3])
                        except ParsingError:
                            pass
                        else:
                            if validate(self.board, pondermove):
                                self.pondermove = pondermove
                                self._startPonder()

                self.returnQueue.put(move)
                log.debug("__parseLine: put move=%s into self.returnQueue=%s\n" % \
                    (move, self.returnQueue.queue), self.defname)
                return

        #----------------------------------------------------------- An Analysis
        if self.mode != NORMAL and parts[0] == "info" and "pv" in parts:
            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"
                    sign = score / abs(score)
                    score = sign * (MATE_VALUE - abs(score))

            movstrs = parts[parts.index("pv") + 1:]
            try:
                moves = listToMoves(self.board,
                                    movstrs,
                                    AN,
                                    validate=True,
                                    ignoreErrors=False)
            except ParsingError, e:
                # ParsingErrors may happen when parsing "old" lines from
                # analyzing engines, which haven't yet noticed their new tasks
                log.debug("__parseLine: Ignored (%s) from analyzer: ParsingError%s\n" % \
                    (' '.join(movstrs),e), self.defname)
                return

            self.emit("analyze", moves, score)
            return