def pgn(doc): variant = C2V[doc["v"]] mlist = decode_moves(doc["m"], variant) chess960 = bool(int(doc.get("z"))) if "z" in doc else False if variant[-5:] == "shogi": mlist = list(map(uci2usi, mlist)) elif variant == "xiangqi" or variant == "grand" or variant == "grandhouse" or variant == "shako": mlist = list(map(zero2grand, mlist)) fen = doc[ "if"] if "if" in doc else SHOGI_FEN if variant == "shogi" else MINISHOGI_FEN if variant == "minishogi" else KYOTOSHOGI_FEN if variant == "kyotoshogi" else sf.start_fen( variant) mlist = sf.get_san_moves(variant, fen, mlist, chess960) moves = " ".join( (move if ind % 2 == 1 else "%s. %s" % (((ind + 1) // 2) + 1, move) for ind, move in enumerate(mlist))) no_setup = fen == STANDARD_FEN and not chess960 return '[Event "{}"]\n[Site "{}"]\n[Date "{}"]\n[Round "-"]\n[White "{}"]\n[Black "{}"]\n[Result "{}"]\n[TimeControl "{}+{}"]\n[WhiteElo "{}"]\n[BlackElo "{}"]\n[Variant "{}"]\n{fen}{setup}\n{} {}\n'.format( "PyChess " + ("rated" if doc["y"] == 1 else "casual") + " game", URI + "/" + doc["_id"], doc["d"].strftime("%Y.%m.%d"), doc["us"][0], doc["us"][1], C2R[doc["r"]], doc["b"] * 60, doc["i"], doc["p0"]["e"], doc["p1"]["e"], variant.capitalize() if not chess960 else VARIANT_960_TO_PGN[variant], moves, C2R[doc["r"]], fen="" if no_setup else '[FEN "%s"]\n' % fen, setup="" if no_setup else '[SetUp "1"]\n')
def pgn(doc): variant = C2V[doc["v"]] mlist = decode_moves(doc["m"], variant) chess960 = bool(int(doc.get("z"))) if "z" in doc else False if variant == "xiangqi" or variant == "grand" or variant == "grandhouse" or variant == "shako" or variant == "janggi": mlist = list(map(zero2grand, mlist)) fen = doc["if"] if "if" in doc else sf.start_fen(variant) mlist = sf.get_san_moves(variant, fen, mlist, chess960) moves = " ".join( (move if ind % 2 == 1 else "%s. %s" % (((ind + 1) // 2) + 1, move) for ind, move in enumerate(mlist))) no_setup = fen == STANDARD_FEN and not chess960 # Use lichess format for crazyhouse games to support easy import setup_fen = fen if variant != "crazyhouse" else fen.replace("[]", "") return '[Event "{}"]\n[Site "{}"]\n[Date "{}"]\n[Round "-"]\n[White "{}"]\n[Black "{}"]\n[Result "{}"]\n[TimeControl "{}+{}"]\n[WhiteElo "{}"]\n[BlackElo "{}"]\n[Variant "{}"]\n{fen}{setup}\n{} {}\n'.format( "PyChess " + ("rated" if "y" in doc and doc["y"] == 1 else "casual") + " game", URI + "/" + doc["_id"], doc["d"].strftime("%Y.%m.%d"), doc["us"][0], doc["us"][1], C2R[doc["r"]], doc["b"] * 60, doc["i"], doc["p0"]["e"] if "p0" in doc else "?", doc["p1"]["e"] if "p1" in doc else "?", variant.capitalize() if not chess960 else VARIANT_960_TO_PGN[variant], moves, C2R[doc["r"]], fen="" if no_setup else '[FEN "%s"]\n' % setup_fen, setup="" if no_setup else '[SetUp "1"]\n')
def test_encode_decode(self): for idx, variant in enumerate(VARIANTS): print(idx, variant) variant = variant.rstrip("960") FEN = sf.start_fen(variant) # fill the pockets with possible pieces for empty_pocket in ("[]", "[-]"): if empty_pocket in FEN: pocket = "".join([ i for i in set(FEN.split()[0]) if i in string.ascii_letters and i not in "Kk" ]) parts = FEN.split(empty_pocket) FEN = "%s[%s]%s" % (parts[0], pocket, parts[1]) board = FairyBoard(variant, initial_fen=FEN) moves = board.legal_moves() saved_restored = decode_moves(encode_moves(moves, variant), variant) self.assertEqual(saved_restored, moves)
async def load_game(app, game_id): """ Return Game object from app cache or from database """ db = app["db"] games = app["games"] users = app["users"] if game_id in games: return games[game_id] doc = await db.game.find_one({"_id": game_id}) if doc is None: return None wp, bp = doc["us"] if wp in users: wplayer = users[wp] else: wplayer = User(app, username=wp, anon=True) users[wp] = wplayer if bp in users: bplayer = users[bp] else: bplayer = User(app, username=bp, anon=True) users[bp] = bplayer variant = C2V[doc["v"]] initial_fen = doc.get("if") # Old USI Shogi games saved using usi2uci() need special handling usi_format = variant.endswith("shogi") and doc.get("uci") is None if usi_format: wplayer, bplayer = bplayer, wplayer if initial_fen: # print("load_game() USI SFEN was:", initial_fen) parts = initial_fen.split() if len(parts) > 3 and parts[1] in "wb": pockets = "[%s]" % parts[2] if parts[2] not in "-0" else "" initial_fen = parts[0] + pockets + ( " w" if parts[1] == "b" else " b") + " 0 " + parts[3] else: initial_fen = parts[0] + (" w" if parts[1] == "b" else " b") + " 0" # print(" changed to:", initial_fen) game = Game(app, game_id, variant, initial_fen, wplayer, bplayer, base=doc["b"], inc=doc["i"], byoyomi_period=int(bool(doc.get("bp"))), level=doc.get("x"), rated=bool(doc.get("y")), chess960=bool(doc.get("z")), create=False) mlist = decode_moves(doc["m"], variant) if mlist: game.saved = True if usi_format and variant == "shogi": mirror = mirror9 mlist = map(mirror, mlist) elif usi_format and (variant == "minishogi" or variant == "kyotoshogi"): mirror = mirror5 mlist = map(mirror, mlist) elif variant == "xiangqi" or variant == "grand" or variant == "grandhouse" or variant == "shako" or variant == "janggi": mlist = map(zero2grand, mlist) if "a" in doc: if usi_format and "m" in doc["a"][0]: doc["a"][0]["m"] = mirror(usi2uci(doc["a"][0]["m"])) game.steps[0]["analysis"] = doc["a"][0] if "mct" in doc: print(doc["mct"]) manual_count_toggled = iter(doc["mct"]) count_started = -1 count_ended = -1 for ply, move in enumerate(mlist): try: if "mct" in doc: # print("Ply", ply, "Move", move) if ply + 1 >= count_ended: try: game.board.count_started = -1 count_started, count_ended = next(manual_count_toggled) # print("New count interval", (count_started, count_ended)) except StopIteration: # print("Piece's honour counting started") count_started = 0 count_ended = MAX_PLY + 1 game.board.count_started = 0 if ply + 1 == count_started: # print("Count started", count_started) game.board.count_started = ply san = game.board.get_san(move) game.board.push(move) game.check = game.board.is_checked() turnColor = "black" if game.board.color == BLACK else "white" if usi_format: turnColor = "black" if turnColor == "white" else "white" game.steps.append({ "fen": game.board.fen, "move": move, "san": san, "turnColor": turnColor, "check": game.check }) if "a" in doc: if usi_format and "m" in doc["a"][ply + 1]: doc["a"][ply + 1]["m"] = mirror( usi2uci(doc["a"][ply + 1]["m"])) try: game.steps[-1]["analysis"] = doc["a"][ply + 1] except IndexError: print("IndexError", ply, move, san) except Exception: log.exception("ERROR: Exception in load_game() %s %s %s %s %s" % (game_id, variant, doc.get("if"), move, list(mlist))) break if len(game.steps) > 1: move = game.steps[-1]["move"] game.lastmove = move level = doc.get("x") game.date = doc["d"] game.status = doc["s"] game.level = level if level is not None else 0 game.result = C2R[doc["r"]] try: game.wrating = doc["p0"]["e"] game.wrdiff = doc["p0"]["d"] game.brating = doc["p1"]["e"] game.brdiff = doc["p1"]["d"] except KeyError: game.wrating = "1500?" game.wrdiff = "0" game.brating = "1500?" game.brdiff = "0" return game
def pgn(doc): variant = C2V[doc["v"]] mlist = decode_moves(doc["m"], variant) if len(mlist) == 0: return None chess960 = bool(int(doc.get("z"))) if "z" in doc else False initial_fen = doc.get("if") usi_format = variant.endswith("shogi") and doc.get("uci") is None if usi_format: # wplayer, bplayer = bplayer, wplayer if initial_fen: # print("load_game() USI SFEN was:", initial_fen) parts = initial_fen.split() if len(parts) > 3 and parts[1] in "wb": pockets = "[%s]" % parts[2] if parts[2] not in "-0" else "" initial_fen = parts[0] + pockets + ( " w" if parts[1] == "b" else " b") + " 0 " + parts[3] else: initial_fen = parts[0] + (" w" if parts[1] == "b" else " b") + " 0" # print(" changed to:", initial_fen) if usi_format and variant == "shogi": mirror = mirror9 mlist = list(map(mirror, mlist)) elif usi_format and (variant == "minishogi" or variant == "kyotoshogi"): mirror = mirror5 mlist = list(map(mirror, mlist)) elif variant == "xiangqi" or variant == "grand" or variant == "grandhouse" or variant == "shako" or variant == "janggi": mlist = list(map(zero2grand, mlist)) fen = initial_fen if initial_fen is not None else sf.start_fen(variant) # print(variant, fen, mlist) try: mlist = sf.get_san_moves(variant, fen, mlist, chess960) except Exception: try: mlist = sf.get_san_moves(variant, fen, mlist[:-1], chess960) except Exception: log.error("Movelist contains invalid move %s" % mlist) mlist = mlist[0] moves = " ".join( (move if ind % 2 == 1 else "%s. %s" % (((ind + 1) // 2) + 1, move) for ind, move in enumerate(mlist))) no_setup = fen == STANDARD_FEN and not chess960 # Use lichess format for crazyhouse games to support easy import setup_fen = fen if variant != "crazyhouse" else fen.replace("[]", "") return '[Event "{}"]\n[Site "{}"]\n[Date "{}"]\n[Round "-"]\n[White "{}"]\n[Black "{}"]\n[Result "{}"]\n[TimeControl "{}+{}"]\n[WhiteElo "{}"]\n[BlackElo "{}"]\n[Variant "{}"]\n{fen}{setup}\n{} {}\n'.format( "PyChess " + ("rated" if "y" in doc and doc["y"] == 1 else "casual") + " game", URI + "/" + doc["_id"], doc["d"].strftime("%Y.%m.%d"), doc["us"][0], doc["us"][1], C2R[doc["r"]], doc["b"] * 60, doc["i"], doc["p0"]["e"] if "p0" in doc else "?", doc["p1"]["e"] if "p1" in doc else "?", variant.capitalize() if not chess960 else VARIANT_960_TO_PGN[variant], moves, C2R[doc["r"]], fen="" if no_setup else '[FEN "%s"]\n' % setup_fen, setup="" if no_setup else '[SetUp "1"]\n')
async def load_game(app, game_id): db = app["db"] games = app["games"] users = app["users"] if game_id in games: return games[game_id] doc = await db.game.find_one({"_id": game_id}) if doc is None: return None wp = doc["us"][0] if wp in users: wplayer = users[wp] else: wplayer = User(db=db, username=wp, anon=True) users[wp] = wplayer bp = doc["us"][1] if bp in users: bplayer = users[bp] else: bplayer = User(db=db, username=bp, anon=True) users[bp] = bplayer variant = C2V[doc["v"]] game = Game(app, game_id, variant, doc.get("if"), wplayer, bplayer, doc["b"], doc["i"], doc.get("x"), bool(doc.get("y")), bool(doc.get("z"))) mlist = decode_moves(doc["m"], variant) if mlist: game.saved = True if variant[-5:] == "shogi": mlist = map(uci2usi, mlist) elif variant == "xiangqi" or variant == "grand" or variant == "grandhouse" or variant == "shako": mlist = map(zero2grand, mlist) if "a" in doc: game.steps[0]["analysis"] = doc["a"][0] for ply, move in enumerate(mlist): try: san = game.board.get_san(move) game.board.push(move) game.check = game.board.is_checked() game.steps.append({ "fen": game.board.fen, "move": move, "san": san, "turnColor": "black" if game.board.color == BLACK else "white", "check": game.check }) if "a" in doc: game.steps[-1]["analysis"] = doc["a"][ply + 1] except Exception: log.exception("ERROR: Exception in load_game() %s %s %s %s" % (game_id, variant, doc.get("if"), mlist)) break if len(game.steps) > 1: move = game.steps[-1]["move"] game.lastmove = move level = doc.get("x") game.date = doc["d"] game.status = doc["s"] game.level = level if level is not None else 0 game.result = C2R[doc["r"]] game.random_move = "" try: game.wrating = doc["p0"]["e"] game.wrdiff = doc["p0"]["d"] game.brating = doc["p1"]["e"] game.brdiff = doc["p1"]["d"] except KeyError: game.wrating = "1500?" game.wrdiff = "0" game.brating = "1500?" game.brdiff = "0" return game
async def get_user_games(request): users = request.app["users"] db = request.app["db"] profileId = request.match_info.get("profileId") if profileId is not None and profileId not in users: await asyncio.sleep(3) return web.json_response({}) # Who made the request? session = await aiohttp_session.get_session(request) session_user = session.get("user_name") filter_cond = {} # print("URL", request.rel_url) level = request.rel_url.query.get("x") variant = request.path[request.path.rfind("/") + 1 :] if "/win" in request.path: filter_cond["$or"] = [ {"r": "a", "us.0": profileId}, {"r": "b", "us.1": profileId}, ] elif "/loss" in request.path: # level8win requests Fairy-Stockfish lost games if level is not None: filter_cond["$and"] = [ {"$or": [{"r": "a", "us.1": profileId}, {"r": "b", "us.0": profileId}]}, {"x": int(level)}, {"$or": [{"if": None}, {"v": "j"}]}, # Janggi games always have initial FEN! { "$or": [ {"s": MATE}, {"s": VARIANTEND}, {"s": INVALIDMOVE}, {"s": CLAIM}, ] }, ] else: filter_cond["$or"] = [ {"r": "a", "us.1": profileId}, {"r": "b", "us.0": profileId}, ] elif "/rated" in request.path: filter_cond["$or"] = [{"y": 1, "us.1": profileId}, {"y": 1, "us.0": profileId}] elif "/import" in request.path: filter_cond["by"] = profileId filter_cond["y"] = 2 elif "/perf" in request.path and variant in VARIANTS: if variant.endswith("960"): v = V2C[variant[:-3]] z = 1 else: v = V2C[variant] z = 0 filter_cond["$or"] = [ {"v": v, "z": z, "us.1": profileId}, {"v": v, "z": z, "us.0": profileId}, ] elif "/me" in request.path: session = await aiohttp_session.get_session(request) session_user = session.get("user_name") filter_cond["$or"] = [ {"us.0": session_user, "us.1": profileId}, {"us.1": session_user, "us.0": profileId}, ] else: filter_cond["us"] = profileId page_num = request.rel_url.query.get("p") if not page_num: page_num = 0 game_doc_list = [] if profileId is not None: # print("FILTER:", filter_cond) cursor = db.game.find(filter_cond) cursor.sort("d", -1).skip(int(page_num) * GAME_PAGE_SIZE).limit(GAME_PAGE_SIZE) async for doc in cursor: # filter out private games if ( "p" in doc and doc["p"] == 1 and session_user != doc["us"][0] and session_user != doc["us"][1] ): continue doc["v"] = C2V[doc["v"]] doc["r"] = C2R[doc["r"]] doc["wt"] = users[doc["us"][0]].title if doc["us"][0] in users else "" doc["bt"] = users[doc["us"][1]].title if doc["us"][1] in users else "" doc["lm"] = decode_moves((doc["m"][-1],), doc["v"])[-1] if len(doc["m"]) > 0 else "" if doc["v"] in GRANDS and doc["lm"] != "": doc["lm"] = zero2grand(doc["lm"]) tournament_id = doc.get("tid") if tournament_id is not None: doc["tn"] = await get_tournament_name(request.app, tournament_id) game_doc_list.append(doc) return web.json_response(game_doc_list, dumps=partial(json.dumps, default=datetime.isoformat))
async def load_game(app, game_id): """ Return Game object from app cache or from database """ db = app["db"] games = app["games"] users = app["users"] if game_id in games: return games[game_id] doc = await db.game.find_one({"_id": game_id}) if doc is None: return None wp, bp = doc["us"] if wp in users: wplayer = users[wp] else: wplayer = User(app, username=wp, anon=True) users[wp] = wplayer if bp in users: bplayer = users[bp] else: bplayer = User(app, username=bp, anon=True) users[bp] = bplayer variant = C2V[doc["v"]] initial_fen = doc.get("if") # Old USI Shogi games saved using usi2uci() need special handling usi_format = variant.endswith("shogi") and doc.get("uci") is None if usi_format: wplayer, bplayer = bplayer, wplayer if initial_fen: print("load_game() USI SFEN was:", initial_fen) parts = initial_fen.split() pockets = "[%s]" % parts[2] initial_fen = parts[0] + pockets + (" w" if parts[1] == " b" else " w") + " 0 " + parts[3] print(" changed to:", initial_fen) game = Game(app, game_id, variant, initial_fen, wplayer, bplayer, doc["b"], doc["i"], doc.get("x"), bool(doc.get("y")), bool(doc.get("z")), create=False) mlist = decode_moves(doc["m"], variant) if mlist: game.saved = True if usi_format and variant == "shogi": mirror = mirror9 mlist = list(map(mirror, mlist)) elif usi_format and (variant == "minishogi" or variant == "kyotoshogi"): mirror = mirror5 mlist = list(map(mirror, mlist)) elif variant == "xiangqi" or variant == "grand" or variant == "grandhouse" or variant == "shako": mlist = map(zero2grand, mlist) if "a" in doc: if usi_format and "m" in doc["a"][0]: doc["a"][0]["m"] = mirror(usi2uci(doc["a"][0]["m"])) game.steps[0]["analysis"] = doc["a"][0] for ply, move in enumerate(mlist): try: san = game.board.get_san(move) game.board.push(move) game.check = game.board.is_checked() game.steps.append({ "fen": game.board.fen, "move": move, "san": san, "turnColor": "black" if game.board.color == BLACK else "white", "check": game.check} ) if "a" in doc: if usi_format and "m" in doc["a"][ply + 1]: doc["a"][ply + 1]["m"] = mirror(usi2uci(doc["a"][ply + 1]["m"])) game.steps[-1]["analysis"] = doc["a"][ply + 1] except Exception: log.exception("ERROR: Exception in load_game() %s %s %s %s" % (game_id, variant, doc.get("if"), mlist)) break if len(game.steps) > 1: move = game.steps[-1]["move"] game.lastmove = move level = doc.get("x") game.date = doc["d"] game.status = doc["s"] game.level = level if level is not None else 0 game.result = C2R[doc["r"]] game.random_move = "" try: game.wrating = doc["p0"]["e"] game.wrdiff = doc["p0"]["d"] game.brating = doc["p1"]["e"] game.brdiff = doc["p1"]["d"] except KeyError: game.wrating = "1500?" game.wrdiff = "0" game.brating = "1500?" game.brdiff = "0" return game