def on_game_list(self, match): gameno, wrating, wname, brating, bname, private, shorttype, rated, min, \ inc, whour, wmin, wsec, bhour, bmin, bsec, wmat, bmat, color, movno = match.groups() try: gametype = GAME_TYPES_BY_SHORT_FICS_NAME[shorttype] except KeyError: return wplayer = self.connection.players.get(FICSPlayer(wname)) bplayer = self.connection.players.get(FICSPlayer(bname)) game = FICSGame(wplayer, bplayer, gameno=int(gameno), rated=(rated == "r"), private=(private == "p"), minutes=int(min), inc=int(inc), game_type=gametype) for player, rating in ((wplayer, wrating), (bplayer, brating)): if player.status != IC_STATUS_PLAYING: player.status = IC_STATUS_PLAYING if player.game != game: player.game = game rating = self.parseRating(rating) if gametype.rating_type in player.ratings and \ player.ratings[gametype.rating_type].elo != rating: player.ratings[gametype.rating_type].elo = rating self.games.append(self.connection.games.get(game, emit=False))
def on_game_remove(self, match): gameno, wname, bname, comment, result = match.groups() result, reason = parse_reason(reprResult.index(result), comment, wname=wname) wplayer = FICSPlayer(wname) try: wplayer = self.connection.players.get(wplayer, create=False) wplayer.restore_previous_status( ) # no status update will be sent by # FICS if the player doesn't become available, so we restore # previous status first (not necessarily true, but the best guess) except KeyError: pass bplayer = FICSPlayer(bname) try: bplayer = self.connection.players.get(bplayer, create=False) bplayer.restore_previous_status() except KeyError: pass game = FICSGame(wplayer, bplayer, gameno=int(gameno), result=result, reason=reason) if wplayer.game is not None: game.rated = wplayer.game.rated game = self.connection.games.get(game, emit=False) self.connection.games.game_ended(game) # Do this last to give anybody connected to the game's signals a chance # to disconnect from them first wplayer.game = None bplayer.game = None
def __onHistoryResponseYES(self, matchlist): #History for user: #Opponent Type ECO End Date #66: - 1735 B 0 GuestHKZX [ bu 3 0] B23 Res Sun Dec 6, 15:50 EST 2015 #67: - 1703 B 0 GuestQWML [ lu 1 0] B07 Fla Sun Dec 6, 15:53 EST 2015 history = [] for match in matchlist[1:]: #print(match.groups()) history_no = match.groups()[0] result = match.groups()[1] our_color = match.groups()[2] if result == "+": result = WHITEWON if our_color == "W" else BLACKWON elif result == "-": result = WHITEWON if our_color == "B" else BLACKWON else: result = DRAW opponent_name = match.groups()[3] game_type = match.groups()[4] minutes, gain = match.groups()[5:7] eco = match.groups()[7] reason = reasons_dict[match.groups()[8]] week, month, day, hour, minute, timezone, year = match.groups( )[9:16] gametime = datetime.datetime(int(year), months.index(month) + 1, int(day), int(hour), int(minute)) private = game_type[0] == "p" rated = game_type[2] == "r" gametype = GAME_TYPES_BY_SHORT_FICS_NAME[game_type[1]] our_color = our_color == "B" and BLACK or WHITE minutes = int(minutes) gain = int(gain) user = self.connection.players.get( FICSPlayer(self.connection.getUsername())) opponent = FICSPlayer(opponent_name, status=IC_STATUS_OFFLINE) opponent = self.connection.players.get(opponent) wplayer, bplayer = (user, opponent) if our_color == WHITE \ else (opponent, user) game = FICSHistoryGame(wplayer, bplayer, game_type=gametype, rated=rated, minutes=minutes, inc=gain, private=private, our_color=our_color, time=gametime, reason=reason, history_no=history_no, result=result) if game not in self.connection.games: game = self.connection.games.get(game, emit=False) self.emit("historyGameAdded", game) history.append(game) self.emit("onHistoryList", history)
def __onStoredResponseYES(self, matchlist): #Stored games for User: # C Opponent On Type Str M ECO Date # 1: W TheDane N [ br 2 12] 0-0 B2 ??? Sun Nov 23, 6:14 CST 1997 # 2: W PyChess Y [psu 2 12] 39-39 W3 C20 Sun Jan 11, 17:40 ??? 2009 # 3: B cjavad N [ wr 2 2] 31-31 W18 --- Wed Dec 23, 06:58 PST 2009 self.connection.stored_owner = matchlist[0].groups()[0] adjournments = [] for match in matchlist[2:]: our_color = match.groups()[0] opponent_name, opponent_online = match.groups()[1:3] game_type = match.groups()[3] minutes, gain = match.groups()[4:6] str_white, str_black = match.groups()[6:8] next_color = match.groups()[8] move_num = match.groups()[9] eco = match.groups()[10] week, month, day, hour, minute, timezone, year = match.groups( )[11:18] gametime = datetime.datetime(int(year), months.index(month) + 1, int(day), int(hour), int(minute)) private = game_type[0] == "p" rated = game_type[2] == "r" gametype = GAME_TYPES_BY_SHORT_FICS_NAME[game_type[1]] our_color = our_color == "B" and BLACK or WHITE minutes = int(minutes) gain = int(gain) length = (int(move_num) - 1) * 2 if next_color == "B": length += 1 user = self.connection.players.get( FICSPlayer(self.connection.stored_owner)) opponent = FICSPlayer(opponent_name, status=IC_STATUS_OFFLINE) opponent = self.connection.players.get(opponent) wplayer, bplayer = (user, opponent) if our_color == WHITE \ else (opponent, user) game = FICSAdjournedGame(wplayer, bplayer, game_type=gametype, rated=rated, our_color=our_color, length=length, time=gametime, minutes=minutes, inc=gain, private=private) if game.opponent.adjournment != True: game.opponent.adjournment = True if game not in self.connection.games: game = self.connection.games.get(game, emit=False) self.emit("adjournedGameAdded", game) adjournments.append(game) self.emit("onAdjournmentsList", adjournments)
def onPlayGameCreated(self, matchlist): log.debug( "'%s' '%s' '%s'" % (matchlist[0].string, matchlist[1].string, matchlist[-1].string), extra={"task": (self.connection.username, "BM.onPlayGameCreated")}) wname, wrating, bname, brating, rated, match_type, minutes, inc = matchlist[ 0].groups() item = 2 if self.connection.USCN else 1 gameno, wname, bname, rated, match_type = matchlist[item].groups() gameno = int(gameno) wrating = self.parseRating(wrating) brating = self.parseRating(brating) rated = rated == "rated" game_type = GAME_TYPES[match_type] wplayer = self.connection.players.get(FICSPlayer(wname)) bplayer = self.connection.players.get(FICSPlayer(bname)) for player, rating in ((wplayer, wrating), (bplayer, brating)): if game_type.rating_type in player.ratings and \ player.ratings[game_type.rating_type].elo != rating: player.ratings[game_type.rating_type].elo = rating style12 = matchlist[-1].groups()[0] castleSigns = self.generateCastleSigns(style12, game_type) self.castleSigns[gameno] = castleSigns gameno, relation, curcol, ply, wname, bname, wms, bms, gain, lastmove, fen = \ self.parseStyle12(style12, castleSigns) game = FICSGame(wplayer, bplayer, gameno=gameno, rated=rated, game_type=game_type, minutes=int(minutes), inc=int(inc), board=FICSBoard(wms, bms, fen=fen)) game = self.connection.games.get(game) for player in (wplayer, bplayer): if player.status != IC_STATUS_PLAYING: player.status = IC_STATUS_PLAYING if player.game != game: player.game = game player.keep_after_logout = True self.theGameImPlaying = game self.gamemodelStartedEvents[gameno] = threading.Event() self.connection.client.run_command("follow") self.emit("playGameCreated", game)
def __onJournalResponseYES(self, matchlist): # Journal for User: # White Rating Black Rating Type ECO End Result # %01: tentacle 2291 larsa 2050 [ lr 1 2] D35 Rep 1/2-1/2 # %02: larsa 2045 tentacle 2296 [ lr 1 2] A46 Res 0-1 journal = [] self.connection.journal_owner = matchlist[0].groups()[0] for match in matchlist[2:]: # print(match.groups()) journal_no = match.groups()[0] result = match.groups()[10] result = reprResult.index(result) white = match.groups()[1] wrating = match.groups()[2] black = match.groups()[3] brating = match.groups()[4] game_type = match.groups()[5] minutes, gain = match.groups()[6:8] reason = reasons_dict[match.groups()[9]] private = game_type[0] == "p" rated = game_type[2] == "r" gametype = GAME_TYPES_BY_SHORT_FICS_NAME[game_type[1]] minutes = int(minutes) gain = int(gain) wplayer = self.connection.players.get( FICSPlayer(white, status=IC_STATUS_OFFLINE)) bplayer = self.connection.players.get( FICSPlayer(black, status=IC_STATUS_OFFLINE)) game = FICSJournalGame(wplayer, bplayer, game_type=gametype, rated=rated, minutes=minutes, inc=gain, private=private, wrating=wrating, brating=brating, reason=reason, journal_no=journal_no, result=result) if game not in self.connection.games: game = self.connection.games.get(game, emit=False) self.emit("journalGameAdded", game) journal.append(game) self.emit("onJournalList", journal)
def on_icc_my_game_result(self, data): log.debug("DG_MY_GAME_RESULT %s" % data) # gamenumber become-examined game_result_code score_string2 description-string ECO # 1242 1 Res 1-0 {Black resigns} {D89} parts = data.split(" ", 4) gameno, ex, result_code, result, rest = parts gameno = int(gameno) comment, rest = rest[2:].split("}", 1) try: game = self.connection.games.get_game_by_gameno(gameno) except KeyError: return wname = game.wplayer.name bname = game.bplayer.name result, reason = parse_reason(reprResult.index(result), comment, wname=wname) try: wplayer = self.connection.players.get(wname) wplayer.restore_previous_status() # no status update will be sent by # FICS if the player doesn't become available, so we restore # previous status first (not necessarily true, but the best guess) except KeyError: log.debug("%s not in self.connections.players - creating" % wname) wplayer = FICSPlayer(wname) try: bplayer = self.connection.players.get(bname) bplayer.restore_previous_status() except KeyError: log.debug("%s not in self.connections.players - creating" % bname) bplayer = FICSPlayer(bname) game = FICSGame(wplayer, bplayer, gameno=int(gameno), result=result, reason=reason) if wplayer.game is not None: game.rated = wplayer.game.rated game = self.connection.games.get(game, emit=False) self.connection.games.game_ended(game) # Do this last to give anybody connected to the game's signals a chance # to disconnect from them first wplayer.game = None bplayer.game = None
def onDepartedNotification (self, match): name = match.groups()[0] try: player = self.connection.players.get(FICSPlayer(name)) except KeyError: return GLib.idle_add(self.emit, "departedNotification", player)
def on_player_connectI(self, match, set_online=True): # bslwBzSLx # gbtami 001411E1663P1483P1720P0P1646P0P0P1679P name, status, titlehex, blitz, blitzdev, std, stddev, light, lightdev, \ wild, wilddev, bughouse, bughousedev, crazyhouse, crazyhousedev, \ suicide, suicidedev, losers, losersdev, atomic, atomicdev = match.groups() player = self.connection.players.get(FICSPlayer(name)) self.players.append(player) titles = parse_title_hex(titlehex) if not player.titles >= titles: player.titles |= titles for rtype, elo, dev in \ ((TYPE_BLITZ, blitz, blitzdev), (TYPE_STANDARD, std, stddev), (TYPE_LIGHTNING, light, lightdev), (TYPE_ATOMIC, atomic, atomicdev), (TYPE_WILD, wild, wilddev), (TYPE_CRAZYHOUSE, crazyhouse, crazyhousedev), (TYPE_BUGHOUSE, bughouse, bughousedev), (TYPE_LOSERS, losers, losersdev), (TYPE_SUICIDE, suicide, suicidedev)): parse_rating = self.parseRating(elo) if player.ratings[rtype].elo != parse_rating: player.ratings[rtype].elo = parse_rating player.ratings[rtype].deviation = DEVIATION[dev] # do last so rating info is there when notifications are generated status = STATUS[status] if player.status != status: player.status = status if set_online and not player.online: player.online = True
def get_allob_List(self,match): """ Description: Processes the returning pattern matched of the FICS allob command extracts out the gameno and a list of observers before emmiting them for collection by the observers view match: (re.reg-ex) is the complied matching pattern for processing """ obs_dic = {} gameno = match.group(1) observers = match.group(2) oblist = observers.split() for player in oblist: if '(U)' not in player : try: ficsplayer = self.connection.players[FICSPlayer(player)] obs_dic[player] = ficsplayer.getRatingByGameType(GAME_TYPES['standard']) except KeyError: obs_dic[player] = 0 print("player %s is not in self.connection.players" % player) else : obs_dic[player] = 0 obs_sorted = sorted(obs_dic.items(), key=operator.itemgetter(1),reverse=True) obs_str = "\n" for toople in obs_sorted : player,rating = toople if rating == 0 : obs_str += player + " " # Don't print ratings for guest accounts else: obs_str += player + "[" + str(rating) + "] " self.emit('observers_received',gameno,obs_str)
def onArrivalNotification (self, match): name = match.groups()[0] try: player = self.connection.players.get(FICSPlayer(name)) except KeyError: return if player.name not in self.connection.notify_users: self.connection.notify_users.append(player.name) GLib.idle_add(self.emit, "arrivalNotification", player)
def on_game_remove(self, match): gameno, wname, bname, comment, result = match.groups() result, reason = parse_reason( reprResult.index(result), comment, wname=wname) try: wplayer = self.connection.players.get(wname) wplayer.restore_previous_status() # no status update will be sent by # FICS if the player doesn't become available, so we restore # previous status first (not necessarily true, but the best guess) except KeyError: print("%s not in self.connections.players - creating" % wname) wplayer = FICSPlayer(wname) try: bplayer = self.connection.players.get(bname) bplayer.restore_previous_status() except KeyError: print("%s not in self.connections.players - creating" % bname) bplayer = FICSPlayer(bname) game = FICSGame(wplayer, bplayer, gameno=int(gameno), result=result, reason=reason) if wplayer.game is not None: game.rated = wplayer.game.rated game = self.connection.games.get(game, emit=False) # Our played/observed game ends are handled in main connection to prevent # removing them by helper connection before latest move(style12) comes from server if game == self.connection.bm.theGameImPlaying or \ game in self.connection.bm.gamesImObserving: return self.connection.games.game_ended(game) # Do this last to give anybody connected to the game's signals a chance # to disconnect from them first wplayer.game = None bplayer.game = None
def on_game_add(self, match): gameno, wname, bname, rated, game_type = match.groups() if game_type not in GAME_TYPES: return wplayer = self.connection.players.get(FICSPlayer(wname)) bplayer = self.connection.players.get(FICSPlayer(bname)) game = FICSGame(wplayer, bplayer, gameno=int(gameno), rated=(rated == "rated"), game_type=GAME_TYPES[game_type]) for player in (wplayer, bplayer): if player.status != IC_STATUS_PLAYING: player.status = IC_STATUS_PLAYING if player.game != game: player.game = game self.connection.games.get(game)
def on_player_unavailable(self, match): name, titles = match.groups() player = self.connection.players.get(FICSPlayer(name)) titles = self.parseTitles(titles) if not player.titles >= titles: player.titles |= titles # we get here after players start a game, so we make sure that we don't # overwrite IC_STATUS_PLAYING if player.game is None and \ player.status not in (IC_STATUS_PLAYING, IC_STATUS_NOT_AVAILABLE): player.status = IC_STATUS_NOT_AVAILABLE
def on_player_who(self, match): for blitz, status, name, titles in whomatch_re.findall(match.string): player = self.connection.players.get(FICSPlayer(name)) if not player.online: player.online = True status = STATUS[status] if player.status != status: player.status = status titles = self.parseTitles(titles) if not player.titles >= titles: player.titles |= titles blitz = self.parseRating(blitz) if player.ratings[TYPE_BLITZ].elo != blitz: player.ratings[TYPE_BLITZ].elo = blitz
def on_player_available(self, matches): name, titles, blitz, std, wild, light, bughouse = matches[0].groups() player = self.connection.players.get(FICSPlayer(name)) if not player.online: player.online = True if player.status != IC_STATUS_AVAILABLE: player.status = IC_STATUS_AVAILABLE titles = self.parseTitles(titles) if not player.titles >= titles: player.titles |= titles for rtype, rating in ((TYPE_BLITZ, blitz), (TYPE_STANDARD, std), (TYPE_LIGHTNING, light), (TYPE_WILD, wild), (TYPE_BUGHOUSE, bughouse)): rating = self.parseRating(rating) if player.ratings[rtype].elo != rating: player.ratings[rtype].elo = rating
def parseGame(self, matchlist, gameclass, in_progress=False, gameno=None): """ Parses the header and movelist for an observed or stored game from its matchlist (an re.match object) into a gameclass (FICSGame or subclass of) object. in_progress - should be True for an observed game matchlist, and False for stored/adjourned games """ # ################ observed game movelist example: # Movelist for game 64: # # Ajido (2281) vs. IMgooeyjim (2068) --- Thu Oct 14, 20:36 PDT 2010 # Rated standard match, initial time: 15 minutes, increment: 3 seconds. # # Move Ajido IMgooeyjim # ---- --------------------- --------------------- # 1. d4 (0:00.000) Nf6 (0:00.000) # 2. c4 (0:04.061) g6 (0:00.969) # 3. Nc3 (0:13.280) Bg7 (0:06.422) # {Still in progress} * # # ################# stored game example: # BwanaSlei (1137) vs. mgatto (1336) --- Wed Nov 5, 20:56 PST 2008 # Rated blitz match, initial time: 5 minutes, increment: 0 seconds. # # Move BwanaSlei mgatto # ---- --------------------- --------------------- # 1. e4 (0:00.000) c5 (0:00.000) # 2. d4 (0:05.750) cxd4 (0:03.020) # ... # 23. Qxf3 (1:05.500) # {White lost connection; game adjourned} * # # ################# stored wild/3 game with style12: # kurushi (1626) vs. mgatto (1627) --- Thu Nov 4, 10:33 PDT 2010 # Rated wild/3 match, initial time: 3 minutes, increment: 0 seconds. # # <12> nqbrknrn pppppppp -------- -------- -------- -------- PPPPPPPP NQBRKNRN W -1 0 0 0 0 0 17 kurushi mgatto -4 3 0 39 39 169403 45227 1 none (0:00.000) none 0 1 0 # # Move kurushi mgatto # ---- --------------------- --------------------- # 1. Nb3 (0:00.000) d5 (0:00.000) # 2. Nhg3 (0:00.386) e5 (0:03.672) # ... # 28. Rxd5 (0:00.412) # {Black lost connection; game adjourned} * # # ################# stored game movelist following stored game(s): # Stored games for mgatto: # C Opponent On Type Str M ECO Date # 1: W BabyLurking Y [ br 5 0] 29-13 W27 D37 Fri Nov 5, 04:41 PDT 2010 # 2: W gbtami N [ wr 5 0] 32-34 W14 --- Thu Oct 21, 00:14 PDT 2010 # # mgatto (1233) vs. BabyLurking (1455) --- Fri Nov 5, 04:33 PDT 2010 # Rated blitz match, initial time: 5 minutes, increment: 0 seconds. # # Move mgatto BabyLurking # ---- ---------------- ---------------- # 1. Nf3 (0:00) d5 (0:00) # 2. d4 (0:03) Nf6 (0:00) # 3. c4 (0:03) e6 (0:00) # {White lost connection; game adjourned} * # # ################## stored game movelist following stored game(s): # ## Note: A wild stored game in this format won't be parseable into a board because # ## it doesn't come with a style12 that has the start position, so we warn and return # ################## # Stored games for mgatto: # C Opponent On Type Str M ECO Date # 1: W gbtami N [ wr 5 0] 32-34 W14 --- Thu Oct 21, 00:14 PDT 2010 # # mgatto (1627) vs. gbtami (1881) --- Thu Oct 21, 00:10 PDT 2010 # Rated wild/fr match, initial time: 5 minutes, increment: 0 seconds. # # Move mgatto gbtami # ---- ---------------- ---------------- # 1. d4 (0:00) b6 (0:00) # 2. b3 (0:06) d5 (0:03) # 3. c4 (0:08) e6 (0:03) # 4. e3 (0:04) dxc4 (0:02) # 5. bxc4 (0:02) g6 (0:09) # 6. Nd3 (0:12) Bg7 (0:02) # 7. Nc3 (0:10) Ne7 (0:03) # 8. Be2 (0:08) c5 (0:05) # 9. a4 (0:07) cxd4 (0:38) # 10. exd4 (0:06) Bxd4 (0:03) # 11. O-O (0:10) Qc6 (0:06) # 12. Bf3 (0:16) Qxc4 (0:04) # 13. Bxa8 (0:03) Rxa8 (0:14) # {White lost connection; game adjourned} * # # ################# other reasons the game could be stored/adjourned: # Game courtesyadjourned by (Black|White) # Still in progress # This one must be a FICS bug # Game adjourned by mutual agreement # (White|Black) lost connection; game adjourned # Game adjourned by ((server shutdown)|(adjudication)|(simul holder)) index = 0 if in_progress: gameno = int(matchlist[index].groups()[0]) index += 2 header1 = matchlist[index] if isinstance(matchlist[index], str) \ else matchlist[index].group() matches = moveListHeader1.match(header1).groups() wname, wrating, bname, brating = matches[:4] if self.connection.FatICS: year, month, day, hour, minute, timezone = matches[11:] else: weekday, month, day, hour, minute, timezone, year = matches[4:11] month = months.index(month) + 1 wrating = self.parseRating(wrating) brating = self.parseRating(brating) rated, game_type, minutes, increment = \ moveListHeader2.match(matchlist[index + 1]).groups() minutes = int(minutes) increment = int(increment) game_type = GAME_TYPES[game_type] reason = matchlist[-1].group().lower() if in_progress: result = None result_str = "*" elif "1-0" in reason: result = WHITEWON result_str = "1-0" elif "0-1" in reason: result = BLACKWON result_str = "0-1" elif "1/2-1/2" in reason: result = DRAW result_str = "1/2-1/2" else: result = ADJOURNED result_str = "*" result, reason = parse_reason(result, reason, wname=wname) index += 3 if matchlist[index].startswith("<12>"): style12 = matchlist[index][5:] castleSigns = self.generateCastleSigns(style12, game_type) gameno, relation, curcol, ply, wname, bname, wms, bms, gain, lastmove, \ fen = self.parseStyle12(style12, castleSigns) initialfen = fen movesstart = index + 4 else: if game_type.rating_type == TYPE_WILD: # we need a style12 start position to correctly parse a wild/* board log.error("BoardManager.parseGame: no style12 for %s board." % game_type.fics_name) return None castleSigns = ("k", "q") initialfen = None movesstart = index + 2 if in_progress: self.castleSigns[gameno] = castleSigns moves = {} times = {} wms = bms = minutes * 60 * 1000 for line in matchlist[movesstart:-1]: if not moveListMoves.match(line): log.error("BoardManager.parseGame: unmatched line: \"%s\"" % repr(line)) raise Exception( "BoardManager.parseGame: unmatched line: \"%s\"" % repr(line)) moveno, wmove, whour, wmin, wsec, wmsec, bmove, bhour, bmin, bsec, bmsec = \ moveListMoves.match(line).groups() whour = 0 if whour is None else int(whour[0]) bhour = 0 if bhour is None else int(bhour[0]) ply = int(moveno) * 2 - 2 if wmove: moves[ply] = wmove wms -= (int(whour) * 60 * 60 * 1000) + (int(wmin) * 60 * 1000) + (int(wsec) * 1000) if wmsec is not None: wms -= int(wmsec) else: wmsec = 0 if increment > 0: wms += (increment * 1000) times[ply] = "%01d:%02d:%02d.%03d" % (int(whour), int(wmin), int(wsec), int(wmsec)) if bmove: moves[ply + 1] = bmove bms -= (int(bhour) * 60 * 60 * 1000) + (int(bmin) * 60 * 1000) + (int(bsec) * 1000) if bmsec is not None: bms -= int(bmsec) else: bmsec = 0 if increment > 0: bms += (increment * 1000) times[ply + 1] = "%01d:%02d:%02d.%03d" % (int(bhour), int(bmin), int(bsec), int(bmsec)) if in_progress and gameno in self.queuedStyle12s: # Apply queued board updates for style12 in self.queuedStyle12s[gameno]: gameno, relation, curcol, ply, wname, bname, wms, bms, gain, lastmove, fen = \ self.parseStyle12(style12, castleSigns) if lastmove is None: continue moves[ply - 1] = lastmove # Updated the queuedMoves in case there has been a takeback for moveply in list(moves.keys()): if moveply > ply - 1: del moves[moveply] del self.queuedStyle12s[gameno] pgnHead = [ ("Event", "FICS %s %s game" % (rated.lower(), game_type.fics_name)), ("Site", "freechess.org"), ("White", wname), ("Black", bname), ("TimeControl", "%d+%d" % (minutes * 60, increment)), ("Result", result_str), ("WhiteClock", msToClockTimeTag(wms)), ("BlackClock", msToClockTimeTag(bms)), ] if wrating != 0: pgnHead += [("WhiteElo", wrating)] if brating != 0: pgnHead += [("BlackElo", brating)] if year and month and day and hour and minute: pgnHead += [ ("Date", "%04d.%02d.%02d" % (int(year), int(month), int(day))), ("Time", "%02d:%02d:00" % (int(hour), int(minute))), ] if initialfen: pgnHead += [("SetUp", "1"), ("FEN", initialfen)] if game_type.variant_type == FISCHERRANDOMCHESS: pgnHead += [("Variant", "Fischerandom")] # FR is the only variant used in this tag by the PGN generator @ # ficsgames.org. They put all the other wild/* stuff only in the # "Event" header. elif game_type.variant_type == CRAZYHOUSECHESS: pgnHead += [("Variant", "Crazyhouse")] elif game_type.variant_type in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): pgnHead += [("Variant", "Wildcastle")] elif game_type.variant_type == ATOMICCHESS: pgnHead += [("Variant", "Atomic")] elif game_type.variant_type == LOSERSCHESS: pgnHead += [("Variant", "Losers")] elif game_type.variant_type == SUICIDECHESS: pgnHead += [("Variant", "Suicide")] pgn = "\n".join(['[%s "%s"]' % line for line in pgnHead]) + "\n" moves = sorted(moves.items()) for ply, move in moves: if ply % 2 == 0: pgn += "%d. " % (ply // 2 + 1) time = times[ply] pgn += "%s {[%%emt %s]} " % (move, time) pgn += "*\n" wplayer = self.connection.players.get(FICSPlayer(wname)) bplayer = self.connection.players.get(FICSPlayer(bname)) for player, rating in ((wplayer, wrating), (bplayer, brating)): if game_type.rating_type in player.ratings and \ player.ratings[game_type.rating_type].elo != rating: player.ratings[game_type.rating_type].elo = rating player.keep_after_logout = True game = gameclass(wplayer, bplayer, game_type=game_type, result=result, rated=(rated.lower() == "rated"), minutes=minutes, inc=increment, board=FICSBoard(wms, bms, pgn=pgn)) if in_progress: game.gameno = gameno else: if gameno is not None: game.gameno = gameno game.reason = reason game = self.connection.games.get(game, emit=False) return game
def test1(self): """ Test following lecturebot starting new lecture after finishing previous one """ lines = [ 'LectureBot(TD)(----)[1] kibitzes: That concludes this lecture. If this lecture inspired you to write your own lecture, "finger LectureBot" and read note 8. toddmf would really like to hear from you about LectureBot. Please send him comments! Hope to see you all again soon!', 'fics% ', 'LectureBot stopped examining game 1.', '', 'Game 1 (which you were observing) has no examiners.', 'Removing game 1 from observation list.', 'fics% ', 'LectureBot, whom you are following, has started examining a game.', 'You are now observing game 1.', 'Game 1: LectureBot (0) LectureBot (0) unrated untimed 0 0', '', '<12> rnbqkbnr pppppppp -------- -------- -------- -------- PPPPPPPP RNBQKBNR W -1 1 1 1 1 0 1 LectureBot LectureBot -2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', ] signal = 'obsGameCreated' game = FICSGame( FICSPlayer("LectureBot"), FICSPlayer("LectureBot"), gameno=1) game = self.connection.games.get(game) expectedResults = (game, ) self.runAndAssertEquals(signal, lines, expectedResults) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ '<12> rnbqkbnr pppppppp -------- -------- -------- -------- PPPPPPPP RNBQKBNR W -1 1 1 1 1 0 1 Henley LectureBot -2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', '<12> rnbqkbnr pppppppp -------- -------- -------- -------- PPPPPPPP RNBQKBNR W -1 1 1 1 1 0 1 Henley Browne -2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', '<12> rnbqkbnr pppppppp -------- -------- -------- -----N-- PPPPPPPP RNBQKB-R B -1 1 1 1 1 1 1 Henley Browne -2 0 0 39 39 0 0 1 N/g1-f3 (0:00.000) Nf3 0 0 0', 'fics% ', 'Game 1: LectureBot moves: Nf3', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 1) lines = [ '<12> rnbqkbnr pp-ppppp -------- --p----- -------- -----N-- PPPPPPPP RNBQKB-R W 2 1 1 1 1 0 1 Henley Browne -2 0 0 39 39 0 0 2 P/c7-c5 (0:00.000) c5 0 0 0', 'fics% ', 'Game 1: LectureBot moves: c5', '<12> rnbqkbnr pp-ppppp -------- --p----- -------- -----NP- PPPPPP-P RNBQKB-R B -1 1 1 1 1 0 1 Henley Browne -2 0 0 39 39 0 0 2 P/g2-g3 (0:00.000) g3 0 0 0', 'fics% ' 'Game 1: LectureBot moves: g3', '<12> rnbqkbnr p--ppppp -p------ --p----- -------- -----NP- PPPPPP-P RNBQKB-R W -1 1 1 1 1 0 1 Henley Browne -2 0 0 39 39 0 0 3 P/b7-b6 (0:00.000) b6 0 0 0', 'fics% ' 'Game 1: LectureBot moves: b6', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 4)
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(FICSPlayer(fname)) rating = frating.strip() rating = int(rating) if rating.isdigit() else 0 if gametype.rating_type in player.ratings and \ player.ratings[gametype.rating_type].elo != rating: player.ratings[gametype.rating_type].elo = rating 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 onObserveGameCreated(self, matchlist): log.debug("'%s'" % (matchlist[1].string), extra={ "task": (self.connection.username, "BM.onObserveGameCreated") }) if self.connection.USCN: # TODO? is this ok? game_type = GAME_TYPES["blitz"] castleSigns = ("k", "q") else: gameno, wname, wrating, bname, brating, rated, gametype, minutes, inc = matchlist[ 1].groups() wrating = self.parseRating(wrating) brating = self.parseRating(brating) game_type = GAME_TYPES[gametype] style12 = matchlist[-1].groups()[0] castleSigns = self.generateCastleSigns(style12, game_type) gameno, relation, curcol, ply, wname, bname, wms, bms, gain, lastmove, fen = \ self.parseStyle12(style12, castleSigns) gameno = int(gameno) self.castleSigns[gameno] = castleSigns wplayer = self.connection.players.get(FICSPlayer(wname)) bplayer = self.connection.players.get(FICSPlayer(bname)) if relation == IC_POS_OBSERVING_EXAMINATION: pgnHead = [("Event", "FICS %s %s game" % (rated, game_type.fics_name)), ("Site", "freechess.org"), ("White", wname), ("Black", bname), ("Result", "*"), ("SetUp", "1"), ("FEN", fen)] pgn = "\n".join(['[%s "%s"]' % line for line in pgnHead]) + "\n*\n" game = FICSGame(wplayer, bplayer, gameno=gameno, rated=rated == "rated", game_type=game_type, minutes=int(minutes), inc=int(inc), board=FICSBoard(wms, bms, pgn=pgn), relation=relation) game = self.connection.games.get(game) self.gamesImObserving[game] = None self.gamemodelStartedEvents[game.gameno] = threading.Event() self.emit("obsGameCreated", game) self.gamemodelStartedEvents[game.gameno].wait() else: game = FICSGame(wplayer, bplayer, gameno=gameno, rated=rated == "rated", game_type=game_type, minutes=int(minutes), inc=int(inc), relation=relation) game = self.connection.games.get(game, emit=False) if not game.supported: log.warning("Trying to follow an unsupported type game %s" % game.game_type) return if game.gameno in self.gamemodelStartedEvents: log.warning("%s already in gamemodelstartedevents" % game.gameno) return self.gamesImObserving[game] = None self.queuedStyle12s[game.gameno] = [] self.queuedEmits[game.gameno] = [] self.gamemodelStartedEvents[game.gameno] = threading.Event() # FICS doesn't send the move list after 'observe' and 'follow' commands self.connection.client.run_command("moves %d" % game.gameno)
def test2(self): """ Test manual examine lecturebot lec2 """ lines = [ # examine BLOCK_START + '62' + BLOCK_SEPARATOR + '36' + BLOCK_SEPARATOR, 'Starting a game in examine (scratch) mode.', '', '<12> rnbqkbnr pppppppp -------- -------- -------- -------- PPPPPPPP RNBQKBNR W -1 1 1 1 1 0 77 gbtami gbtami 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, ] signal = 'exGameCreated' game = FICSGame( FICSPlayer("gbtami"), FICSPlayer("gbtami"), gameno=77) game = self.connection.games.get(game) expectedResults = (game, ) self.runAndAssertEquals(signal, lines, expectedResults) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ # bsetup BLOCK_START + '63' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, "Entering setup mode.", 'fics% ', BLOCK_END, # bsetup fen r1bq2k1/2p2p1p/p1pp2pB/2n4r/8/2N2Q2/PPP2PPP/4RRK1 BLOCK_START + '64' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, '<12> r-bq--k- --p--p-p p-pp--pB --n----r -------- --N--Q-- PPP--PPP ----RRK- W -1 1 1 1 1 0 77 gbtami gbtami 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bsetup wcastle none BLOCK_START + '65' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, '<12> r-bq--k- --p--p-p p-pp--pB --n----r -------- --N--Q-- PPP--PPP ----RRK- W -1 0 0 1 1 0 77 gbtami gbtami 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bsetup bcastle none BLOCK_START + '66' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, '<12> r-bq--k- --p--p-p p-pp--pB --n----r -------- --N--Q-- PPP--PPP ----RRK- W -1 0 0 0 0 0 77 gbtami gbtami 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # tomove white BLOCK_START + '67' + BLOCK_SEPARATOR + '134' + BLOCK_SEPARATOR, '<12> r-bq--k- --p--p-p p-pp--pB --n----r -------- --N--Q-- PPP--PPP ----RRK- W -1 0 0 0 0 0 77 gbtami gbtami 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bsetup done BLOCK_START + '68' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, 'Game is validated - entering examine mode.', '<12> r-bq--k- --p--p-p p-pp--pB --n----r -------- --N--Q-- PPP--PPP ----RRK- W -1 0 0 0 0 0 77 gbtami gbtami 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # wname Honfi BLOCK_START + '69' + BLOCK_SEPARATOR + '148' + BLOCK_SEPARATOR, '<12> r-bq--k- --p--p-p p-pp--pB --n----r -------- --N--Q-- PPP--PPP ----RRK- W -1 0 0 0 0 0 77 Honfi gbtami 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bname Sebestyen BLOCK_START + '70' + BLOCK_SEPARATOR + '19' + BLOCK_SEPARATOR, '<12> r-bq--k- --p--p-p p-pp--pB --n----r -------- --N--Q-- PPP--PPP ----RRK- W -1 0 0 0 0 0 77 Honfi Sebestyen 2 0 0 39 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # f3f6 BLOCK_START + '71' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> r-bq--k- --p--p-p p-pp-QpB --n----r -------- --N----- PPP--PPP ----RRK- B -1 0 0 0 0 1 77 Honfi Sebestyen 2 0 0 39 39 0 0 1 Q/f3-f6 (0:00.000) Qf6 0 0 0', 'Game 77: gbtami moves: Qf6', 'fics% ', BLOCK_END, # d8f6 BLOCK_START + '72' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> r-b---k- --p--p-p p-pp-qpB --n----r -------- --N----- PPP--PPP ----RRK- W -1 0 0 0 0 0 77 Honfi Sebestyen 2 0 0 30 39 0 0 2 Q/d8-f6 (0:00.000) Qxf6 0 0 0', 'Game 119: gbtami moves: Qxf6', 'fics% ', BLOCK_END, # e1e8 BLOCK_START + '73' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> r-b-R-k- --p--p-p p-pp-qpB --n----r -------- --N----- PPP--PPP -----RK- B -1 0 0 0 0 1 77 Honfi Sebestyen 2 0 0 30 39 0 0 2 R/e1-e8 (0:00.000) Re8# 0 0 0', 'Game 119: gbtami moves: Re8#', 'Game 119: Black checkmated 1-0', 'fics% ', BLOCK_END, ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 3) lines = [ # kibitz Example 2:... BLOCK_START + '74' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, 'gbtami(1719)[77] kibitzes: Example 2: Dementyev vs Karpov, Riga 1971', BLOCK_END, # bsetup BLOCK_START + '75' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, "Entering setup mode.", 'fics% ', BLOCK_END, # bsetup fen q3r1k1/4Rp1p/6p1/2Q1B3/P2P4/1r1b3P/5PP1/4R1K1 BLOCK_START + '76' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, '<12> q---r-k- ----Rp-p ------p- --Q-B--- P--P---- -r-b---P -----PP- ----R-K- B -1 0 0 0 0 0 77 Honfi Sebestyen 2 0 0 30 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bsetup wcastle none BLOCK_START + '77' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, '<12> q---r-k- ----Rp-p ------p- --Q-B--- P--P---- -r-b---P -----PP- ----R-K- B -1 0 0 0 0 0 77 Honfi Sebestyen 2 0 0 30 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bsetup bcastle none BLOCK_START + '78' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, '<12> q---r-k- ----Rp-p ------p- --Q-B--- P--P---- -r-b---P -----PP- ----R-K- B -1 0 0 0 0 0 77 Honfi Sebestyen 2 0 0 30 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # tomove white BLOCK_START + '79' + BLOCK_SEPARATOR + '134' + BLOCK_SEPARATOR, '<12> q---r-k- ----Rp-p ------p- --Q-B--- P--P---- -r-b---P -----PP- ----R-K- W -1 0 0 0 0 0 77 Honfi Sebestyen 2 0 0 30 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bsetup done BLOCK_START + '80' + BLOCK_SEPARATOR + '21' + BLOCK_SEPARATOR, 'Game is validated - entering examine mode.', '<12> q---r-k- ----Rp-p ------p- --Q-B--- P--P---- -r-b---P -----PP- ----R-K- W -1 0 0 0 0 0 77 Honfi Sebestyen 2 0 0 30 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # wname Dementyev BLOCK_START + '81' + BLOCK_SEPARATOR + '148' + BLOCK_SEPARATOR, '<12> q---r-k- ----Rp-p ------p- --Q-B--- P--P---- -r-b---P -----PP- ----R-K- W -1 0 0 0 0 0 77 Dementyev Sebestyen 2 0 0 30 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # bname Karpov BLOCK_START + '82' + BLOCK_SEPARATOR + '19' + BLOCK_SEPARATOR, '<12> q---r-k- ----Rp-p ------p- --Q-B--- P--P---- -r-b---P -----PP- ----R-K- W -1 0 0 0 0 0 77 Dementyev Karpov 2 0 0 30 39 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', BLOCK_END, # c5d5 BLOCK_START + '83' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> q---r-k- ----Rp-p ------p- ---QB--- P--P---- -r-b---P -----PP- ----R-K- B -1 0 0 0 0 1 77 Dementyev Karpov 2 0 0 30 39 0 0 1 Q/c5-d5 (0:00.000) Qd5 0 0 0', 'Game 119: gbtami moves: Qd5', 'fics% ', BLOCK_END, ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 1)
def __onHistoryResponseYES(self, matchlist): #History for User: #Opponent Type ECO End Date #66: - 1735 B 0 GuestHKZX [ bu 3 0] B23 Res Sun Dec 6, 15:50 EST 2015 #67: - 1703 B 0 GuestQWML [ lu 1 0] B07 Fla Sun Dec 6, 15:53 EST 2015 history = [] self.connection.history_owner = matchlist[0].groups()[0] for match in matchlist[2:]: #print(match.groups()) history_no = match.groups()[0] result = match.groups()[1] owner_rating = match.groups()[2] owner_color = match.groups()[3] opp_rating = match.groups()[4] if result == "+": result = WHITEWON if owner_color == "W" else BLACKWON elif result == "-": result = WHITEWON if owner_color == "B" else BLACKWON else: result = DRAW opponent_name = match.groups()[5] if owner_color == "W": white = self.connection.history_owner black = opponent_name wrating = owner_rating brating = opp_rating else: white = opponent_name black = self.connection.history_owner brating = owner_rating wrating = opp_rating game_type = match.groups()[6] minutes, gain = match.groups()[7:9] eco = match.groups()[9] reason = reasons_dict[match.groups()[10]] week, month, day, hour, minute, timezone, year = match.groups( )[11:18] gametime = datetime.datetime(int(year), months.index(month) + 1, int(day), int(hour), int(minute)) private = game_type[0] == "p" rated = game_type[2] == "r" gametype = GAME_TYPES_BY_SHORT_FICS_NAME[game_type[1]] owner_color = owner_color == "B" and BLACK or WHITE minutes = int(minutes) gain = int(gain) wplayer = self.connection.players.get( FICSPlayer(white, status=IC_STATUS_OFFLINE)) bplayer = self.connection.players.get( FICSPlayer(black, status=IC_STATUS_OFFLINE)) game = FICSHistoryGame(wplayer, bplayer, game_type=gametype, rated=rated, minutes=minutes, inc=gain, private=private, wrating=wrating, brating=brating, time=gametime, reason=reason, history_no=history_no, result=result) if game not in self.connection.games: game = self.connection.games.get(game, emit=False) self.emit("historyGameAdded", game) history.append(game) self.emit("onHistoryList", history)
def on_player_disconnect(self, match): name = match.groups()[0] self.connection.players.player_disconnected(FICSPlayer(name))
def test1(self): """ Test puzzlebot starting a new mate in 2 puzzle """ # tell puzzlebot gm2 lines = [ BLOCK_START + '92' + BLOCK_SEPARATOR + '132' + BLOCK_SEPARATOR, '(told puzzlebot, who is examining a game)', BLOCK_END, 'fics% ', BLOCK_POSE_START + '\n' + BLOCK_START + '0' + BLOCK_SEPARATOR + '80' + BLOCK_SEPARATOR, 'You are now observing game 127.', 'Game 127: gbtami (0) puzzlebot (0) unrated untimed 0 0', '', '<12> --kr---r pp--bppp ----bn-- -Npp---q -----B-Q ---B---- PPP--PPP -K-R---R W -1 0 0 0 0 0 127 gbtami puzzlebot -2 0 0 0 0 0 0 1 none (0:00.000) none 0 0 0', BLOCK_END, 'fics% ', BLOCK_POSE_END, ] signal = 'obsGameCreated' game = FICSGame(FICSPlayer("gbtami"), FICSPlayer("puzzlebot"), gameno=127, relation=IC_POS_OBSERVING_EXAMINATION) game = self.connection.games.get(game) expectedResults = (game, ) self.runAndAssertEquals(signal, lines, expectedResults) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ 'Removing game 127 from observation list.' '', 'puzzlebot has made you an examiner of game 127.', '', '<12> --kr---r pp--bppp ----bn-- -Npp---q -----B-Q ---B---- PPP--PPP -K-R---R W -1 0 0 0 0 0 127 gbtami puzzlebot 2 0 0 0 0 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: This is mate problem number [00655]', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: White moves and mates in 2 moves.', 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) # check that our relation has changed as expected self.assertEqual(game.relation, IC_POS_EXAMINATING) # try with a wrong move first: h4h5 lines = [ 'puzzlebot stopped examining game 127.', 'fics% ', BLOCK_START + '109' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> --kr---r pp--bppp ----bn-- -Npp---Q -----B-- ---B---- PPP--PPP -K-R---R B -1 0 0 0 0 0 127 gbtami puzzlebot 2 0 0 0 247 0 0 1 Q/h4-h5 (0:00.000) Qxh5 0 0 0', '', 'Game 127: gbtami moves: Qxh5', BLOCK_END, 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) game = self.connection.games.get(game) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 1) # puzzlebot just backs up our move and sends the puzzle starting position again lines = [ BLOCK_POSE_START + '\n' + BLOCK_START + '0' + BLOCK_SEPARATOR + '80' + BLOCK_SEPARATOR, 'puzzlebot is now an examiner of game 127.', BLOCK_END, 'fics% ', BLOCK_POSE_END, 'Game 127: puzzlebot backs up 1 move.', '', '<12> --kr---r pp--bppp ----bn-- -Npp---q -----B-Q ---B---- PPP--PPP -K-R---R W -1 0 0 0 0 0 127 gbtami puzzlebot 2 0 0 34 35 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', 'Game 127: Still in progress *', '', 'puzzlebot(TD)(----)[127] kibitzes: There is a better move.', 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 0) # now we take the good move: b5a7 lines = [ 'puzzlebot stopped examining game 127.', 'fics% ', BLOCK_START + '114' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> --kr---r Np--bppp ----bn-- --pp---q -----B-Q ---B---- PPP--PPP -K-R---R B -1 0 0 0 0 0 127 gbtami puzzlebot 2 0 0 34 34 0 0 1 N/b5-a7 (0:00.000) Nxa7+ 0 0 0', '', 'Game 127: gbtami moves: Nxa7+', BLOCK_END, 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 1) # puzzlebot moves lines = [ BLOCK_POSE_START + '\n' + BLOCK_START + '0' + BLOCK_SEPARATOR + '75' + BLOCK_SEPARATOR, 'puzzlebot is now an examiner of game 127.', BLOCK_END, 'fics% ', BLOCK_POSE_END, '<12> ---r---r Np-kbppp ----bn-- --pp---q -----B-Q ---B---- PPP--PPP -K-R---R W -1 0 0 0 0 1 127 gbtami puzzlebot 2 0 0 34 34 0 0 2 K/c8-d7 (0:00.000) Kd7 0 0 0', 'fics% ', 'Game 127: puzzlebot moves: Kd7', 'fics% ' ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) print(self.games_persp.cur_gmwidg().gamemodel.moves) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 2) # we make the mating move: d3b5 lines = [ 'puzzlebot stopped examining game 127.', 'fics% ', BLOCK_START + '117' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> ---r---r Np-kbppp ----bn-- -Bpp---q -----B-Q -------- PPP--PPP -K-R---R B -1 0 0 0 0 2 127 gbtami puzzlebot 2 0 0 34 34 0 0 2 B/d3-b5 (0:00.000) Bb5# 0 0 0', '' 'Game 127: gbtami moves: Bb5#', '', 'Game 127: Black checkmated 1-0', BLOCK_END, 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: You solved problem number [00655] in 02m01s', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: You made 1 wrong moves and needed 0 hints', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: To solve the next problem type "tell puzzlebot next". To solve the previous problem type "tell puzzlebot previous" To automatically load problems type "tell puzzlebot auto" Or type "tell puzzlebot getmate" or "tell puzzlebot gettactics" or "tell puzzlebot getstudy"', 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 3) ######################################################################## # now start another mate in 2 puzzle # tell puzzlebot next lines = [ BLOCK_START + '125' + BLOCK_SEPARATOR + '132' + BLOCK_SEPARATOR, '(told puzzlebot, who is examining a game)', BLOCK_END, 'fics% ', BLOCK_POSE_START + '\n' + BLOCK_START + '0' + BLOCK_SEPARATOR + '80' + BLOCK_SEPARATOR, 'You are now observing game 127.', 'Game 127: gbtami (0) puzzlebot (0) unrated untimed 0 0', '', '<12> -----k-- -R------ --p--n-- -------- P-P--pr- -P---RNK -----PP- ----r--- B -1 0 0 0 0 0 127 puzzlebot gbtami -2 0 0 0 0 0 0 1 none (0:00.000) none 0 0 0', ] signal = 'obsGameCreated' game = FICSGame(FICSPlayer("puzzlebot"), FICSPlayer("gbtami"), gameno=127, relation=IC_POS_OBSERVING_EXAMINATION) game = self.connection.games.get(game) expectedResults = (game, ) self.runAndAssertEquals(signal, lines, expectedResults) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ 'Removing game 127 from observation list.' '', 'puzzlebot has made you an examiner of game 127.', '', '<12> -----k-- -R------ --p--n-- -------- P-P--pr- -P---RNK -----PP- ----r--- B -1 0 0 0 0 0 127 puzzlebot gbtami 2 0 0 0 0 0 0 1 none (0:00.000) none 0 0 0', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: This is mate problem number [00656]', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: Black moves and mates in 2 moves.', 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) # check that our relation has changed as expected self.assertEqual(game.relation, IC_POS_EXAMINATING) # g4g3 lines = [ 'puzzlebot stopped examining game 127.', 'fics% ', BLOCK_START + '147' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> -----k-- -R------ --p--n-- -------- P-P--p-- -P---RrK -----PP- ----r--- W -1 0 0 0 0 0 127 puzzlebot gbtami 2 0 0 253 0 0 0 2 R/g4-g3 (0:00.000) Rxg3+ 0 0 0', '', 'Game 127: gbtami moves: Rxg3+', BLOCK_END, 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 2) # puzzlebot moves lines = [ BLOCK_POSE_START + '\n' + BLOCK_START + '0' + BLOCK_SEPARATOR + '75' + BLOCK_SEPARATOR, 'puzzlebot is now an examiner of game 127.', BLOCK_END, 'fics% ', BLOCK_POSE_END, 'fics% ', '<12> -----k-- -R------ --p--n-- -------- P-P--p-- -P---RPK ------P- ----r--- B -1 0 0 0 0 0 127 puzzlebot gbtami 2 0 0 253 251 0 0 2 P/f2-g3 (0:00.000) fxg3 0 0 0', 'Game 127: puzzlebot moves: fxg3', 'fics% ' ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 3) # we make the mating move: e1h1 lines = [ 'puzzlebot stopped examining game 127.', 'fics% ', BLOCK_START + '150' + BLOCK_SEPARATOR + '1' + BLOCK_SEPARATOR, '<12> -----k-- -R------ --p--n-- -------- P-P--p-- -P---RPK ------P- -------r W -1 0 0 0 0 1 127 puzzlebot gbtami 2 0 0 253 251 0 0 3 R/e1-h1 (0:00.000) Rh1# 0 0 0', '' 'Game 127: gbtami moves: Rh1#', '', 'Game 127: White checkmated 0-1', BLOCK_END, 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: You solved problem number [00656] in 00m45s', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: You made 1 wrong moves and needed 0 hints', 'fics% ', 'puzzlebot(TD)(----)[127] kibitzes: To solve the next problem type "tell puzzlebot next". To solve the previous problem type "tell puzzlebot previous" To automatically load problems type "tell puzzlebot auto" Or type "tell puzzlebot getmate" or "tell puzzlebot gettactics" or "tell puzzlebot getstudy"', 'fics% ', ] async def coro(): await self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) print(self.games_persp.cur_gmwidg().gamemodel.boards[-1]) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 4)
def on_seek_add(self, match): # The <s> message looks like: # <s> index w=name_from ti=titles rt=rating t=time i=increment # r=rated('r')/unrated('u') tp=type("wild/fr", "wild/4","blitz") # c=color rr=rating_range(lower-upper) a=automatic?('t'/'f') # f=formula_checked('t'/f') parts = match.groups()[0].split(" ") seek = {} for key, value in [p.split("=") for p in parts[1:] if p]: seek[key] = value try: index = int(parts[0]) player = self.connection.players.get(FICSPlayer(seek['w'])) player.titles |= parse_title_hex(seek['ti']) rated = seek['r'] == 'r' minutes = int(seek['t']) increment = int(seek['i']) rmin, rmax = [int(r) for r in seek['rr'].split("-")] rating = seek['rt'] if rating[-1] in (" ", "P", "E"): deviation = DEVIATION[rating[-1]] rating = rating[:-1] rating = int(rating) deviation = None automatic = seek['a'] == 't' color = None if seek['c'] == "W": color = "white" elif seek['c'] == "B": color = "black" except KeyError as e: log.warning("on_seek_add: KeyError: %s %s" % (repr(e), repr(seek))) return try: gametype = GAME_TYPES[seek["tp"]] except KeyError: if self.connection.FatICS and seek["tp"] == "chess": # TODO: remove when fixed in FatICS expected_time = minutes + increment * 2 / 3 if expected_time == 0: gametype = "untimed" elif expected_time < 3: gametype = "lightning" elif expected_time < 15: gametype = "blitz" else: gametype = "standard" gametype = GAME_TYPES[gametype] else: return if gametype.variant_type in UNSUPPORTED: return try: ratingobj = player.ratings[gametype.rating_type] if ratingobj.elo != rating: ratingobj.elo = rating ratingobj.deviation = deviation except KeyError: pass seek = FICSSeek(index, player, minutes, increment, rated, color, gametype, rmin=rmin, rmax=rmax, automatic=automatic) self.emit("addSeek", seek)
def matchDeclined(self, match): decliner, = match.groups() decliner = self.connection.players.get(FICSPlayer(decliner), create=False) self.emit("matchDeclined", decliner)
def test2(self): """ Test observing lecturebot """ # ♜ ♛ . . ♚ . ♞ ♜ # ♟ ♟ . ♝ . ♟ ♟ ♟ # . . . ♝ ♟ . . . # . . . ♘ . . . . # . . . . . . ♕ . # . . . ♗ . . . . # ♙ ♙ . . . ♙ ♙ . # ♖ . ♗ . ♖ . . ♔ lines = [ BLOCK_START + '53' + BLOCK_SEPARATOR + '80' + BLOCK_SEPARATOR, 'You are now observing game 1.', 'Game 1: LectureBot (0) LectureBot (0) unrated untimed 0 0', '', '<12> rq--k-nr pp-b-ppp ---bp--- ---N---- ------Q- ---B---- PP---PP- R-B-R--K W -1 0 0 1 1 2 1 LectureBot LectureBot -2 0 0 32 34 0 0 15 B/h2-d6 (0:00.000) Bd6 0 0 0', BLOCK_END ] signal = 'obsGameCreated' game = FICSGame( FICSPlayer("LectureBot"), FICSPlayer("LectureBot"), gameno=1) game = self.connection.games.get(game) expectedResults = (game, ) self.runAndAssertEquals(signal, lines, expectedResults) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ "<12> rq--k-nr pp-b-pQp ---bp--- ---N---- -------- ---B---- PP---PP- R-B-R--K B -1 0 0 1 1 0 1 LectureBot LectureBot -2 0 0 32 33 0 0 15 Q/g4-g7 (0:00.000) Qxg7 0 0 0", "fics% ", "Game 1: LectureBot moves: Qxg7", "nfics% ", "LectureBot(TD)(----)[1] kibitzes: Black is now going to drop a rook. Therefore, Black must find an alternate to 13...Bxh2+.", "fics% ", "LectureBot(TD)(----)[1] kibitzes: Let\'s back up and find the right 13th move.", "fics% " ] def coro(): yield from self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 29) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ "Game 1: LectureBot backs up 4 moves.", "<12> rq--k-nr pp-b-ppp ---bp--- ---N---- ------Q- ---B---- PP---PPP R-B-R-K- B -1 0 0 1 1 2 1 LectureBot LectureBot -2 0 0 33 34 0 0 13 Q/d1-g4 (0:00.000) Qg4 0 0 0", "fics% ", "LectureBot(TD)(----)[1] kibitzes: Can you find Black\'s best move?", "fics% " ] def coro(): yield from self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 25) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ "<12> rq---knr pp-b-ppp ---bp--- ---N---- ------Q- ---B---- PP---PPP R-B-R-K- W -1 0 0 0 0 3 1 LectureBot LectureBot -2 0 0 33 34 0 0 14 K/e8-f8 (0:00.000) Kf8 0 0 0", "fics% ", "Game 1: LectureBot moves: Kf8", "fics% ", "LectureBot(TD)(----)[1] kibitzes: Black needs to guard the g-pawn. This also gets the king out of the pin.", "fics% ", "<12> rq---knr pp-b-ppp ---bp--- ---N---- ------Q- ---B---- PP-B-PPP R---R-K- B -1 0 0 0 0 4 1 LectureBot LectureBot -2 0 0 33 34 0 0 14 B/c1-d2 (0:00.000) Bd2 0 0 0", "fics% ", "Game 1: LectureBot moves: Bd2", "fics% ", "LectureBot(TD)(----)[1] kibitzes: White doesn\'t have to move the attacked knight because if 14. exd5, 15. Qxd7 is a trade.", "fics% ", "LectureBot(TD)(----)[1] kibitzes: Not to mention, it would be a trade favorable to White.", "fics% ", "<12> rq---knr pp-b-pp- ---bp--- ---N---p ------Q- ---B---- PP-B-PPP R---R-K- W 7 0 0 0 0 0 1 LectureBot LectureBot -2 0 0 33 34 0 0 15 P/h7-h5 (0:00.000) h5 0 0 0", "fics% ", "Game 1: LectureBot moves: h5", "fics% ", "LectureBot(TD)(----)[1] kibitzes: Here, Black attacks White\'s queen, and will try to open the h-file for his rook.", "fics% " ] def coro(): yield from self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 28) print(self.games_persp.cur_gmwidg().gamemodel)
def on_player_connect(self, match): name = match.groups()[0] player = self.connection.players.get(FICSPlayer(name)) self.players.append(player) player.online = True
def onStyle12(self, match): style12 = match.groups()[0] gameno = int(style12.split()[15]) if gameno in self.queuedStyle12s: self.queuedStyle12s[gameno].append(style12) return try: self.gamemodelStartedEvents[gameno].wait() except KeyError: pass if gameno in self.castleSigns: castleSigns = self.castleSigns[gameno] else: castleSigns = ("k", "q") gameno, relation, curcol, ply, wname, bname, wms, bms, gain, lastmove, fen = \ self.parseStyle12(style12, castleSigns) # examine starts with a <12> line only if lastmove is None and relation == IC_POS_EXAMINATING: pgnHead = [("Event", "FICS examined game"), ("Site", "freechess.org"), ("White", wname), ("Black", bname), ("Result", "*"), ("SetUp", "1"), ("FEN", fen)] pgn = "\n".join(['[%s "%s"]' % line for line in pgnHead]) + "\n*\n" wplayer = self.connection.players.get(FICSPlayer(wname)) bplayer = self.connection.players.get(FICSPlayer(bname)) # examine from console or got mexamine in observed game if self.connection.examined_game is None: no_smoves = True game = FICSGame(wplayer, bplayer, gameno=int(gameno), game_type=GAME_TYPES["examined"], minutes=0, inc=0, board=FICSBoard(0, 0, pgn=pgn), relation=relation) self.connection.examined_game = game else: # examine an archived game from GUI no_smoves = False game = self.connection.examined_game game.gameno = int(gameno) game.relation = relation # game.game_type = GAME_TYPES["examined"] game = self.connection.games.get(game) # don't start new game in puzzlebot/endgamebot when they just reuse gameno if game.relation == IC_POS_OBSERVING_EXAMINATION or \ (game.board is not None and game.board.pgn == pgn): self.emit("boardUpdate", gameno, ply, curcol, lastmove, fen, wname, bname, wms, bms) return game.relation = relation game.board = FICSBoard(0, 0, pgn=pgn) self.gamesImObserving[game] = None # start a new game now or after smoves self.gamemodelStartedEvents[game.gameno] = threading.Event() if no_smoves: self.emit("exGameCreated", game) self.gamemodelStartedEvents[game.gameno].wait() else: if isinstance(game, FICSHistoryGame): self.connection.client.run_command( "smoves %s %s" % (self.connection.history_owner, game.history_no)) elif isinstance(game, FICSJournalGame): self.connection.client.run_command( "smoves %s %%%s" % (self.connection.journal_owner, game.journal_no)) elif isinstance(game, FICSAdjournedGame): self.connection.client.run_command( "smoves %s %s" % (self.connection.stored_owner, game.opponent.name)) self.connection.client.run_command("forward 999") else: self.emit("boardUpdate", gameno, ply, curcol, lastmove, fen, wname, bname, wms, bms)
def test1(self): """ Test observing game """ lines = [ "{Game 463 (schachbjm vs. Maras) Creating rated standard match.}", BLOCK_START + '34' + BLOCK_SEPARATOR + '80' + BLOCK_SEPARATOR, 'You are now observing game 463.', 'Game 463: schachbjm (2243) Maras (2158E) rated standard 45 45', '', '<12> -r------ --k----- ----p--- n-ppPb-p -----P-P -PP-K-P- PR------ --R----- W -1 0 0 0 0 11 463 schachbjm Maras 0 45 45 17 15 557871 274070 37 R/f8-b8 (0:10.025) Rb8 0 1 0', BLOCK_END ] def coro(): yield from self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(self.connection.client.commands[-1], "moves 463") signal = 'obsGameCreated' lines = [ 'Movelist for game 463:', '', 'schachbjm (2243) vs. Maras (2158) --- Sat Jan 23, 14:34 EST 2016', 'Rated standard match, initial time: 45 minutes, increment: 45 seconds.', '', 'Move schachbjm Maras', '---- --------------------- ---------------------', '1. e4 (0:00.000) e6 (0:00.000)', '2. d4 (0:01.617) d5 (0:02.220)', '3. Nc3 (0:00.442) Nc6 (0:54.807)', '4. e5 (0:40.427) Nge7 (0:28.205)', '5. Nf3 (0:21.570) Nf5 (0:28.818)', '6. h4 (1:17.369) h5 (4:58.315)', '7. Bg5 (0:55.946) Be7 (4:01.555)', '8. Qd2 (0:02.434) b6 (5:12.110)', '9. O-O-O (0:59.124) Bb7 (0:08.796)', '10. Kb1 (0:01.900) Qd7 (4:39.500)', '11. Bxe7 (19:59.514) Qxe7 (2:42.462)', '12. g3 (0:58.847) O-O-O (0:36.468)', '13. Bh3 (0:12.284) Nh6 (4:06.076)', '14. Ne2 (0:02.387) g6 (5:02.695)', '15. Nf4 (0:02.976) Kb8 (5:26.776)', '16. Rhe1 (2:33.781) Na5 (2:23.956)', '17. b3 (0:28.817) Rc8 (1:09.281)', '18. Ng5 (8:15.515) c5 (5:17.139)', '19. Bxe6 (12:26.052) fxe6 (1:14.670)', '20. Nxg6 (0:02.168) Qd7 (1:23.832)', '21. Nxh8 (0:02.249) Rxh8 (0:04.212)', '22. dxc5 (0:14.456) Nf5 (0:24.046)', '23. cxb6 (0:07.092) axb6 (0:03.296)', '24. Qb4 (0:42.800) Qc6 (2:48.991)', '25. Nf7 (2:09.657) Rc8 (0:37.030)', '26. Rd2 (0:01.602) Qc5 (5:03.082)', '27. Qxc5 (0:09.672) bxc5 (0:00.100)', '28. Nd6 (0:00.849) Rf8 (0:04.101)', '29. c3 (0:57.437) Kc7 (3:05.263)', '30. Nxf5 (1:51.872) Rxf5 (0:00.100)', '31. f4 (0:00.603) Bc6 (1:06.696)', '32. Kc2 (0:01.613) Be8 (0:07.670)', '33. Kd3 (1:39.823) Rf8 (1:28.227)', '34. Ke3 (0:06.207) Bg6 (0:08.648)', '35. Rc1 (3:24.100) Bf5 (1:11.762)', '36. Rb2 (0:13.173) Rb8 (0:10.025)', '{Still in progress} *', ] game = FICSGame( FICSPlayer("schachbjm"), FICSPlayer("Maras"), gameno=463) game = self.connection.games.get(game) expectedResults = (game, ) self.runAndAssertEquals(signal, lines, expectedResults) print(self.games_persp.cur_gmwidg().gamemodel) lines = [ '<12> -r------ --k----- ----p--- n-ppPb-p -----P-P -PP-K-P- PR------ ------R- B -1 0 0 0 0 11 463 schachbjm Maras 0 45 45 17 15 557871 274070 37 R/b2-g2 (0:10.025) Rg2 0 1 0', '<12> ------r- --k----- ----p--- n-ppPb-p -----P-P -PP-K-P- PR------ ------R- W -1 0 0 0 0 11 463 schachbjm Maras 0 45 45 17 15 557871 274070 38 R/b8-g8 (0:10.025) Rg8 0 1 0', ] def coro(): yield from self.connection.process_lines(lines) self.loop.run_until_complete(coro()) self.assertEqual(game.move_queue.qsize(), 0) self.assertEqual(self.games_persp.cur_gmwidg().gamemodel.ply, 74) print(self.games_persp.cur_gmwidg().gamemodel)