def test_start_fen(self): result = sf.start_fen("capablanca") self.assertEqual(result, CAPA) result = sf.start_fen("capahouse") self.assertEqual(result, CAPAHOUSE) result = sf.start_fen("xiangqi") self.assertEqual(result, XIANGQI) result = sf.start_fen("grandhouse") self.assertEqual(result, GRANDHOUSE) result = sf.start_fen("shogun") self.assertEqual(result, SHOGUN)
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 start_fen(self, variant, chess960=False, disabled_fen=""): if chess960: new_fen = self.shuffle_start() while new_fen == disabled_fen: new_fen = self.shuffle_start() return new_fen return sf.start_fen(variant)
def sanitize_fen(variant, initial_fen, chess960): # Initial_fen needs validation to prevent segfaulting in pyffish sanitized_fen = initial_fen start_fen = sf.start_fen(variant) # self.board.start_fen(self.variant) start = start_fen.split() init = initial_fen.split() # Cut off tail if len(init) > 6: init = init[:6] sanitized_fen = " ".join(init) # We need starting color invalid0 = len(init) < 2 # Only piece types listed in variant start position can be used later if variant == "makruk" or variant == "cambodian": non_piece = "~+0123456789[]fF" else: non_piece = "~+0123456789[]" invalid1 = any((c not in start[0] + non_piece for c in init[0])) # Required number of rows invalid2 = start[0].count("/") != init[0].count("/") # Accept zh FEN in lichess format (they use / instead if [] for pockets) if invalid2 and variant == "crazyhouse": if (init[0].count("/") == 8) and ("[" not in init[0]) and ("]" not in init[0]): k = init[0].rfind("/") init[0] = init[0][:k] + "[" + init[0][k + 1:] + "]" sanitized_fen = " ".join(init) invalid2 = False # Allowed starting colors invalid3 = len(init) > 1 and init[1] not in "bw" # Castling rights (and piece virginity) check invalid4 = False if variant == "seirawan" or variant == "shouse": invalid4 = len(init) > 2 and any((c not in "KQABCDEFGHkqabcdefgh-" for c in init[2])) elif chess960: if all((c in "KQkq-" for c in init[2])): chess960 = False else: invalid4 = len(init) > 2 and any((c not in "ABCDEFGHIJabcdefghij-" for c in init[2])) elif variant[-5:] != "shogi": invalid4 = len(init) > 2 and any((c not in start[2] + "-" for c in init[2])) # Number of kings invalid5 = init[0].count("k") != 1 or init[0].count("K") != 1 if invalid0 or invalid1 or invalid2 or invalid3 or invalid4 or invalid5: print(invalid0, invalid1, invalid2, invalid3, invalid4, invalid5) sanitized_fen = start_fen return False, start_fen else: return True, sanitized_fen
def newgame(self, variant, time_control): with self.lock: self.rank_conversion = sf.start_fen(variant).count('/') + 1 == 10 self.process.stdin.write('new\n') self.process.stdin.write('variant {}\n'.format(variant)) if self.partner: self.process.stdin.write('partner test\n') self.process.stdin.write('level {}\n'.format(time_control.format_xboard())) self.process.stdin.flush()
def __init__(self, engine1, engine2, time_control, variant='chess', games=1, start_fens=None): self.two_boards = sf.two_boards(variant) self.engines = [Engine([engine1]), Engine([engine2])] self.board2_engines = [Engine([engine1]), Engine([engine2])] if self.two_boards else None self.time_control = time_control self.variant = variant self.games = games self.start_fens = start_fens if start_fens else [sf.start_fen(variant)] self.score = [0, 0, 0]
def __init__(self, engine1, engine2, time_control, variant='chess', start_fen=None): self.engines = [engine1, engine2] self.time_control = time_control self.variant = variant self.start_fen = start_fen or sf.start_fen(variant) self.moves = [] self.result = None self.partner = None self.obtained_holdings = '' self.clock_times = [self.time_control.time, self.time_control.time] self.lock = threading.RLock()
def start_fen(self, variant, chess960=False): # pyffish gives internal color representation for shogi # "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL[-] w 0 1" if variant == "shogi": return SHOGI_FEN elif variant == "kyotoshogi": return KYOTOSHOGI_FEN elif variant == "minishogi": return MINISHOGI_FEN else: if chess960: return self.shuffle_start() else: return sf.start_fen(variant)
def test_validate_fen(self): # valid for variant, positions in variant_positions.items(): for fen in positions: self.assertEqual(sf.validate_fen(fen, variant), sf.FEN_OK, "{}: {}".format(variant, fen)) # invalid for variant, positions in invalid_variant_positions.items(): for fen in positions: self.assertNotEqual(sf.validate_fen(fen, variant), sf.FEN_OK, "{}: {}".format(variant, fen)) # chess960 self.assertEqual(sf.validate_fen(CHESS960, "chess", True), sf.FEN_OK, "{}: {}".format(variant, fen)) self.assertEqual(sf.validate_fen("nrbqbkrn/pppppppp/8/8/8/8/PPPPPPPP/NRBQBKRN w BGbg - 0 1", "newzealand", True), sf.FEN_OK, "{}: {}".format(variant, fen)) # all variants starting positions for variant in sf.variants(): fen = sf.start_fen(variant) self.assertEqual(sf.validate_fen(fen, variant), sf.FEN_OK, "{}: {}".format(variant, fen))
def test_short_castling(self): legals = [ 'f5f4', 'a7a6', 'b7b6', 'c7c6', 'd7d6', 'e7e6', 'i7i6', 'j7j6', 'a7a5', 'b7b5', 'c7c5', 'e7e5', 'i7i5', 'j7j5', 'b8a6', 'b8c6', 'h6g4', 'h6i4', 'h6j5', 'h6f7', 'h6g8', 'h6i8', 'd5a2', 'd5b3', 'd5f3', 'd5c4', 'd5e4', 'd5c6', 'd5e6', 'd5f7', 'd5g8', 'j8g8', 'j8h8', 'j8i8', 'e8f7', 'c8b6', 'c8d6', 'g6g2', 'g6g3', 'g6f4', 'g6g4', 'g6h4', 'g6e5', 'g6g5', 'g6i5', 'g6a6', 'g6b6', 'g6c6', 'g6d6', 'g6e6', 'g6f6', 'g6h8', 'f8f7', 'f8g8', 'f8i8' ] moves = [ 'b2b4', 'f7f5', 'c2c3', 'g8d5', 'a2a4', 'h8g6', 'f2f3', 'i8h6', 'h2h3' ] result = sf.legal_moves("capablanca", CAPA, moves) self.assertCountEqual(legals, result) self.assertIn("f8i8", result) moves = [ 'a2a4', 'f7f5', 'b2b3', 'g8d5', 'b1a3', 'i8h6', 'c1a2', 'h8g6', 'c2c4' ] result = sf.legal_moves("capablanca", CAPA, moves) self.assertIn("f8i8", result) moves = [ 'f2f4', 'g7g6', 'g1d4', 'j7j6', 'h1g3', 'b8a6', 'i1h3', 'h7h6' ] result = sf.legal_moves("capablanca", CAPA, moves) self.assertIn("f1i1", result) # Check that chess960 castling notation is used for otherwise ambiguous castling move # d1e1 is a normal king move, so castling has to be d1f1 result = sf.legal_moves( "diana", "rbnk1r/pppbpp/3p2/5P/PPPPPB/RBNK1R w KQkq - 2 3", []) self.assertIn("d1f1", result) # Test configurable piece perft legals = [ 'a3a4', 'b3b4', 'c3c4', 'd3d4', 'e3e4', 'f3f4', 'g3g4', 'e1e2', 'f1f2', 'b1a2', 'b1b2', 'b1c2', 'c1b2', 'c1c2', 'c1d2', 'a1a2', 'g1g2', 'd1c2', 'd1d2', 'd1e2' ] result = sf.legal_moves("yarishogi", sf.start_fen("yarishogi"), []) self.assertCountEqual(legals, result)
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)
def start_fen(self, variant, chess960=False): if chess960: return self.shuffle_start() else: return sf.start_fen(variant)
print(board.fen) board.print_pos() print(board.legal_moves()) board = FairyBoard("minishogi") print(board.fen) board.print_pos() print(board.legal_moves()) board = FairyBoard("kyotoshogi") print(board.fen) board.print_pos() print(board.legal_moves()) print("--- SHOGUN ---") print(sf.start_fen("shogun")) board = FairyBoard("shogun") for move in ("c2c4", "b8c6", "b2b4", "b7b5", "c4b5", "c6b8"): print("push move", move, board.get_san(move)) if board.move_stack: print("is_checked(), insuff material, draw?", board.is_checked(), board.insufficient_material(), board.is_claimable_draw()) board.push(move) board.print_pos() print(board.fen) print(board.legal_moves()) FEN = "rnb+fkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB+FKBNR w KQkq - 0 1" board = FairyBoard("shogun", initial_fen=FEN) for move in ("c2c4", "h7h6", "c4c5", "h6h5", "c5c6+", "h8h6", "c6b7", "g7g6", "b7a8", "c8a6", "a8b7", "b8c6", "d1b3", "a6e2+", "b3b6", "e7e6", "b6c7", "P@h4", "c7c8", "e2c4", "f1c4", "h6h8", "c4e6+", "g8h6", "b2b4", "h8g8", "b4b5", "g6g5", "b5c6", "f8e7", "c6c7", "d7e6", "c8b8", "B@b6"): print("push move", move, board.get_san(move)) if board.move_stack:
def sanitize_fen(variant, initial_fen, chess960): # Initial_fen needs validation to prevent segfaulting in pyffish sanitized_fen = initial_fen start_fen = sf.start_fen(variant) # self.board.start_fen(self.variant) start = start_fen.split() init = initial_fen.split() # Cut off tail if len(init) > 6: init = init[:6] sanitized_fen = " ".join(init) # We need starting color invalid0 = len(init) < 2 # Only piece types listed in variant start position can be used later if variant == "makruk" or variant == "cambodian": non_piece = "~+0123456789[]fF" else: non_piece = "~+0123456789[]" invalid1 = any((c not in start[0] + non_piece for c in init[0])) # Required number of rows invalid2 = start[0].count("/") != init[0].count("/") # Accept zh FEN in lichess format (they use / instead if [] for pockets) if invalid2 and variant == "crazyhouse": if (init[0].count("/") == 8) and ("[" not in init[0]) and ("]" not in init[0]): k = init[0].rfind("/") init[0] = init[0][:k] + "[" + init[0][k + 1:] + "]" sanitized_fen = " ".join(init) invalid2 = False # Allowed starting colors invalid3 = len(init) > 1 and init[1] not in "bw" # Castling rights (and piece virginity) check invalid4 = False if variant == "seirawan" or variant == "shouse": invalid4 = len(init) > 2 and any((c not in "KQABCDEFGHkqabcdefgh-" for c in init[2])) elif chess960: if all((c in "KQkq-" for c in init[2])): chess960 = False else: invalid4 = len(init) > 2 and any((c not in "ABCDEFGHIJabcdefghij-" for c in init[2])) elif variant[-5:] != "shogi": invalid4 = len(init) > 2 and any((c not in start[2] + "-" for c in init[2])) # Castling right need rooks and king placed in starting square if not invalid4: rows = init[0].split("/") backRankB = rows[1] if (variant == 'shako') else rows[0] backRankW = rows[-2] if (variant == 'shako') else rows[-1] rookPosQ = 1 if (variant == 'shako') else 0 rookPosK = -2 if (variant == 'shako') else -1 if ("q" in init[2] and backRankB[rookPosQ] != 'r') or \ ("k" in init[2] and backRankB[rookPosK] != 'r') or \ ("Q" in init[2] and backRankW[rookPosQ] != 'R') or \ ("K" in init[2] and backRankW[rookPosK] != 'R'): invalid4 = True # Number of kings invalid5 = init[0].count("k") != 1 or init[0].count("K") != 1 # Opp king already in check curr_color = init[1] opp_color = "w" if curr_color == "b" else "b" init[1] = init[1].replace(curr_color, opp_color) board = FairyBoard(variant, " ".join(init), chess960) invalid6 = board.is_checked() if invalid0 or invalid1 or invalid2 or invalid3 or invalid4 or invalid5 or invalid6: print(invalid0, invalid1, invalid2, invalid3, invalid4, invalid5, invalid6) sanitized_fen = start_fen return False, start_fen else: return True, sanitized_fen
def sanitize_fen(variant, initial_fen, chess960): # Prevent this particular one to fail on our general sastling check if variant == "capablanca" and initial_fen == CONSERVATIVE_CAPA_FEN: return True, initial_fen # Initial_fen needs validation to prevent segfaulting in pyffish sanitized_fen = initial_fen start_fen = sf.start_fen(variant) # self.board.start_fen(self.variant) start = start_fen.split() init = initial_fen.split() # Cut off tail if len(init) > 6: init = init[:6] sanitized_fen = " ".join(init) # We need starting color invalid0 = len(init) < 2 # Only piece types listed in variant start position can be used later if variant == "dobutsu": non_piece = "~+0123456789[]hH-" elif variant == "orda": non_piece = "~+0123456789[]qH-" else: non_piece = "~+0123456789[]-" invalid1 = any((c not in start[0] + non_piece for c in init[0])) # Required number of rows invalid2 = start[0].count("/") != init[0].count("/") # Accept zh FEN in lichess format (they use / instead if [] for pockets) if invalid2 and variant == "crazyhouse": if (init[0].count("/") == 8) and ("[" not in init[0]) and ("]" not in init[0]): k = init[0].rfind("/") init[0] = init[0][:k] + "[" + init[0][k + 1:] + "]" sanitized_fen = " ".join(init) invalid2 = False # Allowed starting colors invalid3 = len(init) > 1 and init[1] not in "bw" # Castling rights (and piece virginity) check invalid4 = False if len(init) > 2: if variant in ("seirawan", "shouse"): invalid4 = any((c not in "KQABCDEFGHkqabcdefgh-" for c in init[2])) elif chess960: if all((c in "KQkq-" for c in init[2])): chess960 = False else: invalid4 = any((c not in "ABCDEFGHIJabcdefghij-" for c in init[2])) elif variant[-5:] != "shogi" and variant not in ("dobutsu", "gorogoro", "gorogoroplus"): invalid4 = any((c not in start[2] + "-" for c in init[2])) # Castling right need rooks and king placed in starting square if (not invalid2) and (not invalid4) and not (chess960 and (variant in ("seirawan", "shouse"))): rows = init[0].split("/") backRankB = rows[1] if (variant == 'shako') else rows[0] backRankW = rows[-2] if (variant == 'shako') else rows[-1] # cut off pockets k = backRankW.rfind("[") if k > 0: backRankW = backRankW[:k] rookPosQ = 1 if (variant == 'shako') else 0 rookPosK = -2 if (variant == 'shako') else -1 if ("q" in init[2] and backRankB[rookPosQ] != 'r') or \ ("k" in init[2] and backRankB[rookPosK] != 'r') or \ ("Q" in init[2] and backRankW[rookPosQ] != 'R') or \ ("K" in init[2] and backRankW[rookPosK] != 'R'): invalid4 = True # Number of kings bking = "l" if variant == "dobutsu" else "k" wking = "L" if variant == "dobutsu" else "K" invalid5 = init[0].count(bking) != 1 or init[0].count(wking) != 1 # Opp king already in check curr_color = init[1] opp_color = "w" if curr_color == "b" else "b" init[1] = init[1].replace(curr_color, opp_color) board = FairyBoard(variant, " ".join(init), chess960) invalid6 = board.is_checked() if invalid0 or invalid1 or invalid2 or invalid3 or invalid4 or invalid5 or invalid6: print(invalid0, invalid1, invalid2, invalid3, invalid4, invalid5, invalid6) sanitized_fen = start_fen return False, start_fen return True, sanitized_fen
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) 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')
def __init__(self, app, gameId, variant, initial_fen, wplayer, bplayer, base=1, inc=0, level=0, rated=False, chess960=False): self.db = app["db"] self.users = app["users"] self.games = app["games"] self.tasks = app["tasks"] self.highscore = app["highscore"] self.saved = False self.variant = variant self.initial_fen = initial_fen self.wplayer = wplayer self.bplayer = bplayer self.rated = rated self.base = base self.inc = inc self.level = level if level is not None else 0 self.chess960 = chess960 self.white_rating = wplayer.get_rating(variant, chess960) self.wrating = "%s%s" % (int( round(self.white_rating.mu, 0)), "?" if self.white_rating.phi > PROVISIONAL_PHI else "") self.wrdiff = 0 self.black_rating = bplayer.get_rating(variant, chess960) self.brating = "%s%s" % (int( round(self.black_rating.mu, 0)), "?" if self.black_rating.phi > PROVISIONAL_PHI else "") self.brdiff = 0 self.spectators = set() self.draw_offers = set() self.rematch_offers = set() self.messages = collections.deque([], 200) self.date = datetime.utcnow() self.ply_clocks = [{ "black": (base * 1000 * 60) + 0 if base > 0 else inc * 1000, "white": (base * 1000 * 60) + 0 if base > 0 else inc * 1000, "movetime": 0 }] self.dests = {} self.promotions = [] self.lastmove = None self.check = False self.status = CREATED self.result = "*" self.bot_game = False self.last_server_clock = monotonic() self.id = gameId # print("Game", self.variant, self.initial_fen, self.chess960) # Initial_fen needs validation to prevent segfaulting in pyffish if self.initial_fen: start_fen = sf.start_fen( self.variant) # self.board.start_fen(self.variant) start = start_fen.split() init = self.initial_fen.split() # Cut off tail if len(init) > 6: init = init[:6] self.initial_fen = " ".join(init) # We need starting color invalid0 = len(init) < 2 # Only piece types listed in variant start position can be used later invalid1 = any( (c not in start[0] + "~+" for c in init[0] if not c.isdigit())) # Required number of rows invalid2 = start[0].count("/") != init[0].count("/") # Allowed starting colors invalid3 = len(init) > 1 and init[1] not in "bw" # Castling rights (and piece virginity) check invalid4 = False if self.variant == "seirawan" or self.variant == "shouse": invalid4 = len(init) > 2 and any( (c not in "KQABCDEFGHkqabcdefgh-" for c in init[2])) elif self.chess960: if all((c in "KQkq-" for c in init[2])): self.chess960 = False else: invalid4 = len(init) > 2 and any( (c not in "ABCDEFGHIJabcdefghij-" for c in init[2])) elif self.variant[-5:] != "shogi": invalid4 = len(init) > 2 and any( (c not in start[2] + "-" for c in init[2])) if invalid0 or invalid1 or invalid2 or invalid3 or invalid4: log.error("Got invalid initial_fen %s for game %s" % (self.initial_fen, self.id)) print(invalid0, invalid1, invalid2, invalid3, invalid4) self.initial_fen = start_fen if self.chess960 and self.initial_fen: if self.wplayer.fen960_as_white == self.initial_fen: self.initial_fen = "" self.board = self.create_board(self.variant, self.initial_fen, self.chess960) self.initial_fen = self.board.initial_fen self.wplayer.fen960_as_white = self.initial_fen self.bot_game = self.bplayer.bot or self.wplayer.bot self.random_mover = self.wplayer.username == "Random-Mover" or self.bplayer.username == "Random-Mover" self.random_move = "" self.set_dests() if self.board.move_stack: self.check = self.board.is_checked() self.steps = [{ "fen": self.initial_fen if self.initial_fen else self.board.initial_fen, "san": None, "turnColor": "black" if self.board.color == BLACK else "white", "check": self.check }] self.stopwatch = Clock(self)