def on_icc_offers_in_my_game(self, data): log.debug("DG_OFFERS_IN_MY_GAME %s" % data) # gamenumber wdraw bdraw wadjourn badjourn wabort babort wtakeback btakeback gamenumber, wdraw, bdraw, wadjourn, badjourn, wabort, babort, wtakeback, btakeback = map( int, data.split()) if wdraw or bdraw: offertype = DRAW_OFFER elif wadjourn or badjourn: offertype = ADJOURN_OFFER elif wabort or babort: offertype = ABORT_OFFER elif wtakeback or btakeback: offertype = TAKEBACK_OFFER else: log.debug( "ICCOfferManager.on_icc_offers_in_my_game: unknown offer data: %s" % data) return index = gamenumber * 100000 + OFFERS.index(offertype) if offertype == TAKEBACK_OFFER: parameters = wtakeback if wtakeback else btakeback offer = Offer(offertype, param=parameters, index=index) else: offer = Offer(offertype, index=index) self.offers[offer.index] = offer log.debug( "ICCOfferManager.on_icc_offers_in_my_game: emitting onOfferAdd: %s" % offer) self.emit("onOfferAdd", offer)
def __init__(self, connection): GObject.GObject.__init__(self) self.connection = connection self.connection.expect_line( self.onOfferAdd, "<p(t|f)> (\d+) w=%s t=(\w+) p=(.+)" % names) self.connection.expect_line(self.onOfferRemove, "<pr> (\d+)") for ficsstring, offer, error in ( ( "You cannot switch sides once a game is underway.", Offer(SWITCH_OFFER), ACTION_ERROR_SWITCH_UNDERWAY, ), ( "Opponent is not out of time.", Offer(FLAG_CALL), ACTION_ERROR_NOT_OUT_OF_TIME, ), ( "The clock is not ticking yet.", Offer(PAUSE_OFFER), ACTION_ERROR_CLOCK_NOT_STARTED, ), ( "The clock is not ticking.", Offer(FLAG_CALL), ACTION_ERROR_CLOCK_NOT_STARTED, ), ( "The clock is not paused.", Offer(RESUME_OFFER), ACTION_ERROR_CLOCK_NOT_PAUSED, ), ): self.connection.expect_line( lambda match: self.emit("onActionError", offer, error), ficsstring) self.connection.expect_line( self.notEnoughMovesToUndo, "There are (?:(no)|only (\d+) half) moves in your game\.", ) self.connection.expect_line( self.noOffersToAccept, "There are no ([^ ]+) offers to (accept).") self.connection.expect_line( self.onOfferDeclined, "\w+ declines the (draw|takeback|pause|unpause|abort|adjourn) request\.", ) self.lastPly = 0 self.offers = {} self.connection.client.run_command("iset pendinfo 1")
def end(self, status, reason): if self.status in UNFINISHED_STATES: self.__disconnect() if self.inControl: self.connection.om.offer(Offer(ABORT_OFFER), -1) self.connection.om.offer(Offer(RESIGNATION), -1) else: self.connection.bm.unobserve(self.gameno) GameModel.end(self, status, reason)
def callback (infobar, response, message): if response == 1: # newGameDialog uses ionest uses gamenanny uses newGameDialog... from pychess.widgets.newGameDialog import createRematch createRematch(gamemodel) elif response == 2: if gamemodel.ply > 1: offer = Offer(TAKEBACK_OFFER, gamemodel.ply-2) else: offer = Offer(TAKEBACK_OFFER, gamemodel.ply-1) if gamemodel.players[0].__type__ == LOCAL: gamemodel.players[0].emit("offer", offer) else: gamemodel.players[1].emit("offer", offer) return False
def end(self, status, reason): if self.status in UNFINISHED_STATES: self.__disconnect() if self.isObservationGame(): self.connection.bm.unobserve(self.ficsgame) else: self.connection.om.offer(Offer(ABORT_OFFER), -1) self.connection.om.offer(Offer(RESIGNATION), -1) if status == KILLED: GameModel.kill(self, reason) else: GameModel.end(self, status, reason)
def onOfferAdd(self, match): log.debug("OfferManager.onOfferAdd: match.string=%s\n" % match.string) tofrom, index, offertype, parameters = match.groups() if tofrom == "t": # ICGameModel keeps track of the offers we've sent ourselves, so we # don't need this return if offertype not in strToOfferType: log.error("OfferManager.onOfferAdd: Declining unknown offer type: " + \ "offertype=%s parameters=%s index=%s\n" % (offertype, parameters, index)) print >> self.connection.client, "decline", index offertype = strToOfferType[offertype] if offertype == TAKEBACK_OFFER: offer = Offer(offertype, param=int(parameters), index=int(index)) else: offer = Offer(offertype, index=int(index)) self.offers[offer.index] = offer if offer.type == MATCH_OFFER: if matchreUntimed.match(parameters) != None: fname, frating, col, tname, trating, rated, type = \ matchreUntimed.match(parameters).groups() mins = "0" incr = "0" else: fname, frating, col, tname, trating, rated, type_short, mins, incr, type = \ matchre.match(parameters).groups() if not type or "adjourned" in type: type = type_short if type.split()[-1] in unsupportedtypes: self.decline(offer) else: rating = frating.strip() rating = rating.isdigit() and rating or "0" rated = rated == "unrated" and "u" or "r" match = { "tp": convertName(type), "w": fname, "rt": rating, "r": rated, "t": mins, "i": incr } self.emit("onChallengeAdd", index, match) else: log.debug("OfferManager.onOfferAdd: emitting onOfferAdd: %s\n" % offer) self.emit("onOfferAdd", offer)
def zero_reached(self, timemodel, color): if conf.get('autoCallFlag', False) and \ self.gamemodel.status == RUNNING and \ timemodel.getPlayerTime(1-self.color) <= 0: log.log('Automatically sending flag call on behalf of player %s.' % self.name) self.emit("offer", Offer(FLAG_CALL))
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))
def zero_reached(self, timemodel, color): if conf.get('autoCallFlag'): if self.status == RUNNING and timemodel.getPlayerTime(color) <= 0: log.info( 'Automatically sending flag call on behalf of player %s.' % self.players[1 - color].name) self.players[1 - color].emit("offer", Offer(FLAG_CALL))
def notEnoughMovesToUndo(self, match): ply = match.groups()[0] or match.groups()[1] if ply == "no": ply = 0 else: ply = int(ply) offer = Offer(TAKEBACK_OFFER, param=ply) self.emit("onActionError", offer, ACTION_ERROR_TOO_LARGE_UNDO)
def cb(messageDialog, responseId): if responseId == 0: if gamemodel.players[0].__type__ == REMOTE: gamemodel.players[0].offerRematch() else: gamemodel.players[1].offerRematch() elif responseId == 1: from pychess.widgets.newGameDialog import createRematch createRematch(gamemodel) elif responseId == 2: if gamemodel.curplayer.__type__ == LOCAL and gamemodel.ply > 1: offer = Offer(TAKEBACK_OFFER, gamemodel.ply - 2) else: offer = Offer(TAKEBACK_OFFER, gamemodel.ply - 1) if gamemodel.players[0].__type__ == LOCAL: gamemodel.players[0].emit("offer", offer) else: gamemodel.players[1].emit("offer", offer)
def noOffersToAccept(self, match): offertype, request = match.groups() if request == "accept": error = ACTION_ERROR_NONE_TO_ACCEPT elif request == "withdraw": error = ACTION_ERROR_NONE_TO_WITHDRAW elif request == "decline": error = ACTION_ERROR_NONE_TO_DECLINE offer = Offer(strToOfferType[offertype]) self.emit("onActionError", offer, error)
def zero_reached(self, timemodel, color): if ( conf.get("autoCallFlag") and self.gamemodel.status == RUNNING and timemodel.getPlayerTime(1 - self.color) <= 0 ): log.info( "Automatically sending flag call on behalf of player %s." % self.name ) self.emit("offer", Offer(FLAG_CALL))
def answer (message): try: data = urlopen("http://www.pandorabots.com/pandora/talk?botid=8d034368fe360895", urlencode({"message":message, "botcust2":"x"}).encode("utf-8")).read().decode('utf-8') except IOError as e: log.warning("Couldn't answer message from online bot: '%s'" % e, extra={"task":self.defname}) return ss = "<b>DMPGirl:</b>" es = "<br>" answer = data[data.find(ss)+len(ss) : data.find(es,data.find(ss))] self.emit("offer", Offer(CHAT_ACTION, answer))
def emit_action(self, action, param): # If there are two or more tabs open, we have to ensure us that it is # us who are in the active tab, and not the others if self.gmwidg != cur_gmwidg(): return # If there are two human players, we have to ensure us that it was us # who did the action, and not the others if self.gamemodel.players[1 - self.color].__type__ == LOCAL: if action == HURRY_ACTION: if self.gamemodel.boards[-1].color == self.color: return else: if self.gamemodel.boards[-1].color != self.color: return self.emit("offer", Offer(action, param=param))
def emit_action (self, action, param): # If there are two or more tabs open, we have to ensure us that it is # us who are in the active tab, and not the others if not self.gmwidg.isInFront(): return log.debug("Human.emit_action: self.name=%s, action=%s" % (self.name, action)) # If there are two human players, we have to ensure us that it was us # who did the action, and not the others if self.gamemodel.players[1-self.color].__type__ == LOCAL: if action == HURRY_ACTION: if self.gamemodel.boards[-1].color == self.color: return else: if self.gamemodel.boards[-1].color != self.color: return self.emit("offer", Offer(action, param=param))
def putMessage(self, message): 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 ss = "<b>DMPGirl:</b>" es = "<br>" answer = data[data.find(ss) + len(ss):data.find(es, data.find(ss))] self.emit("offer", Offer(CHAT_ACTION, answer))
def parseLine(self, engine, line): if line[0:1] == "#": # Debug line which we shall ignore as specified in CECPv2 specs return # 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]) return # 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) 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", extra={"task": 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" % movestr, extra={"task": self.defname}) print("undo", file=self.engine) return else: try: move = parseAny(self.board, movestr) except ParsingError as e: 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) return if validate(self.board, move): self.board = None self.returnQueue.put(move) return 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) return finally: log.debug("__parseLine(): releasing self.boardLock", extra={"task": 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("book off", file=self.engine) return 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())]) 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": # 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:]): return # 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() return # 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:]))) 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 not key 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.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("new", file=self.engine) # 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 emit_action(board, action, param, gmwidg): if gmwidg.isInFront(): gamemodel.curplayer.emit("offer", Offer(action, param=param))
def onOfferAdd(self, match): log.debug("OfferManager.onOfferAdd: match.string=%s" % match.string) tofrom, index, offertype, parameters = match.groups() index = int(index) if tofrom == "t": # ICGameModel keeps track of the offers we've sent ourselves, so we # don't need this return if offertype not in strToOfferType: log.warning("OfferManager.onOfferAdd: Declining unknown offer type: " + "offertype=%s parameters=%s index=%d" % (offertype, parameters, index)) self.connection.client.run_command("decline %d" % index) return offertype = strToOfferType[offertype] if offertype == TAKEBACK_OFFER: offer = Offer(offertype, param=int(parameters), index=index) else: offer = Offer(offertype, index=index) self.offers[offer.index] = offer if offer.type == MATCH_OFFER: is_adjourned = False if matchreUntimed.match(parameters) is not None: fname, frating, col, tname, trating, rated, type = \ matchreUntimed.match(parameters).groups() mins = 0 incr = 0 gametype = GAME_TYPES["untimed"] else: fname, frating, col, tname, trating, rated, gametype, mins, \ incr, wildtype, adjourned = matchre.match(parameters).groups() if (wildtype and "adjourned" in wildtype) or \ (adjourned and "adjourned" in adjourned): is_adjourned = True if wildtype and "wild" in wildtype: gametype = wildtype try: gametype = GAME_TYPES[gametype] except KeyError: log.warning("OfferManager.onOfferAdd: auto-declining " + "unknown offer type: '%s'\n" % gametype) self.decline(offer) del self.offers[offer.index] return player = self.connection.players.get(fname) rating = frating.strip() rating = int(rating) if rating.isdigit() else 0 if player.ratings[gametype.rating_type] != rating: player.ratings[gametype.rating_type] = rating player.emit("ratings_changed", gametype.rating_type, player) rated = rated != "unrated" challenge = FICSChallenge(index, player, int(mins), int(incr), rated, col, gametype, adjourned=is_adjourned) self.emit("onChallengeAdd", challenge) else: log.debug("OfferManager.onOfferAdd: emitting onOfferAdd: %s" % offer) self.emit("onOfferAdd", offer)
def __onPrivateMessage(self, cm, name, title, isadmin, text): if name == self.ichandle: self.emit("offer", Offer(CHAT_ACTION, param=text))
def sendMessage (self, text): self.emit("offer", Offer(CHAT_ACTION, param=text))
def onOfferDeclined(self, match): log.debug("OfferManager.onOfferDeclined: match.string=%s" % match.string) type = match.groups()[0] offer = Offer(strToOfferType[type]) self.emit("onOfferDeclined", offer)