def testFEN(self): """Testing board-FEN conversion with several positions""" for i, fenstr in enumerate(self.positions[1:]): board = LBoard() board.applyFen(fenstr) fenstr2 = board.asFen() self.assertEqual(fenstr, fenstr2)
def test_pdb_database_get_bitboards(self): """Testing .pdb database get_bitboards""" db = self.load_test_pgn() board = LBoard() board.applyFen(FEN_START) for ply in range(4): bb_candidates = {} for lmove in genAllMoves(board): board.applyMove(lmove) if board.opIsChecked(): board.popMove() continue bb_candidates[board.friends[0] | board.friends[1]] = lmove board.popMove() bitboards = db.get_bitboards(ply, bb_candidates) print("==========") for row in bitboards: print(row) print("----------") self.assertEqual(len(bitboards), BITBOARD_COUNT[ply]) self.assertEqual(sum([row[1] for row in bitboards]), GAME_COUNT)
def run(cls, fenstr, variant): cls._ensureReady() if cls.widgets["newgamedialog"].props.visible: cls.widgets["newgamedialog"].present() return cls._hideOthers() for button in ("copy_button", "clear_button", "paste_button", "initial_button"): cls.widgets[button].show() cls.widgets["newgamedialog"].set_title(_("Setup Position")) cls.widgets["setupPositionSidePanel"].show() cls.setupmodel = SetupModel() cls.board_control = BoardControl(cls.setupmodel, {}, setup_position=True) cls.setupmodel.curplayer = SetupPlayer(cls.board_control) cls.setupmodel.connect("game_changed", cls.game_changed) child = cls.widgets["setupBoardDock"].get_child() if child is not None: cls.widgets["setupBoardDock"].remove(child) cls.widgets["setupBoardDock"].add(cls.board_control) cls.board_control.show_all() if fenstr is not None: lboard = LBoard(variant) lboard.applyFen(fenstr) cls.setupmodel.boards = [cls.setupmodel.variant(setup=fenstr, lboard=lboard)] cls.setupmodel.variations = [cls.setupmodel.boards] cls.ini_widgets(fenstr, lboard) else: fenstr = cls.get_fen() cls.ini_widgets(True) cls.widgets["fen_entry"].set_text(fenstr) cls.setupmodel.start() cls.board_control.emit("action", "SETUP", None, fenstr) def _validate(gamemodel): try: fenstr = cls.get_fen() cls.setupmodel.variant(setup=fenstr) return True except (AssertionError, LoadingError, SyntaxError) as e: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK, message_format=e.args[0]) if len(e.args) > 1: d.format_secondary_text(e.args[1]) d.connect("response", lambda d, a: d.hide()) d.show() return False def _callback(gamemodel, p0, p1): text = cls.get_fen() perspective = perspective_manager.get_perspective("games") create_task(perspective.generalStart( gamemodel, p0, p1, (StringIO(text), fen, 0, -1))) cls._generalRun(_callback, _validate)
def test_gating_king_move(self): board = LBoard(SCHESS) board.applyFen(SCHESSSTART) for move in "e2e3 e7e5 d1h5 d8e7 h5g4 b8c6 g1f3 d7d5 g4h4 e7c5 h4a4 e5e4 d2d4 c5e7 f3d2 e7b4 a4b4 c6b4".split(): board.applyMove(parseAN(board, move)) print("--------") print(board) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) self.assertIn("e1d1e", moves) self.assertIn("e1d1h", moves) fen = board.asFen() move = parseAN(board, "e1d1e") board.applyMove(move) print("e1d1e") print(board) self.assertEqual(placement(board.asFen()), "r1b1kbnr/ppp2ppp/8/3p4/1n1Pp3/4P3/PPPN1PPP/RNBKEB1R[heH]") board.popMove() self.assertEqual(placement(board.asFen()), placement(fen))
def feed_book(self, records, positions): BOOK_DEPTH_MAX = conf.get("book_depth_max") for rec in records: model = GameModel() if rec["Result"] == DRAW: score = (1, 1) elif rec["Result"] == WHITEWON: score = (2, 0) elif rec["Result"] == BLACKWON: score = (0, 2) else: score = (0, 0) fenstr = rec["FEN"] variant = self.chessfile.get_variant(rec) if variant: model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError: continue else: board.applyFen(FEN_START) boards = [board] movetext = self.chessfile.get_movetext(rec) boards = self.chessfile.parse_movetext(movetext, boards[0], -1) for board in boards: if board.plyCount > BOOK_DEPTH_MAX: break move = board.lastMove if move is not None: poly_move = toPolyglot(board.prev, move) # move_str = "%s%s" % (reprCord[FCORD(move)], reprCord[TCORD(move)]) # print("%0.16x" % board.prev.hash, poly_move, board.prev.asFen(), move_str) if board.prev.hash in positions: if poly_move in positions[board.prev.hash]: positions[board.prev.hash][poly_move] += score[ board.prev.color ] else: positions[board.prev.hash][poly_move] = score[ board.prev.color ] else: # board.prev.asFen(), move_str, positions[board.prev.hash] = { poly_move: score[board.prev.color] }
def benchmark(maxdepth=6): """ Times a search of a static list of positions. """ suite_time = time() suite_nodes = lsearch.nodes lsearch.endtime = sys.maxsize lsearch.searching = True for i, fen in enumerate(benchmarkPositions): lsearch.table.clear() clearPawnTable() board = LBoard(NORMALCHESS) board.applyFen(fen) pos_start_time = time() pos_start_nodes = lsearch.nodes for depth in range(1, maxdepth): mvs, scr = lsearch.alphaBeta(board, depth) pos_time = time() - pos_start_time pos_nodes = lsearch.nodes - pos_start_nodes pv = " ".join(listToSan(board, mvs)) time_cs = int(100 * pos_time) print(depth, scr, time_cs, pos_nodes, pv) print("Searched position", i, "at", int(pos_nodes / pos_time) if pos_time > 0 else pos_nodes, "n/s") suite_time = time() - suite_time suite_nodes = lsearch.nodes - suite_nodes print("Total:", suite_nodes, "nodes in", suite_time, "s: ", suite_nodes / suite_time, "n/s") lsearch.nodes = 0
def benchmark(maxdepth=6): """ Times a search of a static list of positions. """ suite_time = time() suite_nodes = lsearch.nodes lsearch.endtime = sys.maxsize lsearch.searching = True for i, fen in enumerate(benchmarkPositions): lsearch.table.clear() clearPawnTable() board = LBoard(NORMALCHESS) board.applyFen(fen) pos_start_time = time() pos_start_nodes = lsearch.nodes for depth in range(1, maxdepth): mvs, scr = lsearch.alphaBeta(board, depth) pos_time = time() - pos_start_time pos_nodes = lsearch.nodes - pos_start_nodes pv = " ".join(listToSan(board, mvs)) time_cs = int(100 * pos_time) print(depth, scr, time_cs, pos_nodes, pv) print("Searched position", i, "at", int(pos_nodes / pos_time) if pos_time > 0 else pos_nodes, "n/s") suite_time = time() - suite_time suite_nodes = lsearch.nodes - suite_nodes print("Total:", suite_nodes, "nodes in", suite_time, "s: ", suite_nodes / suite_time, "n/s") lsearch.nodes = 0
def test_default_gating_avail(self): board = LBoard(SCHESS) board.applyFen(SCHESSSTART) print(board) print(toString(board.virgin[0])) print(toString(board.virgin[1])) self.assertEqual(board.virgin[0], brank8[1]) self.assertEqual(board.virgin[1], brank8[0]) board = LBoard(SCHESS) FEN = "r1hqerk1/pp1nbppp/2pp1nb1/4p3/2PPP3/2N1B1PP/PP2NPB1/RH1QK2R/E w KQHEDA - 2 10" board.applyFen(FEN) print("-----------") print(board) move = parseSAN(board, "O-O") board.applyMove(move) castl = board.asFen().split()[2] self.assertNotIn("Q", castl) self.assertNotIn("K", castl) self.assertNotIn("E", castl) self.assertNotIn("H", castl)
def as_fen(self, variant): fenstr = [] for r, row in enumerate(reversed(self.data)): empty = 0 for i in range(0, 8): piece = row.get(i) if piece is not None: if empty > 0: fenstr.append(str(empty)) empty = 0 sign = reprSign[piece.piece] if piece.color == BLACK: sign = sign.lower() else: sign = sign.upper() fenstr.append(sign) else: empty += 1 if empty > 0: fenstr.append(str(empty)) if r != 7: fenstr.append("/") board = LBoard(variant) board.applyFen("".join(fenstr) + " w") return board.asFen().split()[0]
def test_gating_castle_at_rook_bOO(self): board = LBoard(SCHESS) board.applyFen(SCHESSSTART) for move in "e2e3 e7e6 g1f3 c7c6 c2c3 d7d5 d2d4 g8f6 h2h3 f6e4 d1c2 b8d7 f1d3 f7f5 e1g1e d8c7 e1e2 f8e7 c2d1".split(): board.applyMove(parseAN(board, move)) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) print("--------") print(board) self.assertIn("h8e8h", moves) self.assertIn("h8e8e", moves) fen = board.asFen() move = parseAN(board, "h8e8h") board.applyMove(move) print("h8e8e") print(board) self.assertEqual(placement(board.asFen()), "r1b2rkh/ppqnb1pp/2p1p3/3p1p2/3Pn3/2PBPN1P/PP2EPP1/RNBQ1RK1[eH]") board.popMove() self.assertEqual(placement(board.asFen()), placement(fen))
def test_gating_castle_at_rook_wOO(self): FEN = "r2qk2r/pppbbppp/4pn2/1N1p4/1n1P4/4PN2/PPPBBPPP/R2QK2R[heHE] w KQkq - 10 8" board = LBoard(SCHESS) board.applyFen(FEN) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) print("--------") print(board) self.assertIn("h1e1h", moves) self.assertIn("h1e1e", moves) self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O/Hh1'))), 'h1e1h') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O/Eh1'))), 'h1e1e') self.assertEqual(toSAN(board, newMove(H1, E1, HAWK_GATE_AT_ROOK)), "O-O/Hh1") self.assertEqual(toSAN(board, newMove(H1, E1, ELEPHANT_GATE_AT_ROOK)), "O-O/Eh1") fen = board.asFen() move = parseAN(board, "h1e1e") board.applyMove(move) print("h1e1e") print(board) self.assertEqual(placement(board.asFen()), "r2qk2r/pppbbppp/4pn2/1N1p4/1n1P4/4PN2/PPPBBPPP/R2Q1RKE[heH]") board.popMove() self.assertEqual(placement(board.asFen()), placement(fen))
def test_gating_castle_at_rook_wOOO(self): board = LBoard(SCHESS) board.applyFen(SCHESSSTART) for move in "e2e4 d7d6 d1e2 c8d7 g1f3 g8f6 e4e5 f6d5 e2e4 d5b6 d2d3 d7c6 e4f5 c6d7 f5g5 g7g6 f3d4 h7h5 g5f4 d6e5 f4h4 f8g7 d4f3 d7g4 b1c3 f7f6 c3e4 d8d5 e4c3 d5c5 c1e3 c5d6 h2h3 g4f3 g2f3 e8g8h".split(): board.applyMove(parseAN(board, move)) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) print("--------") print(board) self.assertIn("a1e1h", moves) self.assertIn("a1e1e", moves) fen = board.asFen() parseAN(board, "a1e1e") board.applyMove(move) print("a1e1e") print(board) self.assertEqual(placement(board.asFen()), "rn2hrk1/ppp1p1b1/1n1q1pp1/4p2p/7Q/2NPBP1P/PPP2P2/E1KR1B1R[eH]") board.popMove() self.assertEqual(placement(board.asFen()), placement(fen))
def testFEN(self): """Testing board-FEN conversion with several positions""" for i, fenstr in enumerate(self.positions[1:]): board = LBoard() board.applyFen(fenstr) fenstr2 = board.asFen() self.assertEqual(fenstr, fenstr2)
def testPolyglot_1(self): """Testing hash keys agree with Polyglot's""" for testcase in testcases: board = LBoard(Board) board.applyFen(testcase[0]) self.assertEqual(board.hash, testcase[1])
def test_paresSAN1(self): """Testing parseSAN with unambiguous notations variants""" board = LBoard() board.applyFen("4k2B/8/8/8/8/8/8/B3K3 w - - 0 1") self.assertEqual(repr(Move(parseSAN(board, 'Ba1b2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bh8b2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'Bab2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bhb2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'B1b2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'B8b2'))), 'h8b2') board = LBoard() board.applyFen("4k2B/8/8/8/8/8/1b6/B3K3 w - - 0 1") self.assertEqual(repr(Move(parseSAN(board, 'Ba1xb2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bh8xb2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'Baxb2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bhxb2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'B1xb2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'B8xb2'))), 'h8b2')
def test_paresSAN1(self): """Testing parseSAN with unambiguous notations variants""" board = LBoard() board.applyFen("4k2B/8/8/8/8/8/8/B3K3 w - - 0 1") self.assertEqual(repr(Move(parseSAN(board, 'Ba1b2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bh8b2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'Bab2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bhb2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'B1b2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'B8b2'))), 'h8b2') board = LBoard() board.applyFen("4k2B/8/8/8/8/8/1b6/B3K3 w - - 0 1") self.assertEqual(repr(Move(parseSAN(board, 'Ba1xb2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bh8xb2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'Baxb2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'Bhxb2'))), 'h8b2') self.assertEqual(repr(Move(parseSAN(board, 'B1xb2'))), 'a1b2') self.assertEqual(repr(Move(parseSAN(board, 'B8xb2'))), 'h8b2')
def testPolyglot_1(self): """Testing hash keys agree with Polyglot's""" for testcase in testcases: board = LBoard(Board) board.applyFen(testcase[0]) self.assertEqual(board.hash, testcase[1])
def create_fen(self, pieces): """ Create a random FEN position using given pieces """ pos = pieces.rfind("k") pieces = pieces[:pos], pieces[pos:] ok = False while not ok: lboard = LBoard() lboard.applyFen("8/8/8/8/8/8/8/8 w - - 0 1") cords = list(range(0, 64)) pawn_cords = list(range(0 + 8, 64 - 8)) for color in (BLACK, WHITE): for char in pieces[color]: piece = chrU2Sign[char.upper()] cord = random.choice(pawn_cords if char == "p" else cords) lboard._addPiece(cord, piece, color) cords.remove(cord) if cord in pawn_cords: pawn_cords.remove(cord) # TODO: 2 same color bishop is not ok ok = (not lboard.isChecked()) and (not lboard.opIsChecked()) fen = lboard.asFen() return fen
def run(cls, fenstr, variant): cls._ensureReady() if cls.widgets["newgamedialog"].props.visible: cls.widgets["newgamedialog"].present() return cls._hideOthers() for button in ("copy_button", "clear_button", "paste_button", "initial_button"): cls.widgets[button].show() cls.widgets["newgamedialog"].set_title(_("Setup Position")) cls.widgets["setupPositionSidePanel"].show() cls.setupmodel = SetupModel() cls.board_control = BoardControl(cls.setupmodel, {}, setup_position=True) cls.setupmodel.curplayer = SetupPlayer(cls.board_control) cls.setupmodel.connect("game_changed", cls.game_changed) child = cls.widgets["setupBoardDock"].get_child() if child is not None: cls.widgets["setupBoardDock"].remove(child) cls.widgets["setupBoardDock"].add(cls.board_control) cls.board_control.show_all() if fenstr is not None: lboard = LBoard(variant) lboard.applyFen(fenstr) cls.setupmodel.boards = [cls.setupmodel.variant(setup=fenstr, lboard=lboard)] cls.setupmodel.variations = [cls.setupmodel.boards] cls.ini_widgets(fenstr, lboard) else: fenstr = cls.get_fen() cls.ini_widgets(True) cls.widgets["fen_entry"].set_text(fenstr) cls.setupmodel.start() def _validate(gamemodel): try: fenstr = cls.get_fen() cls.setupmodel.variant(setup=fenstr) return True except (AssertionError, LoadingError, SyntaxError) as e: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK, message_format=e.args[0]) if len(e.args) > 1: d.format_secondary_text(e.args[1]) d.connect("response", lambda d, a: d.hide()) d.show() return False def _callback(gamemodel, p0, p1): text = cls.get_fen() perspective = perspective_manager.get_perspective("games") create_task(perspective.generalStart( gamemodel, p0, p1, (StringIO(text), fen, 0, -1))) cls._generalRun(_callback, _validate)
def test_toSAN(self): """ Testing toSAN() with giveaway king move """ board = LBoard(GIVEAWAYCHESS) board.applyFen("4R3/8/7B/2P5/8/8/PP5k/6k1 b - - 0 28") self.assertEqual(toSAN(board, newMove(G1, G2)), "Kgg2") self.assertEqual(toSAN(board, newMove(H2, G2)), "Khg2")
def test_apply_pop(self): """Testing Atomic applyMove popMove""" board = LBoard(variant=ATOMICCHESS) board.applyFen(FEN) print(board) hist_exploding_around0 = [a[:] for a in board.hist_exploding_around] print_apply_pop = False for lmove1 in genAllMoves(board): board.applyMove(lmove1) if board.opIsChecked(): if print_apply_pop: print("popMove1 (invalid)", Move(lmove1)) board.popMove() continue hist_exploding_around1 = [ a[:] for a in board.hist_exploding_around ] for lmove2 in genAllMoves(board): board.applyMove(lmove2) if print_apply_pop: print(" applyMove2", Move(lmove2)) if board.opIsChecked(): if print_apply_pop: print(" popMove2 (invalid)", Move(lmove2)) board.popMove() continue hist_exploding_around2 = [ a[:] for a in board.hist_exploding_around ] for lmove3 in genAllMoves(board): board.applyMove(lmove3) if print_apply_pop: print(" applyMove3", Move(lmove3)) if board.opIsChecked(): if print_apply_pop: print(" popMove3 (invalid)", Move(lmove3)) board.popMove() continue board.popMove() if print_apply_pop: print(" popMove3", Move(lmove3)) self.assertEqual(hist_exploding_around2, board.hist_exploding_around) board.popMove() if print_apply_pop: print(" popMove2", Move(lmove2)) self.assertEqual(hist_exploding_around1, board.hist_exploding_around) board.popMove() if print_apply_pop: print("popMove1", Move(lmove1)) self.assertEqual(hist_exploding_around0, board.hist_exploding_around)
def test_gating_san_move(self): board = LBoard(SCHESS) board.applyFen(SCHESSSTART) self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/H'))), 'g1f3h') self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/E'))), 'g1f3e') self.assertEqual(toSAN(board, newMove(G1, F3, HAWK_GATE)), "Nf3/H") self.assertEqual(toSAN(board, newMove(G1, F3, ELEPHANT_GATE)), "Nf3/E")
def testFEN(self): """Testing board-FEN conversion with several positions""" print board = LBoard(Board) for i, fenstr in enumerate(self.positions[1:]): sys.stdout.write("#") board.applyFen(fenstr) fenstr2 = board.asFen() self.assertEqual(fenstr, fenstr2) print
def testFEN(self): """Testing board-FEN conversion with several positions""" print board = LBoard(Board) for i, fenstr in enumerate(self.positions[1:]): sys.stdout.write("#") board.applyFen(fenstr) fenstr2 = board.asFen() self.assertEqual(fenstr, fenstr2) print
def test_paresSAN2(self): """Testing parseAN and parseSAN with bad promotions moves""" board = LBoard() board.applyFen("4k3/P7/8/8/8/8/8/4K3 w - - 0 1") self.assertRaises(ParsingError, parseAN, board, 'a7a8K') self.assertRaises(ParsingError, parseAN, board, 'a7a8') self.assertRaises(ParsingError, parseSAN, board, 'a8K') self.assertRaises(ParsingError, parseSAN, board, 'a8')
def testFRCCastlingUCI(self): """Testing UCI engine FRC castling move""" print() fen = "rbq1krb1/pp1pp1pp/2p1n3/5p2/2PP1P1n/4B1N1/PP2P1PP/RBQNKR2 w FAfa - 2 6" print(fen) board = LBoard(FISCHERRANDOMCHESS) board.applyFen(fen) # print board moves = [move for move in genCastles(board)] self.assertTrue(parseAN(board, "e1g1") in moves)
def test_paresSAN2(self): """Testing parseAN and parseSAN with bad promotions moves""" board = LBoard() board.applyFen("4k3/P7/8/8/8/8/8/4K3 w - - 0 1") self.assertRaises(ParsingError, parseAN, board, 'a7a8K') # If promotion piece is missing pychess assumes queen promotion from 0.99.2 # self.assertRaises(ParsingError, parseAN, board, 'a7a8') self.assertRaises(ParsingError, parseSAN, board, 'a8K')
def testFRCCastlingUCI(self): """Testing UCI engine FRC castling move""" print() fen = "rbq1krb1/pp1pp1pp/2p1n3/5p2/2PP1P1n/4B1N1/PP2P1PP/RBQNKR2 w FAfa - 2 6" print(fen) board = LBoard(FISCHERRANDOMCHESS) board.applyFen(fen) # print board moves = [move for move in genCastles(board)] self.assertTrue(parseAN(board, "e1g1") in moves)
def test_paresSAN2(self): """Testing parseAN and parseSAN with bad promotions moves""" board = LBoard() board.applyFen("4k3/P7/8/8/8/8/8/4K3 w - - 0 1") self.assertRaises(ParsingError, parseAN, board, 'a7a8K') self.assertRaises(ParsingError, parseAN, board, 'a7a8') self.assertRaises(ParsingError, parseSAN, board, 'a8K') self.assertRaises(ParsingError, parseSAN, board, 'a8')
def test_gating_castle_at_rook_wOOO_SAN(self): FEN = "r2ek2r/5pp1/1pp1pnp1/2pp1h2/3P4/2P1PP2/PP1N2PP/R3K1HR/E w KQHEAkq - 2 16" board = LBoard(SCHESS) board.applyFen(FEN) self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O'))), 'e1c1') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ea1'))), 'a1e1e') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ee1'))), 'e1c1e') self.assertEqual(toSAN(board, newMove(E1, C1, QUEEN_CASTLE)), "O-O-O") self.assertEqual(toSAN(board, newMove(A1, E1, ELEPHANT_GATE_AT_ROOK)), "O-O-O/Ea1") self.assertEqual(toSAN(board, newMove(E1, C1, ELEPHANT_GATE)), "O-O-O/Ee1")
def feed_book(self, records, positions): for rec in records: model = GameModel() if rec["Result"] == DRAW: score = (1, 1) elif rec["Result"] == WHITEWON: score = (2, 0) elif rec["Result"] == BLACKWON: score = (0, 2) else: score = (0, 0) fenstr = rec["FEN"] variant = self.chessfile.get_variant(rec) if variant: model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as err: continue else: board.applyFen(FEN_START) boards = [board] movetext = self.chessfile.get_movetext(rec) boards = self.chessfile.parse_movetext(movetext, boards[0], -1) for board in boards: if board.plyCount > BOOK_DEPTH: break move = board.lastMove if move is not None: poly_move = toPolyglot(board.prev, move) # move_str = "%s%s" % (reprCord[FCORD(move)], reprCord[TCORD(move)]) # print("%0.16x" % board.prev.hash, poly_move, board.prev.asFen(), move_str) if board.prev.hash in positions: if poly_move in positions[board.prev.hash]: positions[board.prev.hash][poly_move] += score[board.prev.color] else: positions[board.prev.hash][poly_move] = score[board.prev.color] else: # board.prev.asFen(), move_str, positions[board.prev.hash] = {poly_move: score[board.prev.color]}
def test_promotion(self): # promotion moves FEN = "r4knr/1bpp1Pp1/pp3b2/q2pep1p/3N4/P1N5/1PPQBPPP/R1B1K2R[hHE] w KQ - 1 17" board = LBoard(SCHESS) board.applyFen(FEN) print("--------") print(board) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) self.assertIn("f7g8=H", moves) self.assertIn("f7g8=E", moves)
def testFEN(self): """Testing board-FEN conversion with several positions""" positions = [] with open('gamefiles/perftsuite.epd') as f: for line in f: semi = line.find(" ;") positions.append(line[:semi]) for i, fenstr in enumerate(positions[1:]): board = LBoard() board.applyFen(fenstr) fenstr2 = board.asFen() self.assertEqual(fenstr, fenstr2)
def testFRCCastling(self): """Testing FRC castling movegen""" print() for fen, castles in data: print(fen) board = LBoard(FISCHERRANDOMCHESS) board.applyFen(fen) # print board moves = [move for move in genCastles(board)] self.assertEqual(len(moves), len(castles)) for i, castle in enumerate(castles): kfrom, kto, flag = castle self.assertEqual(moves[i], newMove(kfrom, kto, flag))
def testFRCCastling(self): """Testing FRC castling movegen""" print() for fen, castles in data: print(fen) board = LBoard(FISCHERRANDOMCHESS) board.applyFen(fen) # print board moves = [move for move in genCastles(board)] self.assertEqual(len(moves), len(castles)) for i, castle in enumerate(castles): kfrom, kto, flag = castle self.assertEqual(moves[i], newMove(kfrom, kto, flag))
class EvalTestCase(unittest.TestCase): def setUp (self): self.board = LBoard(NORMALCHESS) self.board.applyFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1") def test1(self): """Testing eval symmetry with startboard (WHITE)""" score = evaluateComplete(self.board, color=WHITE) self.assertEqual(score, 0) def test2(self): """Testing eval symmetry with startboard (BLACK)""" score = evaluateComplete(self.board, color=BLACK) self.assertEqual(score, 0) def test3(self): """Testing eval symmetry of each function""" funcs = (f for f in dir(leval) if f.startswith("eval")) funcs = (getattr(leval,f) for f in funcs) funcs = (f for f in funcs if callable(f) \ and f != leval.evaluateComplete\ and f != leval.evalMaterial\ and f != leval.evalPawnStructure\ and f != leval.evalTrappedBishops) sw, phasew = leval.evalMaterial (self.board, WHITE) sb, phaseb = leval.evalMaterial (self.board, BLACK) self.assertEqual(phasew, phaseb) pawnScore, passed, weaked = leval.cacheablePawnInfo (self.board, phasew) sw = leval.evalPawnStructure (self.board, WHITE, phasew, passed, weaked) pawnScore, passed, weaked = leval.cacheablePawnInfo (self.board, phaseb) sb = leval.evalPawnStructure (self.board, BLACK, phaseb, passed, weaked) self.assertEqual(sw, sb) sw = leval.evalTrappedBishops (self.board, WHITE) sb = leval.evalTrappedBishops (self.board, BLACK) self.assertEqual(sw, sb) for func in funcs: sw = func(self.board, WHITE, phasew) sb = func(self.board, BLACK, phaseb) #print func, sw, sb self.assertEqual(sw, sb)
def movegen(self, positions, variant): for i, (fen, depths) in enumerate(positions): print(i + 1, "/", len(positions), "-", fen) board = LBoard(variant) board.applyFen(fen) hash = board.hash for depth, suposedMoveCount in depths: if depth > self.MAXDEPTH: break self.count = 0 print("searching depth %d for %d moves" % (depth, suposedMoveCount)) self.perft(board, depth, []) self.assertEqual(board.hash, hash) self.assertEqual(self.count, suposedMoveCount)
def movegen(self, positions, variant): for i, (fen, depths) in enumerate(positions): print(i + 1, "/", len(positions), "-", fen) board = LBoard(variant) board.applyFen(fen) hash = board.hash for depth, suposedMoveCount in depths: if depth > self.MAXDEPTH: break self.count = 0 print("searching depth %d for %d moves" % (depth, suposedMoveCount)) self.perft(board, depth, []) self.assertEqual(board.hash, hash) self.assertEqual(self.count, suposedMoveCount)
def test_apply_pop(self): """Testing Atomic applyMove popMove""" board = LBoard(variant=ATOMICCHESS) board.applyFen(FEN1) print(board) hist_exploding_around0 = [a[:] for a in board.hist_exploding_around] print_apply_pop = False for lmove1 in genAllMoves(board): board.applyMove(lmove1) if board.opIsChecked(): if print_apply_pop: print("popMove1 (invalid)", Move(lmove1)) board.popMove() continue hist_exploding_around1 = [a[:] for a in board.hist_exploding_around] for lmove2 in genAllMoves(board): board.applyMove(lmove2) if print_apply_pop: print(" applyMove2", Move(lmove2)) if board.opIsChecked(): if print_apply_pop: print(" popMove2 (invalid)", Move(lmove2)) board.popMove() continue hist_exploding_around2 = [a[:] for a in board.hist_exploding_around] for lmove3 in genAllMoves(board): board.applyMove(lmove3) if print_apply_pop: print(" applyMove3", Move(lmove3)) if board.opIsChecked(): if print_apply_pop: print(" popMove3 (invalid)", Move(lmove3)) board.popMove() continue board.popMove() if print_apply_pop: print(" popMove3", Move(lmove3)) self.assertEqual(hist_exploding_around2, board.hist_exploding_around) board.popMove() if print_apply_pop: print(" popMove2", Move(lmove2)) self.assertEqual(hist_exploding_around1, board.hist_exploding_around) board.popMove() if print_apply_pop: print("popMove1", Move(lmove1)) self.assertEqual(hist_exploding_around0, board.hist_exploding_around)
def create_fen(pieces): """ Create a random FEN position using given pieces """ pos = pieces.rfind("k") pieces = pieces[:pos], pieces[pos:] ok = False while not ok: lboard = LBoard() lboard.applyFen("8/8/8/8/8/8/8/8 w - - 0 1") bishop_cords = [[], []] bishop_colors_ok = True cords = list(range(0, 64)) pawn_cords = list(range(0 + 8, 64 - 8)) # Order of color is important here to prevent offering # positions with trivial captures in first move for color in (WHITE, BLACK): for char in pieces[color]: piece = chrU2Sign[char.upper()] attacked = True limit = 100 while attacked and limit > 0: cord = random.choice(pawn_cords if char == "p" else cords) attacked = isAttacked(lboard, cord, 1 - color) limit -= 1 lboard._addPiece(cord, piece, color) cords.remove(cord) if cord in pawn_cords: pawn_cords.remove(cord) if char == "b": bishop_cords[color].append(cord) # 2 same color bishop is not ok if len(bishop_cords[color]) == 2 and bishop_colors_ok: b0, b1 = bishop_cords[color] b0_color = BLACK if RANK(b0) % 2 == FILE(b0) % 2 else WHITE b1_color = BLACK if RANK(b1) % 2 == FILE(b1) % 2 else WHITE if b0_color == b1_color: bishop_colors_ok = False break ok = (not lboard.isChecked()) and ( not lboard.opIsChecked()) and bishop_colors_ok fen = lboard.asFen() return fen
def test_parseFAN(self): """Testing parseFAN""" board = LBoard() board.applyFen("rnbqkbnr/8/8/8/8/8/8/RNBQKBNR w KQkq - 0 1") for lmove in genAllMoves(board): board.applyMove(lmove) if board.opIsChecked(): board.popMove() continue board.popMove() fan = toFAN(board, lmove) self.assertEqual(parseFAN(board, fan), lmove)
def test_parseFAN(self): """Testing parseFAN""" board = LBoard() board.applyFen("rnbqkbnr/8/8/8/8/8/8/RNBQKBNR w KQkq - 0 1") for lmove in genAllMoves(board): board.applyMove(lmove) if board.opIsChecked(): board.popMove() continue board.popMove() fan = toFAN(board, lmove) self.assertEqual(parseFAN(board, fan), lmove)
def create_fen(pieces): """ Create a random FEN position using given pieces """ pos = pieces.rfind("k") pieces = pieces[:pos], pieces[pos:] ok = False while not ok: lboard = LBoard() lboard.applyFen("8/8/8/8/8/8/8/8 w - - 0 1") bishop_cords = [[], []] bishop_colors_ok = True cords = list(range(0, 64)) pawn_cords = list(range(0 + 8, 64 - 8)) # Order of color is important here to prevent offering # positions with trivial captures in first move for color in (WHITE, BLACK): for char in pieces[color]: piece = chrU2Sign[char.upper()] attacked = True limit = 100 while attacked and limit > 0: cord = random.choice(pawn_cords if char == "p" else cords) attacked = isAttacked(lboard, cord, 1 - color) limit -= 1 lboard._addPiece(cord, piece, color) cords.remove(cord) if cord in pawn_cords: pawn_cords.remove(cord) if char == "b": bishop_cords[color].append(cord) # 2 same color bishop is not ok if len(bishop_cords[color]) == 2 and bishop_colors_ok: b0, b1 = bishop_cords[color] b0_color = BLACK if RANK(b0) % 2 == FILE(b0) % 2 else WHITE b1_color = BLACK if RANK(b1) % 2 == FILE(b1) % 2 else WHITE if b0_color == b1_color: bishop_colors_ok = False break ok = (not lboard.isChecked()) and (not lboard.opIsChecked()) and bishop_colors_ok fen = lboard.asFen() return fen
def test_black_promotion(self): FEN = "k7/8/8/8/8/8/p7/7K b - - 0 1" board = LBoard(LIGHTBRIGADECHESS) board.applyFen(FEN) print("--------") print(board) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) self.assertIn("a2a1=N", moves) self.assertNotIn("a2a1", moves) self.assertNotIn("a2a1=R", moves) self.assertNotIn("a2a1=B", moves) self.assertNotIn("a2a1=Q", moves) self.assertNotIn("a2a1=K", moves)
def on_book_created(persp, event): self.assertTrue(os.path.isfile(BIN)) book.path = BIN book.bookfile = True testcase = testcases[0] board = LBoard(Board) board.applyFen(testcase[0]) openings = book.getOpenings(board) self.assertEqual(sorted(openings), sorted([(newMove(E2, E4), 2, 0), (newMove(A2, A4), 0, 0)])) testcase = testcases[-1] board = LBoard(Board) board.applyFen(testcase[0]) openings = book.getOpenings(board) self.assertEqual(openings, []) event.set()
def movegen(self, positions): for i, (fen, depths) in enumerate(positions): board = LBoard(FISCHERRANDOMCHESS) fen = fen.split() castl = fen[2] castl = castl.replace("K", "H") castl = castl.replace("Q", "A") castl = castl.replace("k", "h") castl = castl.replace("q", "a") fen[2] = castl fen = ' '.join(fen) print(i+1, "/", len(positions), "-", fen) board.applyFen(fen) for depth, suposedMoveCount in enumerate(depths): if depth+1 > self.MAXDEPTH: break self.count = 0 print("searching depth %d for %d moves" % \ (depth+1, suposedMoveCount)) self.perft (board, depth+1, []) self.assertEqual(self.count, suposedMoveCount)
class EvalTestCase(unittest.TestCase): def setUp (self): self.board = LBoard(NORMALCHESS) self.board.applyFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1") def test1(self): """Testing eval symmetry with startboard (WHITE)""" score = evaluateComplete(self.board, color=WHITE, balanced=True) self.assertEqual(score, 0) def test2(self): """Testing eval symmetry with startboard (BLACK)""" score = evaluateComplete(self.board, color=BLACK, balanced=True) self.assertEqual(score, 0) def test3(self): """Testing eval symmetry between colors with balanced=False""" scorew = evaluateComplete(self.board, color=WHITE) scoreb = evaluateComplete(self.board, color=BLACK) self.assertEqual(scorew, scoreb) def test4(self): """Testing eval symmetry of each function""" funcs = (f for f in dir(leval) if f.startswith("eval")) funcs = (getattr(leval,f) for f in funcs) funcs = (f for f in funcs if callable(f) and f != leval.evalMaterial) sw, phasew = leval.evalMaterial (self.board, WHITE) sb, phaseb = leval.evalMaterial (self.board, BLACK) self.assertEqual(phasew, phaseb) for func in funcs: sw = func(self.board, WHITE, phasew) sb = func(self.board, BLACK, phaseb) #print func, sw, sb self.assertEqual(sw, sb)
def loadToModel (self, gameno, position=-1, model=None): if not model: model = GameModel() # the seven mandatory PGN headers model.tags['Event'] = self._getTag(gameno, 'Event') model.tags['Site'] = self._getTag(gameno, 'Site') model.tags['Date'] = self._getTag(gameno, 'Date') model.tags['Round'] = self.get_round(gameno) model.tags['White'], model.tags['Black'] = self.get_player_names(gameno) model.tags['Result'] = reprResult[self.get_result(gameno)] pgnHasYearMonthDay = True for tag in ('Year', 'Month', 'Day'): if not self._getTag(gameno, tag): pgnHasYearMonthDay = False break if model.tags['Date'] and not pgnHasYearMonthDay: date_match = re.match(".*(\d{4}).(\d{2}).(\d{2}).*", model.tags['Date']) if date_match: year, month, day = date_match.groups() model.tags['Year'] = year model.tags['Month'] = month model.tags['Day'] = day # non-mandatory headers for tag in ('Annotator', 'ECO', 'EventDate', 'Time', 'WhiteElo', 'BlackElo', 'TimeControl'): if self._getTag(gameno, tag): model.tags[tag] = self._getTag(gameno, tag) else: model.tags[tag] = "" # TODO: enable this when NewGameDialog is altered to give user option of # whether to use PGN's clock time, or their own custom time. Also, # dialog should set+insensitize variant based on the variant of the # game selected in the dialog if model.tags['TimeControl']: secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timed = True model.timemodel.secs = secs model.timemodel.gain = gain model.timemodel.minutes = secs / 60 for tag, color in (('WhiteClock', WHITE), ('BlackClock', BLACK)): if self._getTag(gameno, tag): try: ms = parseClockTimeTag(self._getTag(gameno, tag)) model.timemodel.intervals[color][0] = ms / 1000 except ValueError: raise LoadingError( \ "Error parsing '%s' Header for gameno %s" % (tag, gameno)) fenstr = self._getTag(gameno, "FEN") variant = self.get_variant(gameno) if variant: model.tags["Variant"] = variant # Fixes for some non statndard Chess960 .pgn if (fenstr is not None) and variant == "Fischerandom": parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as e: board.applyFen(FEN_EMPTY) raise LoadingError(_("The game can't be loaded, because of an error parsing FEN"), e.args[0]) else: board.applyFen(FEN_START) boards = [board] del model.moves[:] del model.variations[:] self.error = None movetext = self.get_movetext(gameno) boards = self.parse_string(movetext, boards[0], position) # The parser built a tree of lboard objects, now we have to # create the high level Board and Move lists... for board in boards: if board.lastMove is not None: model.moves.append(Move(board.lastMove)) self.has_emt = False self.has_eval = False def walk(node, path): if node.prev is None: # initial game board board = model.variant(setup=node.asFen(), lboard=node) else: move = Move(node.lastMove) try: board = node.prev.pieceBoard.move(move, lboard=node) except: raise LoadingError(_("Invalid move."), "%s%s" % (move_count(node, black_periods=True), move)) if node.next is None: model.variations.append(path+[board]) else: walk(node.next, path+[board]) for child in node.children: if isinstance(child, list): if len(child) > 1: # non empty variation, go walk walk(child[1], list(path)) else: if not self.has_emt: self.has_emt = child.find("%emt") >= 0 if not self.has_eval: self.has_eval = child.find("%eval") >= 0 # Collect all variation paths into a list of board lists # where the first one will be the boards of mainline game. # model.boards will allways point to the current shown variation # which will be model.variations[0] when we are in the mainline. walk(boards[0], []) model.boards = model.variations[0] self.has_emt = self.has_emt and "TimeControl" in model.tags if self.has_emt or self.has_eval: if self.has_emt: blacks = len(model.moves)//2 whites = len(model.moves)-blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]]*(whites+1), [model.timemodel.intervals[1][0]]*(blacks+1), ] secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timemodel.intervals[0][0] = secs model.timemodel.intervals[1][0] = secs for ply, board in enumerate(boards): for child in board.children: if isinstance(child, basestring): if self.has_emt: match = movetime.search(child) if match: movecount, color = divmod(ply+1, 2) hour, minute, sec, msec = match.groups() prev = model.timemodel.intervals[color][movecount-1] msec = 0 if msec is None else int(msec) msec += int(sec)*1000 + int(minute)*60*1000 + int(hour)*60*60*1000 model.timemodel.intervals[color][movecount] = prev - msec/1000 if self.has_eval: match = moveeval.search(child) if match: sign, num, fraction, depth = match.groups() sign = 1 if sign is None or sign == "+" else -1 num = int(num) if int(num) == MATE_VALUE else int(num) fraction = 0 if fraction is None else float(fraction)/100 value = sign * (num + fraction) depth = "" if depth is None else depth model.scores[ply] = ("", value, depth) log.debug("pgn.loadToModel: intervals %s" % model.timemodel.intervals) # Find the physical status of the game model.status, model.reason = getStatus(model.boards[-1]) # Apply result from .pgn if the last position was loaded if position == -1 or len(model.moves) == position - model.lowply: status = self.get_result(gameno) if status in (WHITEWON, BLACKWON) and status != model.status: model.status = status model.reason = WON_RESIGN elif status == DRAW and status != model.status: model.status = DRAW model.reason = DRAW_AGREE # If parsing gave an error we throw it now, to enlarge our possibility # of being able to continue the game from where it failed. if self.error: raise self.error return model
class PyChessCECP(PyChess): def __init__(self): PyChess.__init__(self) self.board = LBoard(NORMALCHESS) self.board.applyFen(FEN_START) self.forced = False self.analyzing = False self.thread = None self.basetime = 0 self.features = { "ping": 1, "setboard": 1, "playother": 1, "san": 1, "usermove": 1, "time": 1, "draw": 1, "sigint": 0, "sigterm": 0, "reuse": 1, "analyze": 1, "myname": "PyChess %s" % pychess.VERSION, "variants": "normal,wildcastle,nocastle,fischerandom,crazyhouse," + "losers,suicide,giveaway,horde,atomic," + "kingofthehill,3check,asean,cambodian,makruk,sittuyin", "colors": 0, "ics": 0, "name": 0, "pause": 0, # Unimplemented "nps": 0, # Unimplemented "debug": 1, "memory": 0, # Unimplemented "smp": 0, # Unimplemented "egt": "gaviota", "option": "skipPruneChance -slider 0 0 100" } python = sys.executable.split("/")[-1] python_version = "%s.%s.%s" % sys.version_info[0:3] self.print("# %s [%s %s]" % (self.features["myname"], python, python_version)) def handle_sigterm(self, *args): self.__stopSearching() sys.exit(0) def makeReady(self): signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, self.handle_sigterm) def run(self): while True: try: line = get_input() except EOFError: line = "quit" lines = line.split() try: if not lines: continue log.debug(line, extra={"task": "xboard"}) # CECP commands # See http://home.hccnet.nl/h.g.muller/engine-intf.html if lines[0] == "xboard": pass elif lines[0] == "protover": stringPairs = ["=".join([k, '"%s"' % v if isinstance( v, str) else str(v)]) for k, v in self.features.items()] self.print("feature %s" % " ".join(stringPairs)) self.print("feature done=1") elif lines[0] in ("accepted", "rejected"): # We only really care about one case: if tuple(lines) == ("rejected", "debug"): self.debug = False elif lines[0] == "new": self.__stopSearching() self.board = LBoard(NORMALCHESS) self.board.applyFen(FEN_START) self.outOfBook = False self.forced = False self.playingAs = BLACK self.clock[:] = self.basetime, self.basetime self.searchtime = 0 self.sd = MAXPLY if self.analyzing: self.__analyze() elif lines[0] == "variant": if len(lines) > 1: if lines[1] == "fischerandom": self.board.variant = FISCHERRANDOMCHESS elif lines[1] == "crazyhouse": self.board.variant = CRAZYHOUSECHESS self.board.iniHouse() elif lines[1] == "wildcastle": self.board.variant = WILDCASTLESHUFFLECHESS elif lines[1] == "losers": self.board.variant = LOSERSCHESS elif lines[1] == "suicide": self.board.variant = SUICIDECHESS elif lines[1] == "giveaway": self.board.variant = GIVEAWAYCHESS elif lines[1] == "atomic": self.board.variant = ATOMICCHESS self.board.iniAtomic() elif lines[1] == "3check": self.board.variant = THREECHECKCHESS elif lines[1] == "kingofthehill": self.board.variant = KINGOFTHEHILLCHESS self.print("setup (PNBRQKpnbrqk) 8x8+0_fairy %s" % FEN_START) elif lines[1] == "horde": self.board = LBoard(HORDECHESS) self.board.applyFen(HORDESTART) elif lines[1] == "asean": self.board = LBoard(ASEANCHESS) self.board.applyFen(ASEANSTART) elif lines[1] == "makruk": self.board = LBoard(MAKRUKCHESS) self.board.applyFen(MAKRUKSTART) elif lines[1] == "cambodian": self.board = LBoard(CAMBODIANCHESS) self.board.applyFen(KAMBODIANSTART) self.print( "setup (PN.R.M....SKpn.r.m....sk) 8x8+0_makruk %s" % KAMBODIANSTART) self.print("piece K& KiN") self.print("piece M& FifD") elif lines[1] == "sittuyin": self.board = LBoard(SITTUYINCHESS) self.board.applyFen(SITTUYINSTART) self.print( "setup (PN.R.F....SKpn.r.f....sk) 8x8+6_bughouse %s" % SITTUYINSTART) self.print("piece N& Nj@3") self.print("piece S& FfWj@3") self.print("piece F& Fjb@3") self.print("piece R& R@1") self.print("piece K& Kj@3") self.print("piece P& fmWfcFj@3") elif lines[0] == "quit": self.forced = True self.__stopSearching() sys.exit(0) elif lines[0] == "random": leval.random = True elif lines[0] == "force": if not self.forced and not self.analyzing: self.forced = True self.__stopSearching() elif lines[0] == "go": self.playingAs = self.board.color self.forced = False self.__go() elif lines[0] == "playother": self.playingAs = 1 - self.board.color self.forced = False # TODO: start pondering, if possible elif lines[0] in ("black", "white"): newColor = lines[0] == "black" and BLACK or WHITE self.__stopSearching() self.playingAs = 1 - newColor if self.board.color != newColor: self.board.setColor(newColor) self.board.setEnpassant(None) if self.analyzing: self.__analyze() elif lines[0] == "level": self.movestogo = int(lines[1]) inc = int(lines[3]) minutes = lines[2].split(":") # Per protocol spec, strip off any non-numeric suffixes. for i in range(len(minutes)): minutes[i] = re.match(r'\d*', minutes[i]).group() self.basetime = int(minutes[0]) * 60 if len(minutes) > 1 and minutes[1]: self.basetime += int(minutes[1]) self.clock[:] = self.basetime, self.basetime self.increment = inc, inc elif lines[0] == "st": self.searchtime = float(lines[1]) elif lines[0] == "sd": self.sd = int(lines[1]) # Unimplemented: nps elif lines[0] == "time": self.clock[self.playingAs] = float(lines[1]) / 100. elif lines[0] == "otim": self.clock[1 - self.playingAs] = float(lines[1]) / 100. elif lines[0] == "usermove": self.__stopSearching() try: move = parseAny(self.board, lines[1]) except ParsingError as err: self.print("Error (unknown command): %s" % lines[1]) self.print(self.board.prepr(ascii=ASCII)) continue if not validateMove(self.board, move): self.print("Illegal move: %s" % lines[1]) self.print(self.board.prepr(ascii=ASCII)) continue self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() elif lines[0] == "?": if not self.forced and not self.analyzing: self.__stopSearching() elif lines[0] == "ping": self.print("pong %s" % lines[1]) elif lines[0] == "draw": if self.__willingToDraw(): self.print("offer draw") elif lines[0] == "result": # We don't really care what the result is at the moment. pass elif lines[0] == "setboard": self.__stopSearching() try: self.board = LBoard(self.board.variant) fen = " ".join(lines[1:]) self.board.applyFen(fen.replace("[", "/").replace("]", "")) except SyntaxError as err: self.print("tellusererror Illegal position: %s" % str(err)) # "edit" is unimplemented. See docs. Exiting edit mode returns to analyze mode. elif lines[0] == "hint": pass # TODO: Respond "Hint: MOVE" if we have an expected reply elif lines[0] == "bk": entries = getOpenings(self.board) if entries: totalWeight = sum(entry[1] for entry in entries) for entry in entries: self.print("\t%s\t%02.2f%%" % (toSAN(self.board, entry[0]), entry[1] * 100.0 / totalWeight)) elif lines[0] == "undo": self.__stopSearching() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] == "remove": self.__stopSearching() self.board.popMove() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] in ("hard", "easy"): self.ponder = (lines[0] == "hard") elif lines[0] in ("post", "nopost"): self.post = (lines[0] == "post") elif lines[0] == "analyze": self.analyzing = True self.__analyze() elif lines[0] in ("name", "rating", "ics", "computer"): pass # We don't care. # Unimplemented: pause, resume elif lines[0] == "memory": # FIXME: this is supposed to control the *total* memory use. if lsearch.searching: self.print("Error (already searching):", line) else: limit = int(lines[1]) if limit < 1: self.print("Error (limit too low):", line) else: pass # TODO implement # lsearch.setHashSize(limit) elif lines[0] == "cores": pass # We aren't SMP-capable. elif lines[0] == "egtpath": if len(lines) >= 3 and lines[1] == "gaviota": conf.set("egtb_path", conf.get("egtb_path", lines[2])) from pychess.Utils.lutils.lsearch import enableEGTB enableEGTB() elif lines[0] == "option" and len(lines) > 1: name, eq, value = lines[1].partition("=") if value: value = int( value ) # CECP spec says option values are *always* numeric if name == "skipPruneChance": if 0 <= value <= 100: self.skipPruneChance = value / 100.0 else: self.print( "Error (argument must be an integer 0..100): %s" % line) # CECP analyze mode commands # See http://www.gnu.org/software/xboard/engine-intf.html#11 elif lines[0] == "exit": if self.analyzing: self.__stopSearching() self.analyzing = False # Periodic updates (".") are not implemented. # Custom commands elif lines[0] == "moves": self.print(self.board.prepr(ascii=ASCII)) self.print([toSAN(self.board, move) for move in genAllMoves(self.board)]) elif lines[0] == "captures": self.print(self.board.prepr(ascii=ASCII)) self.print([toSAN(self.board, move) for move in genCaptures(self.board)]) elif lines[0] == "evasions": self.print(self.board.prepr(ascii=ASCII)) self.print([toSAN(self.board, move) for move in genCheckEvasions(self.board)]) elif lines[0] == "benchmark": if len(lines) > 1: benchmark(int(lines[1])) else: benchmark() elif lines[0] == "profile": if len(lines) > 1: import cProfile cProfile.runctx("benchmark()", locals(), globals(), lines[1]) else: self.print("Usage: profile outputfilename") elif lines[0] == "perft": root = "0" if len(lines) < 3 else lines[2] depth = "1" if len(lines) == 1 else lines[1] if root.isdigit() and depth.isdigit(): perft(self.board, int(depth), int(root)) else: self.print("Error (arguments must be integer") elif lines[0] == "stop_unittest": break elif len(lines) == 1: # A GUI without usermove support might try to send a move. try: move = parseAny(self.board, line) except ParsingError: self.print("Error (unknown command): %s" % line) continue if not validateMove(self.board, move): self.print("Illegal move: %s" % lines[0]) self.print(self.board.prepr(ascii=ASCII)) continue self.__stopSearching() self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() else: self.print("Error (unknown command): %s" % line) except IndexError: self.print("Error (missing argument): %s" % line) def __stopSearching(self): lsearch.searching = False if self.thread: self.thread.join() def __go(self): def ondone(result): if not self.forced: self.board.applyMove(parseSAN(self.board, result)) self.print("move %s" % result) # TODO: start pondering, if enabled self.thread = Thread(target=PyChess._PyChess__go, name=fident(PyChess._PyChess__go), args=(self, ondone)) self.thread.daemon = True self.thread.start() def __analyze(self): self.thread = Thread(target=PyChess._PyChess__analyze, name=fident(PyChess._PyChess__analyze), args=(self, )) self.thread.daemon = True self.thread.start() def __willingToDraw(self): return self.scr <= 0 # FIXME: this misbehaves in all but the simplest use cases
class PyChessFICS(PyChess): def __init__ (self, password, from_address, to_address): PyChess.__init__(self) self.ports = (23, 5000) if not password: self.username = "******" else: self.username = "******" self.owner = "Lobais" self.password = password self.from_address = "The PyChess Bot <%s>" % from_address self.to_address = "Thomas Dybdahl Ahle <%s>" % to_address # Possible start times self.minutes = (1,2,3,4,5,6,7,8,9,10) self.gains = (0,5,10,15,20) # Possible colors. None == random self.colors = (WHITE, BLACK, None) # The amount of random challenges, that PyChess sends with each seek self.challenges = 10 enableEGTB() self.sudos = set() self.ownerOnline = False self.waitingForPassword = None self.log = [] self.acceptedTimesettings = [] self.worker = None repeat_sleep(self.sendChallenges, 60*1) def __triangular(self, low, high, mode): """Triangular distribution. Continuous distribution bounded by given lower and upper limits, and having a given mode value in-between. http://en.wikipedia.org/wiki/Triangular_distribution """ u = random.random() c = (mode - low) / (high - low) if u > c: u = 1 - u c = 1 - c low, high = high, low tri = low + (high - low) * (u * c) ** 0.5 if tri < mode: return int(tri) elif tri > mode: return int(math.ceil(tri)) return int(round(tri)) def sendChallenges(self): if self.connection.bm.isPlaying(): return True statsbased = ((0.39197722779282, 3, 0), (0.59341408108783, 5, 0), (0.77320877377846, 1, 0), (0.8246379941394, 10, 0), (0.87388717406441, 2, 12), (0.91443760169489, 15, 0), (0.9286423058163, 4, 0), (0.93891977227793, 2, 0), (0.94674539138335, 20, 0), (0.95321476842423, 2, 2), (0.9594588808257, 5, 2), (0.96564528079889, 3, 2), (0.97173859621034, 7, 0), (0.97774906636184, 3, 1), (0.98357243654425, 5, 12), (0.98881309737017, 5, 5), (0.99319644938247, 6, 0), (0.99675879556023, 3, 12), (1, 5, 3)) #n = random.random() #for culminativeChance, minute, gain in statsbased: # if n < culminativeChance: # break culminativeChance, minute, gain = random.choice(statsbased) #type = random.choice((TYPE_LIGHTNING, TYPE_BLITZ, TYPE_STANDARD)) #if type == TYPE_LIGHTNING: # minute = self.__triangular(0,2+1,1) # mingain = not minute and 1 or 0 # maxgain = int((3-minute)*3/2) # gain = random.randint(mingain, maxgain) #elif type == TYPE_BLITZ: # minute = self.__triangular(0,14+1,5) # mingain = max(int((3-minute)*3/2+1), 0) # maxgain = int((15-minute)*3/2) # gain = random.randint(mingain, maxgain) #elif type == TYPE_STANDARD: # minute = self.__triangular(0,20+1,12) # mingain = max(int((15-minute)*3/2+1), 0) # maxgain = int((20-minute)*3/2) # gain = self.__triangular(mingain, maxgain, mingain) #color = random.choice(self.colors) self.extendlog(["Seeking %d %d" % (minute, gain)]) self.connection.glm.seek(minute, gain, True) opps = random.sample(self.connection.players.get_online_playernames(), self.challenges) self.extendlog("Challenging %s" % op for op in opps) for player in opps: self.connection.om.challenge(player, minute, gain, True) return True def makeReady(self): signal.signal(signal.SIGINT, Gtk.main_quit) PyChess.makeReady(self) self.connection = FICSMainConnection("freechess.org", self.ports, self.username, self.password) self.connection.connect("connectingMsg", self.__showConnectLog) self.connection._connect() self.connection.glm.connect("addPlayer", self.__onAddPlayer) self.connection.glm.connect("removePlayer", self.__onRemovePlayer) self.connection.cm.connect("privateMessage", self.__onTell) self.connection.alm.connect("logOut", self.__onLogOut) self.connection.bm.connect("playGameCreated", self.__onGameCreated) self.connection.bm.connect("curGameEnded", self.__onGameEnded) self.connection.bm.connect("boardUpdate", self.__onBoardUpdate) self.connection.om.connect("onChallengeAdd", self.__onChallengeAdd) self.connection.om.connect("onOfferAdd", self.__onOfferAdd) self.connection.adm.connect("onAdjournmentsList", self.__onAdjournmentsList) self.connection.em.connect("onAmbiguousMove", self.__onAmbiguousMove) self.connection.em.connect("onIllegalMove", self.__onAmbiguousMove) self.connection.adm.queryAdjournments() self.connection.lvm.setVariable("autoflag", 1) self.connection.fm.setFingerNote(1, "PyChess is the chess engine bundled with the PyChess %s " % pychess.VERSION + "chess client. This instance is owned by %s, but acts " % self.owner + "quite autonomously.") self.connection.fm.setFingerNote(2, "PyChess is 100% Python code and is released under the terms of " + "the GPL. The evalution function is largely equal to the one of" + "GnuChess, but it plays quite differently.") self.connection.fm.setFingerNote(3, "PyChess runs on an elderly AMD Sempron(tm) Processor 3200+, 512 " + "MB DDR2 Ram, but is built to take use of 64bit calculating when " + "accessible, through the gpm library.") self.connection.fm.setFingerNote(4, "PyChess uses a small 500 KB openingbook based solely on Kasparov " + "games. The engine doesn't have much endgame knowledge, but might " + "in some cases access an online endgamedatabase.") self.connection.fm.setFingerNote(5, "PyChess will allow any pause/resume and adjourn wishes, but will " + "deny takebacks. Draw, abort and switch offers are accepted, " + "if they are found to be an advance. Flag is auto called, but " + "PyChess never resigns. We don't want you to forget your basic " + "mating skills.") def main(self): self.connection.run() self.extendlog([str(self.acceptedTimesettings)]) self.phoneHome("Session ended\n"+"\n".join(self.log)) print("Session ended") def run(self): t = Thread(target=self.main, name=fident(self.main)) t.daemon = True t.start() Gdk.threads_init() Gtk.main() #=========================================================================== # General #=========================================================================== def __showConnectLog (self, connection, message): print(message) def __onLogOut (self, autoLogoutManager): self.connection.close() #sys.exit() def __onAddPlayer (self, gameListManager, player): if player["name"] in self.sudos: self.sudos.remove(player["name"]) if player["name"] == self.owner: self.connection.cm.tellPlayer(self.owner, "Greetings") self.ownerOnline = True def __onRemovePlayer (self, gameListManager, playername): if playername == self.owner: self.ownerOnline = False def __onAdjournmentsList (self, adjournManager, adjournments): for adjournment in adjournments: if adjournment["online"]: adjournManager.challenge(adjournment["opponent"]) def __usage (self): return "|| PyChess bot help file || " +\ "# help 'Displays this help file' " +\ "# sudo <password> <command> 'Lets PyChess execute the given command' "+\ "# sendlog 'Makes PyChess send you its current log'" def __onTell (self, chatManager, name, title, isadmin, text): if self.waitingForPassword: if text.strip() == self.password or (not self.password and text == "none"): self.sudos.add(name) self.tellHome("%s gained sudo access" % name) self.connection.client.run_command(self.waitingForPassword) else: chatManager.tellPlayer(name, "Wrong password") self.tellHome("%s failed sudo access" % name) self.waitingForPassword = None return args = text.split() #if args == ["help"]: # chatManager.tellPlayer(name, self.__usage()) if args[0] == "sudo": command = " ".join(args[1:]) if name in self.sudos or name == self.owner: # Notice: This can be used to make nasty loops print(command, file=self.connection.client) else: print(repr(name), self.sudos) chatManager.tellPlayer(name, "Please send me the password") self.waitingForPassword = command elif args == ["sendlog"]: if self.log: # TODO: Consider email chatManager.tellPlayer(name, "\\n".join(self.log)) else: chatManager.tellPlayer(name, "The log is currently empty") else: if self.ownerOnline: self.tellHome("%s told me '%s'" % (name, text)) else: def onlineanswer (message): data = urlopen("http://www.pandorabots.com/pandora/talk?botid=8d034368fe360895", urlencode({"message":message, "botcust2":"x"}).encode("utf-8")).read().decode('utf-8') ss = "<b>DMPGirl:</b>" es = "<br>" answer = data[data.find(ss)+len(ss) : data.find(es,data.find(ss))] chatManager.tellPlayer(name, answer) t = Thread(target=onlineanswer, name=fident(onlineanswer), args=(text,)) t.daemon = True t.start() #chatManager.tellPlayer(name, "Sorry, your request was nonsense.\n"+\ # "Please read my help file for more info") #=========================================================================== # Challenges and other offers #=========================================================================== def __onChallengeAdd (self, offerManager, index, match): #match = {"tp": type, "w": fname, "rt": rating, "color": color, # "r": rated, "t": mins, "i": incr} offerManager.acceptIndex(index) def __onOfferAdd (self, offerManager, offer): if offer.type in (PAUSE_OFFER, RESUME_OFFER, ADJOURN_OFFER): offerManager.accept(offer) elif offer.type in (TAKEBACK_OFFER,): offerManager.decline(offer) elif offer.type in (DRAW_OFFER, ABORT_OFFER, SWITCH_OFFER): if self.__willingToDraw(): offerManager.accept(offer) else: offerManager.decline(offer) #=========================================================================== # Playing #=========================================================================== def __onGameCreated (self, boardManager, ficsgame): base = int(ficsgame.minutes)*60 inc = int(ficsgame.inc) self.clock[:] = base, base self.increment[:] = inc, inc self.gameno = ficsgame.gameno self.lastPly = -1 self.acceptedTimesettings.append((base, inc)) self.tellHome("Starting a game (%s, %s) gameno: %s" % (ficsgame.wplayer.name, ficsgame.bplayer.name, ficsgame.gameno)) if ficsgame.bplayer.name.lower() == self.connection.getUsername().lower(): self.playingAs = BLACK else: self.playingAs = WHITE self.board = LBoard(NORMALCHESS) # Now we wait until we recieve the board. def __go (self): if self.worker: self.worker.cancel() self.worker = GtkWorker(lambda worker: PyChess._PyChess__go(self, worker)) self.worker.connect("published", lambda w, msg: self.extendlog(msg)) self.worker.connect("done", self.__onMoveCalculated) self.worker.execute() def __willingToDraw (self): return self.scr <= 0 # FIXME: this misbehaves in all but the simplest use cases def __onGameEnded (self, boardManager, ficsgame): self.tellHome(reprResult_long[ficsgame.result] + " " + reprReason_long[ficsgame.reason]) lsearch.searching = False if self.worker: self.worker.cancel() self.worker = None def __onMoveCalculated (self, worker, sanmove): if worker.isCancelled() or not sanmove: return self.board.applyMove(parseSAN(self.board,sanmove)) self.connection.bm.sendMove(sanmove) self.extendlog(["Move sent %s" % sanmove]) def __onBoardUpdate (self, boardManager, gameno, ply, curcol, lastmove, fen, wname, bname, wms, bms): self.extendlog(["","I got move %d %s for gameno %s" % (ply, lastmove, gameno)]) if self.gameno != gameno: return self.board.applyFen(fen) self.clock[:] = wms/1000., bms/1000. if curcol == self.playingAs: self.__go() def __onAmbiguousMove (self, errorManager, move): # This is really a fix for fics, but sometimes it is necessary if determineAlgebraicNotation(move) == SAN: self.board.popMove() move_ = parseSAN(self.board, move) lanmove = toLAN(self.board, move_) self.board.applyMove(move_) self.connection.bm.sendMove(lanmove) else: self.connection.cm.tellOpponent( "I'm sorry, I wanted to move %s, but FICS called " % move + "it 'Ambigious'. I can't find another way to express it, " + "so you can win") self.connection.bm.resign() #=========================================================================== # Utils #=========================================================================== def extendlog(self, messages): [log.info(m+"\n") for m in messages] self.log.extend(messages) del self.log[:-10] def tellHome(self, message): print(message) if self.ownerOnline: self.connection.cm.tellPlayer(self.owner, message) def phoneHome(self, message): SENDMAIL = '/usr/sbin/sendmail' SUBJECT = "Besked fra botten" p = subprocess.Popen([SENDMAIL, '-f', email.Utils.parseaddr(self.from_address)[1], email.Utils.parseaddr(self.to_address)[1]], stdin=subprocess.PIPE) print("MIME-Version: 1.0", file=p.stdin) print("Content-Type: text/plain; charset=UTF-8", file=p.stdin) print("Content-Disposition: inline", file=p.stdin) print("From: %s" % self.from_address, file=p.stdin) print("To: %s" % self.to_address, file=p.stdin) print("Subject: %s" % SUBJECT, file=p.stdin) print(file=p.stdin) print(message, file=p.stdin) print("Cheers", file=p.stdin) p.stdin.close() p.wait()
def do_import(self, filename): print(filename) # collect new names not in they dict yet self.collection_data = [] self.event_data = [] self.site_data = [] self.player_data = [] self.annotator_data = [] # collect new games and commit them in big chunks for speed self.game_data = [] if filename.lower().endswith(".zip") and zipfile.is_zipfile(filename): zf = zipfile.ZipFile(filename, "r") files = [f for f in zf.namelist() if f.lower().endswith(".pgn")] else: zf = None files = [filename] for pgnfile in files: if zf is None: cf = pgn_load(open(pgnfile, "rU")) else: cf = pgn_load(zf.open(pgnfile, "rU")) # use transaction to avoid autocommit slowness trans = self.conn.begin() try: for i, game in enumerate(cf.games): #print i+1#, cf.get_player_names(i) movelist = array("H") comments = [] cf.error = None fenstr = cf._getTag(i, "FEN") variant = cf.get_variant(i) # Fixes for some non statndard Chess960 .pgn if variant==0 and (fenstr is not None) and "Chess960" in cf._getTag(i,"Event"): cf.tagcache[i]["Variant"] = "Fischerandom" variant = 1 parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) if variant: board = LBoard(FISCHERRANDOMCHESS) else: board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as e: print(_("The game #%s can't be loaded, because of an error parsing FEN") % (i+1), e.args[0]) continue else: board = LBoard_FEN_START.clone() boards = [board] movetext = cf.get_movetext(i) boards = cf.parse_string(movetext, boards[0], -1) if cf.error is not None: print("ERROR in game #%s" % (i+1), cf.error.args[0]) continue walk(boards[0], movelist, comments) if not movelist: if (not comments) and (cf._getTag(i, 'White') is None) and (cf._getTag(i, 'Black') is None): print("empty game") continue event_id = self.get_id(cf._getTag(i, 'Event'), event, EVENT) site_id = self.get_id(cf._getTag(i, 'Site'), site, SITE) game_date = cf._getTag(i, 'Date') if game_date and not '?' in game_date: ymd = game_date.split('.') if len(ymd) == 3: game_year, game_month, game_day = map(int, ymd) else: game_year, game_month, game_day = int(game_date[:4]), None, None elif game_date and not '?' in game_date[:4]: game_year, game_month, game_day = int(game_date[:4]), None, None else: game_year, game_month, game_day = None, None, None game_round = cf._getTag(i, 'Round') white, black = cf.get_player_names(i) white_id = self.get_id(white, player, PLAYER) black_id = self.get_id(black, player, PLAYER) result = cf.get_result(i) white_elo = cf._getTag(i, 'WhiteElo') white_elo = int(white_elo) if white_elo and white_elo.isdigit() else None black_elo = cf._getTag(i, 'BlackElo') black_elo = int(black_elo) if black_elo and black_elo.isdigit() else None ply_count = cf._getTag(i, "PlyCount") event_date = cf._getTag(i, 'EventDate') eco = cf._getTag(i, "ECO") eco = eco[:3] if eco else None fen = cf._getTag(i, "FEN") variant = cf.get_variant(i) board = cf._getTag(i, "Board") annotator = cf._getTag(i, "Annotator") annotator_id = self.get_id(annotator, annotator, ANNOTATOR) collection_id = self.get_id(unicode(pgnfile), collection, COLLECTION) self.game_data.append({ 'event_id': event_id, 'site_id': site_id, 'date_year': game_year, 'date_month': game_month, 'date_day': game_day, 'round': game_round, 'white_id': white_id, 'black_id': black_id, 'result': result, 'white_elo': white_elo, 'black_elo': black_elo, 'ply_count': ply_count, 'eco': eco, 'fen': fen, 'variant': variant, 'board': board, 'annotator_id': annotator_id, 'collection_id': collection_id, 'movelist': movelist.tostring(), 'comments': unicode("|".join(comments)), }) if len(self.game_data) >= CHUNK: if self.collection_data: self.conn.execute(self.ins_collection, self.collection_data) self.collection_data = [] if self.event_data: self.conn.execute(self.ins_event, self.event_data) self.event_data = [] if self.site_data: self.conn.execute(self.ins_site, self.site_data) self.site_data = [] if self.player_data: self.conn.execute(self.ins_player, self.player_data) self.player_data = [] if self.annotator_data: self.conn.execute(self.ins_annotator, self.annotator_data) self.annotator_data = [] self.conn.execute(self.ins_game, self.game_data) self.game_data = [] print(pgnfile, i+1) if self.collection_data: self.conn.execute(self.ins_collection, self.collection_data) self.collection_data = [] if self.event_data: self.conn.execute(self.ins_event, self.event_data) self.event_data = [] if self.site_data: self.conn.execute(self.ins_site, self.site_data) self.site_data = [] if self.player_data: self.conn.execute(self.ins_player, self.player_data) self.player_data = [] if self.annotator_data: self.conn.execute(self.ins_annotator, self.annotator_data) self.annotator_data = [] if self.game_data: self.conn.execute(self.ins_game, self.game_data) self.game_data = [] print(pgnfile, i+1) trans.commit() except ProgrammingError as e: trans.rollback() print("Importing %s failed! %s" % (file, e))
def loadToModel(self, rec, position=-1, model=None): """ Parse game text and load game record header tags to a GameModel object """ if not model: model = GameModel() if self.pgn_is_string: rec = self.games[0] game_date = rec["Date"] result = rec["Result"] variant = rec["Variant"] else: game_date = self.get_date(rec) result = reprResult[rec["Result"]] variant = self.get_variant(rec) # the seven mandatory PGN headers model.tags['Event'] = rec["Event"] model.tags['Site'] = rec["Site"] model.tags['Date'] = game_date model.tags['Round'] = rec["Round"] model.tags['White'] = rec["White"] model.tags['Black'] = rec["Black"] model.tags['Result'] = result if model.tags['Date']: date_match = re.match(".*(\d{4}).(\d{2}).(\d{2}).*", model.tags['Date']) if date_match: year, month, day = date_match.groups() model.tags['Year'] = year model.tags['Month'] = month model.tags['Day'] = day # non-mandatory tags for tag in ('Annotator', 'ECO', 'WhiteElo', 'BlackElo', 'TimeControl'): value = rec[tag] if value: model.tags[tag] = value else: model.tags[tag] = "" if not self.pgn_is_string: model.info = self.tag_database.get_info(rec) if model.tags['TimeControl']: secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timed = True model.timemodel.secs = secs model.timemodel.gain = gain model.timemodel.minutes = secs / 60 for tag, color in (('WhiteClock', WHITE), ('BlackClock', BLACK)): if hasattr(rec, tag): try: millisec = parseClockTimeTag(rec[tag]) # We need to fix when FICS reports negative clock time like this # [TimeControl "180+0"] # [WhiteClock "0:00:15.867"] # [BlackClock "23:59:58.820"] start_sec = ( millisec - 24 * 60 * 60 * 1000 ) / 1000. if millisec > 23 * 60 * 60 * 1000 else millisec / 1000. model.timemodel.intervals[color][0] = start_sec except ValueError: raise LoadingError( "Error parsing '%s'" % tag) fenstr = rec["FEN"] if variant: if variant not in name2variant: raise LoadingError("Unknown variant %s" % variant) model.tags["Variant"] = variant # Fixes for some non statndard Chess960 .pgn if (fenstr is not None) and variant == "Fischerandom": parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as err: board.applyFen(FEN_EMPTY) raise LoadingError( _("The game can't be loaded, because of an error parsing FEN"), err.args[0]) else: board.applyFen(FEN_START) boards = [board] del model.moves[:] del model.variations[:] self.error = None movetext = self.get_movetext(rec) boards = self.parse_movetext(movetext, boards[0], position) # The parser built a tree of lboard objects, now we have to # create the high level Board and Move lists... for board in boards: if board.lastMove is not None: model.moves.append(Move(board.lastMove)) self.has_emt = False self.has_eval = False def walk(model, node, path): if node.prev is None: # initial game board board = model.variant(setup=node.asFen(), lboard=node) else: move = Move(node.lastMove) try: board = node.prev.pieceBoard.move(move, lboard=node) except: raise LoadingError( _("Invalid move."), "%s%s" % (move_count(node, black_periods=True), move)) if node.next is None: model.variations.append(path + [board]) else: walk(model, node.next, path + [board]) for child in node.children: if isinstance(child, list): if len(child) > 1: # non empty variation, go walk walk(model, child[1], list(path)) else: if not self.has_emt: self.has_emt = child.find("%emt") >= 0 if not self.has_eval: self.has_eval = child.find("%eval") >= 0 # Collect all variation paths into a list of board lists # where the first one will be the boards of mainline game. # model.boards will allways point to the current shown variation # which will be model.variations[0] when we are in the mainline. walk(model, boards[0], []) model.boards = model.variations[0] self.has_emt = self.has_emt and "TimeControl" in model.tags if self.has_emt or self.has_eval: if self.has_emt: blacks = len(model.moves) // 2 whites = len(model.moves) - blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]] * (whites + 1), [model.timemodel.intervals[1][0]] * (blacks + 1), ] secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timemodel.intervals[0][0] = secs model.timemodel.intervals[1][0] = secs for ply, board in enumerate(boards): for child in board.children: if isinstance(child, str): if self.has_emt: match = movetime.search(child) if match: movecount, color = divmod(ply + 1, 2) hour, minute, sec, msec = match.groups() prev = model.timemodel.intervals[color][ movecount - 1] hour = 0 if hour is None else int(hour[:-1]) minute = 0 if minute is None else int(minute[:-1]) msec = 0 if msec is None else int(msec) msec += int(sec) * 1000 + int( minute) * 60 * 1000 + int( hour) * 60 * 60 * 1000 model.timemodel.intervals[color][ movecount] = prev - msec / 1000. + gain if self.has_eval: match = moveeval.search(child) if match: sign, num, fraction, depth = match.groups() sign = 1 if sign is None or sign == "+" else -1 num = int(num) if int( num) == MATE_VALUE else int(num) fraction = 0 if fraction is None else int( fraction) value = sign * (num * 100 + fraction) depth = "" if depth is None else depth if board.color == BLACK: value = -value model.scores[ply] = ("", value, depth) log.debug("pgn.loadToModel: intervals %s" % model.timemodel.intervals) # Find the physical status of the game model.status, model.reason = getStatus(model.boards[-1]) # Apply result from .pgn if the last position was loaded if position == -1 or len(model.moves) == position - model.lowply: status = rec["Result"] if status in (WHITEWON, BLACKWON) and status != model.status: model.status = status model.reason = WON_RESIGN elif status == DRAW and status != model.status: model.status = DRAW model.reason = DRAW_AGREE # If parsing gave an error we throw it now, to enlarge our possibility # of being able to continue the game from where it failed. if self.error: raise self.error return model
class ZobristTestCase(unittest.TestCase): def make_move(self, an_move): self.board.applyMove(parseAN(self.board, an_move)) def setUp(self): self.board = LBoard(Board) self.board.applyFen(FEN) def testZobrist_1(self): """Testing zobrist hashing with simple move and take back""" hash = self.board.hash self.make_move("c3b5") self.board.color = 1 - self.board.color self.make_move("b5c3") self.assertEqual(hash, self.board.hash) def testZobrist_2(self): """Testing zobrist hashing with W00,B00,a1b1 vs. a1b1,B00,W00""" self.make_move("e1g1") self.make_move("e8g8") self.make_move("a1b1") hash1 = self.board.hash self.board.popMove() self.board.popMove() self.board.popMove() self.make_move("a1b1") self.make_move("e8g8") self.make_move("e1g1") hash2 = self.board.hash self.assertEqual(hash1, hash2) def testZobrist_3(self): """Testing zobrist hashing with W000,B000,h1g1 vs. h1g1,B000,W000""" self.make_move("e1c1") self.make_move("e8c8") self.make_move("h1g1") hash1 = self.board.hash self.board.popMove() self.board.popMove() self.board.popMove() self.make_move("h1g1") self.make_move("e8c8") self.make_move("e1c1") hash2 = self.board.hash self.assertEqual(hash1, hash2) def testZobrist_4(self): """Testing zobrist hashing with en-passant""" self.make_move("a2a4") self.make_move("b4a3") self.make_move("e1c1") self.make_move("c7c5") self.make_move("d5c6") self.make_move("e8c8") hash1 = self.board.hash self.board.popMove() self.board.popMove() self.board.popMove() self.board.popMove() self.board.popMove() self.board.popMove() self.make_move("e1c1") self.make_move("c7c5") self.make_move("d5c6") self.make_move("e8c8") self.make_move("a2a4") self.make_move("b4a3") hash2 = self.board.hash self.assertEqual(hash1, hash2)
site, player, game, annotator, ini_collection CHUNK = 1000 EVENT, SITE, PLAYER, ANNOTATOR, COLLECTION = range(5) removeDic = { ord(unicode("'")): None, ord(unicode(",")): None, ord(unicode(".")): None, ord(unicode("-")): None, ord(unicode(" ")): None, } LBoard_FEN_START = LBoard() LBoard_FEN_START.applyFen(FEN_START) class PgnImport(): def __init__(self): self.conn = engine.connect() self.ins_collection = collection.insert() self.ins_event = event.insert() self.ins_site = site.insert() self.ins_player = player.insert() self.ins_annotator = annotator.insert() self.ins_game = game.insert() self.collection_dict = {} self.event_dict = {} self.site_dict = {}
class PyChessCECP(PyChess): def __init__ (self): PyChess.__init__(self) self.board = LBoard(NORMALCHESS) self.board.applyFen(FEN_START) self.forced = False self.analyzing = False self.thread = None self.basetime = 0 self.features = { "ping": 1, "setboard": 1, "playother": 1, "san": 1, "usermove": 1, "time": 1, "draw": 1, "sigint": 0, "sigterm": 0, "reuse": 1, "analyze": 1, "myname": "PyChess %s" % pychess.VERSION, "variants": "normal,wildcastle,nocastle,fischerandom,crazyhouse,losers,suicide,atomic", "colors": 0, "ics": 0, "name": 0, "pause": 0, # Unimplemented "nps": 0, # Unimplemented "debug": 1, "memory": 1, "smp": 0, # Unimplemented # "egt": "gaviota", # TODO: re-enable "option": "skipPruneChance -slider 0 0 100" } def handle_sigterm(self, *args): self.__stopSearching() sys.exit(0) def makeReady(self): PyChess.makeReady(self) signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, self.handle_sigterm) def run (self): while True: try: line = raw_input() except EOFError: line = "quit" lines = line.split() if 1: #try: if not lines: continue ########## CECP commands ########## # See http://www.gnu.org/software/xboard/engine-intf.html#8 elif lines[0] == "xboard": pass elif lines[0] == "protover": stringPairs = ["=".join([k, '"%s"' % v if type(v) is str else str(v)]) for k,v in self.features.iteritems()] print "feature %s" % " ".join(stringPairs) print "feature done=1" elif lines[0] in ("accepted", "rejected"): # We only really care about one case: if tuple(lines) == ("rejected", "debug"): self.debug = False elif lines[0] == "new": self.__stopSearching() self.board = LBoard(NORMALCHESS) self.board.applyFen(FEN_START) self.forced = False self.playingAs = BLACK self.clock[:] = self.basetime, self.basetime self.searchtime = 0 self.sd = MAXPLY if self.analyzing: self.__analyze() elif lines[0] == "variant": if len(lines) > 1: if lines[1] == "fischerandom": self.board.variant = FISCHERRANDOMCHESS elif lines[1] == "crazyhouse": self.board.variant = CRAZYHOUSECHESS self.board.iniHouse() elif lines[1] == "wildcastle": self.board.variant = WILDCASTLESHUFFLECHESS elif lines[1] == "losers": self.board.variant = LOSERSCHESS elif lines[1] == "suicide": self.board.variant = SUICIDECHESS elif lines[1] == "atomic": self.board.variant = ATOMICCHESS self.board.iniAtomic() elif lines[0] == "quit": self.forced = True self.__stopSearching() sys.exit(0) elif lines[0] == "random": leval.random = True elif lines[0] == "force": if not self.forced and not self.analyzing: self.forced = True self.__stopSearching() elif lines[0] == "go": self.playingAs = self.board.color self.forced = False self.__go() elif lines[0] == "playother": self.playingAs = 1-self.board.color self.forced = False # TODO: start pondering, if possible elif lines[0] in ("black", "white"): newColor = lines[0] == "black" and BLACK or WHITE self.__stopSearching() self.playingAs = 1-newColor if self.board.color != newColor: self.board.setColor(newColor) self.board.setEnpassant(None) if self.analyzing: self.__analyze() elif lines[0] == "level": self.movestogo = int(lines[1]) inc = int(lines[3]) minutes = lines[2].split(":") # Per protocol spec, strip off any non-numeric suffixes. for i in xrange(len(minutes)): minutes[i] = re.match(r'\d*', minutes[i]).group() self.basetime = int(minutes[0])*60 if len(minutes) > 1 and minutes[1]: self.basetime += int(minutes[1]) self.clock[:] = self.basetime, self.basetime self.increment = inc, inc elif lines[0] == "st": self.searchtime = float(lines[1]) elif lines[0] == "sd": self.sd = int(lines[1]) # Unimplemented: nps elif lines[0] == "time": self.clock[self.playingAs] = float(lines[1])/100. elif lines[0] == "otim": self.clock[1-self.playingAs] = float(lines[1])/100. elif lines[0] == "usermove": self.__stopSearching() try: move = parseAny (self.board, lines[1]) except ParsingError, e: print "Error (unknown command):", lines[1] print self.board continue if not validateMove(self.board, move): print "Illegal move", lines[1] print self.board continue self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() elif lines[0] == "?": if not self.forced and not self.analyzing: self.__stopSearching() elif lines[0] == "ping": print "pong", lines[1] elif lines[0] == "draw": if self.__willingToDraw(): print "offer draw" elif lines[0] == "result": # We don't really care what the result is at the moment. pass elif lines[0] == "setboard": self.__stopSearching() try: self.board = LBoard(self.board.variant) fen = " ".join(lines[1:]) self.board.applyFen(fen.replace("[", "/").replace("]", "")) except SyntaxError as e: print "tellusererror Illegal position:", str(e) if self.analyzing: self.__analyze() # "edit" is unimplemented. See docs. Exiting edit mode returns to analyze mode. elif lines[0] == "hint": pass # TODO: Respond "Hint: MOVE" if we have an expected reply elif lines[0] == "bk": entries = getOpenings(self.board) if entries: totalWeight = sum(entry[1] for entry in entries) for entry in entries: print "\t%s\t%02.2f%%" % (toSAN(self.board, entry[0]), entry[1] * 100.0 / totalWeight) elif lines[0] == "undo": self.__stopSearching() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] == "remove": self.__stopSearching() self.board.popMove() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] in ("hard", "easy"): self.ponder = (lines[0] == "hard") elif lines[0] in ("post", "nopost"): self.post = (lines[0] == "post") elif lines[0] == "analyze": self.analyzing = True self.__analyze() elif lines[0] in ("name", "rating", "ics", "computer"): pass # We don't care. # Unimplemented: pause, resume elif lines[0] == "memory": # FIXME: this is supposed to control the *total* memory use. if lsearch.searching: print "Error (already searching):", line else: limit = int(lines[1]) if limit < 1: print "Error (limit too low):", line else: pass # TODO implement #lsearch.setHashSize(limit) elif lines[0] == "cores": pass # We aren't SMP-capable. elif lines[0] == "egtpath": # TODO: Accept "egtpath TYPE PATH" commands, at least for Gaviota EGTBs pass elif lines[0] == "option" and len(lines) > 1: name, eq, value = lines[1].partition("=") if value: value = int(value) # CECP spec says option values are *always* numeric if name == "skipPruneChance": if 0 <= value <= 100: self.skipPruneChance = value / 100.0 else: print "Error (argument must be an integer 0..100):", line ########## CECP analyze mode commands ########## # See http://www.gnu.org/software/xboard/engine-intf.html#11 elif lines[0] == "exit": if self.analyzing: self.__stopSearching() self.analyzing = False # Periodic updates (".") are not implemented. ########## Custom commands ########## elif lines[0] == "egtb": enableEGTB() elif lines[0] == "benchmark": benchmark() elif lines[0] == "profile": if len(lines) > 1: import cProfile cProfile.runctx("benchmark()", locals(), globals(), lines[1]) else: print "Usage: profile outputfilename" elif len(lines) == 1: # A GUI without usermove support might try to send a move. try: move = parseAny (self.board, line) except: print "Error (unknown command):", line continue if not validateMove(self.board, move): print "Illegal move", lines[0] print self.board continue self.__stopSearching() self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() else: print "Error (unknown command):", line
log.warning("Couldn't parse %s data for position %s.\nData was: %s" % (reprColor[color], fen, repr(data))) self.table[(fen, color)] = [] # Don't try again. if (fen,board.color) in self.table: return self.table[(fen,board.color)] return [] def scoreGame(self, board, omitDepth, probeSoft): scores = self.scoreAllMoves(board, probeSoft) if scores: return scores[0][1], scores[0][2] return None, None if __name__ == "__main__": from pychess.Utils.lutils.LBoard import LBoard from pychess.Utils.lutils.lmove import listToSan board = LBoard(NORMALCHESS) board.applyFen("8/k2P4/8/8/8/8/8/4K2R w - - 0 1") moves = probeEndGameTable(board) assert len(moves) == 18, listToSan(board, (move[0] for move in moves)) board.applyFen("8/p7/6kp/3K4/6PP/8/8/8 b - - 0 1") moves = probeEndGameTable(board) assert len(moves) == 7, listToSan(board, (move[0] for move in moves)) board.applyFen("8/p6k/2K5/7R/6PP/8/8/8 b - - 0 66") moves = probeEndGameTable(board) assert len(moves) == 3, listToSan(board, (move[0] for move in moves))
from pychess.System.prefix import addDataPrefix from pychess.Utils.lutils.LBoard import LBoard from pychess.Utils.lutils.lmove import toSAN, toFAN from pychess.Savers.pgn import move_count from pychess.Savers.pgnbase import nag2symbol from pychess.widgets.Background import set_textview_color from pychess.widgets.ChessClock import formatTime from pychess.Utils.TimeModel import TimeModel __title__ = _("Annotation") __active__ = True __icon__ = addDataPrefix("glade/panel_annotation.svg") __desc__ = _("Annotated game") EMPTY_BOARD = LBoard() EMPTY_BOARD.applyFen(FEN_EMPTY) """ We are maintaining a list of nodes to help manipulate the textbuffer. Node can represent a move, comment or variation (start/end) marker. Nodes are dicts with keys like: board = in move node it's the lboard of move in comment node it's the lboard where the comment belongs to in end variation marker node it's the first lboard of the variation in start variation marker is's None start = the beginning offest of the node in the textbuffer end = the ending offest of the node in the textbuffer parent = the parent lboard if the node is a move in a variation, otherwise None vari = in end variation node it's the start variation marker node in start variation node it's None level = depth in variation tree (0 for mainline nodes, 1 for first level variation moves, etc.)
class OLVFile(ChessFile): def __init__(self, handle): ChessFile.__init__(self, handle) self.games = self.read_games(handle) self.count = len(self.games) def read_games(self, handle): """ We don't return games if stipulation is not 'mate in #' """ games = [] rec = None rec_id = 1 contains_fairy_pieces = False # authors is a more line list (each line starts with "-") in_authors = False # piece list are usually in one line list inside [] # but sometimes given in more line lists in_white = False in_black = False for line in handle: line = line.rstrip() if in_authors and ":" in line: in_authors = False elif in_white and ":" in line: in_white = False elif in_black and ":" in line: in_black = False rec["FEN"] = self.lboard.asFen() # New record start if line == "---": if rec is not None and rec["Black"].startswith("Mate in ") and not contains_fairy_pieces: games.append(rec) rec_id += 1 contains_fairy_pieces = False self.lboard = LBoard() self.lboard.applyFen("8/8/8/8/8/8/8/8 w - - 0 1") rec = collections.defaultdict(str) rec["Id"] = rec_id rec["Offset"] = 0 elif line.startswith("authors:"): in_authors = True elif line.startswith("source:"): rec["Event"] = line[8:] elif line.startswith("source-id:"): rec["Event"] = "%s (%s)" % (rec["Event"], line[12:]) elif line.startswith("date:"): parts = line[6:].split("-") parts_len = len(parts) if parts_len >= 3: rec["Day"] = parts[2] if parts_len >= 2: rec["Month"] = parts[1] if parts_len >= 1: rec["Year"] = parts[0] elif line.startswith("distinction:"): rec["Site"] = line[12:] elif line.startswith("algebraic:"): pass elif line.startswith(" white:"): parts = line.split("[") if len(parts) > 1: pieces = parts[1][:-1] for piece in pieces.split(", "): if piece.startswith("Royal") or piece[0] not in chr2piece: contains_fairy_pieces = True else: cord = Cord(piece[1:3]).cord piece = chr2piece[piece[0]] self.lboard._addPiece(cord, piece, WHITE) else: in_white = True elif line.startswith(" black:"): parts = line.split("[") if len(parts) > 1: pieces = parts[1][:-1] for piece in pieces.split(", "): if piece.startswith("Royal") or piece[0] not in chr2piece: contains_fairy_pieces = True else: cord = Cord(piece[1:3]).cord piece = chr2piece[piece[0]] self.lboard._addPiece(cord, piece, BLACK) rec["FEN"] = self.lboard.asFen() else: in_black = True elif line.startswith("stipulation:"): if line.endswith("Black to move"): line = line[:-14] rec["FEN"] = rec["FEN"].replace("w", "b") line = line.split(": ")[1] if "+" in line: rec["Result"] = WHITEWON rec["Black"] = "Win" elif "-" in line: rec["Result"] = BLACKWON rec["Black"] = "Win" elif "=" in line: rec["Result"] = DRAW rec["Black"] = "Draw" elif line.startswith('"#'): rec["Result"] = WHITEWON rec["Black"] = "Mate in %s" % line[2:-1] rec["Termination"] = "mate in %s" % line[2:-1] elif line.startswith("solution:"): # TODO: solutions can be in several (sometimes rather unusual) form pass else: if in_authors: author = line[line.find("-") + 1:].lstrip() if rec["White"]: rec["White"] = "%s - %s" % (rec["White"], author) else: rec["White"] = author elif in_white: piece = line[line.find("-") + 1:].lstrip() cord = Cord(piece[1:3]).cord piece = chr2piece[piece[0]] self.lboard._addPiece(cord, piece, WHITE) elif in_black: piece = line[line.find("-") + 1:].lstrip() cord = Cord(piece[1:3]).cord piece = chr2piece[piece[0]] self.lboard._addPiece(cord, piece, BLACK) # Append the latest record if rec is not None and rec["Black"].startswith("Mate in ") and not contains_fairy_pieces: games.append(rec) return games def loadToModel(self, rec, position, model=None): if not model: model = GameModel() model.tags['Event'] = rec["Event"] model.tags['Site'] = rec["Site"] model.tags['Date'] = self.get_date(rec) model.tags['Round'] = "" model.tags['White'] = "?" model.tags['Black'] = "?" model.tags['Termination'] = rec["Termination"] fen = rec["FEN"] model.boards = [model.variant(setup=fen)] model.variations = [model.boards] model.status = WAITING_TO_START return model def get_date(self, rec): year = rec['Year'] month = rec['Month'] day = rec['Day'] if year and month and day: tag_date = "%s.%02d.%02d" % (year, int(month), int(day)) elif year and month: tag_date = "%s.%02d" % (year, int(month)) elif year: tag_date = "%s" % year else: tag_date = "" return tag_date
def test_apply_pop(self): """Testing Crazyhouse applyMove popMove""" board = LBoard(variant=CRAZYHOUSECHESS) board.applyFen(FEN1) holding0 = (board.holding[0].copy(), board.holding[1].copy()) promoted0 = board.promoted[:] capture_promoting0 = board.capture_promoting hist_capture_promoting0 = board.hist_capture_promoting[:] print_board_promoted = False print_apply_pop = False for lmove1 in genAllMoves(board): #if lmove1 != parseAN(board, "c7b8=Q"): # continue board.applyMove(lmove1) if print_apply_pop: print("applyMove1", Move(lmove1), board.holding, board.capture_promoting) if print_board_promoted: print(board.promoted) if board.opIsChecked(): if print_apply_pop: print("popMove1 (invalid)", Move(lmove1)) board.popMove() continue holding1 = (board.holding[0].copy(), board.holding[1].copy()) promoted1 = board.promoted[:] capture_promoting1 = board.capture_promoting hist_capture_promoting1 = board.hist_capture_promoting[:] for lmove2 in genAllMoves(board): #if lmove2 != parseAN(board, "e8f7"): # continue board.applyMove(lmove2) if print_apply_pop: print(" applyMove2", Move(lmove2), board.holding, board.capture_promoting) if print_board_promoted: print(board.promoted) if board.opIsChecked(): if print_apply_pop: print(" popMove2 (invalid)", Move(lmove2)) board.popMove() continue holding2 = (board.holding[0].copy(), board.holding[1].copy()) promoted2 = board.promoted[:] capture_promoting2 = board.capture_promoting hist_capture_promoting2 = board.hist_capture_promoting[:] for lmove3 in genAllMoves(board): #if lmove3 != parseAN(board, "b8c8"): # continue board.applyMove(lmove3) if print_apply_pop: print(" applyMove3", Move(lmove3), board.holding, board.capture_promoting) if print_board_promoted: print(board.promoted) if board.opIsChecked(): if print_apply_pop: print(" popMove3 (invalid)", Move(lmove3)) board.popMove() continue board.popMove() if print_apply_pop: print(" popMove3", Move(lmove3), board.holding, board.capture_promoting) if print_board_promoted: print(board.promoted) self.assertEqual(holding2, board.holding) self.assertEqual(promoted2, board.promoted) self.assertEqual(capture_promoting2, board.capture_promoting) self.assertEqual(hist_capture_promoting2, board.hist_capture_promoting) board.popMove() if print_apply_pop: print(" popMove2", Move(lmove2), board.holding, board.capture_promoting) if print_board_promoted: print(board.promoted) self.assertEqual(holding1, board.holding) self.assertEqual(promoted1, board.promoted) self.assertEqual(capture_promoting1, board.capture_promoting) self.assertEqual(hist_capture_promoting1, board.hist_capture_promoting) board.popMove() if print_apply_pop: print("popMove1", Move(lmove1), board.holding, board.capture_promoting) if print_board_promoted: print(board.promoted) self.assertEqual(holding0, board.holding) self.assertEqual(promoted0, board.promoted) self.assertEqual(capture_promoting0, board.capture_promoting) self.assertEqual(hist_capture_promoting0, board.hist_capture_promoting)