Example #1
0
    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))
Example #2
0
    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))
Example #3
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)
Example #4
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))
Example #5
0
    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))
Example #6
0
    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)
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
    def test_moves_from_startpos(self):
        board = LBoard(SCHESS)
        board.applyFen(SCHESSSTART)

        moves = set()
        for move in genAllMoves(board):
            moves.add(toAN(board, move))

            board.applyMove(move)
            board.popMove()
            self.assertEqual(placement(board.asFen()), placement(SCHESSSTART))

        self.assertIn("b1a3e", moves)
        self.assertIn("b1a3h", moves)
        self.assertIn("b1c3e", moves)
        self.assertIn("b1c3h", moves)

        self.assertIn("g1f3e", moves)
        self.assertIn("g1f3h", moves)
        self.assertIn("g1h3e", moves)
        self.assertIn("g1h3h", moves)
Example #10
0
    def test_pdb_database_get_bitboards(self):
        """Testing .pdb database get_bitboards"""

        db = self.load_test_pgn()

        board = LBoard()
        board.applyFen(FEN_START)

        def get_bb_candidates(board):
            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()
            return bb_candidates

        # ply 1
        bitboards = db.get_bitboards(1, get_bb_candidates(board))
        print("==========")
        for row in bitboards:
            print(row)
        print("----------")
        self.assertEqual(len(bitboards), BITBOARD_COUNT[1])
        self.assertEqual(sum([row[1] for row in bitboards]), GAME_COUNT)
        self.assertEqual(bitboards[0][5], 2712)

        # ply 2
        board.applyMove(newMove(E2, E4))
        bitboards = db.get_bitboards(2, get_bb_candidates(board))
        print("==========")
        for row in bitboards:
            print(row)
        print("----------")
        self.assertEqual(len(bitboards), BITBOARD_COUNT[2])
        self.assertEqual(sum([row[1] for row in bitboards]), GAME_COUNT)
Example #11
0
    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)
Example #12
0
    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)
Example #13
0
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," + \
                        "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 = raw_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] == "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] == "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 e:
                        self.print("Error (unknown command): %s" % lines[1])
                        self.print(self.board)
                        continue
                    if not validateMove(self.board, move):
                        self.print("Illegal move: %s" % lines[1])
                        self.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":
                    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 e:
                        self.print("tellusererror Illegal position: %s" %
                                   str(e))

                # "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)
                    self.print([
                        toSAN(self.board, move)
                        for move in genAllMoves(self.board)
                    ])

                elif lines[0] == "captures":
                    self.print(self.board)
                    self.print([
                        toSAN(self.board, move)
                        for move in genCaptures(self.board)
                    ])

                elif lines[0] == "evasions":
                    self.print(self.board)
                    self.print([
                        toSAN(self.board, move)
                        for move in genCheckEvasions(self.board)
                    ])

                elif lines[0] == "benchmark":
                    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 len(lines) == 1:
                    # A GUI without usermove support might try to send a move.
                    try:
                        move = parseAny(self.board, line)
                    except:
                        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)
                        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
Example #14
0
    def test_apply_pop(self):
        """Testing Crazyhouse applyMove popMove"""

        board = LBoard(variant=CRAZYHOUSECHESS)
        board.applyFen(FEN2)

        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)
Example #15
0
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()
Example #16
0
    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)
Example #17
0
    def download_game(self):
        # Download the page
        if self.id is None:
            return None
        url = 'https://chess24.com/en/game/%s' % self.id
        page = self.download(url, userAgent=True)  # Else HTTP 403 Forbidden
        if page is None:
            return None

        # Extract the JSON of the game
        lines = page.split("\n")
        for line in lines:
            line = line.strip()
            pos1 = line.find('.initGameSession({')
            pos2 = line.find('});', pos1)
            if -1 in [pos1, pos2]:
                continue

            # Read the game from JSON
            bourne = self.json_loads(line[pos1 + 17:pos2 + 1])
            chessgame = self.json_field(bourne, 'chessGame')
            moves = self.json_field(chessgame, 'moves')
            if '' in [chessgame, moves]:
                continue

            # Build the header of the PGN file
            game = {}
            game['_moves'] = ''
            game['_url'] = url
            game['Event'] = self.json_field(chessgame, 'meta/Event')
            game['Site'] = self.json_field(chessgame, 'meta/Site')
            game['Date'] = self.json_field(chessgame, 'meta/Date')
            game['Round'] = self.json_field(chessgame, 'meta/Round')
            game['White'] = self.json_field(chessgame, 'meta/White/Name')
            game['WhiteElo'] = self.json_field(chessgame, 'meta/White/Elo')
            game['Black'] = self.json_field(chessgame, 'meta/Black/Name')
            game['BlackElo'] = self.json_field(chessgame, 'meta/Black/Elo')
            game['Result'] = self.json_field(chessgame, 'meta/Result')

            # Build the PGN
            board = LBoard(variant=FISCHERRANDOMCHESS)
            head_complete = False
            for move in moves:
                # Info from the knot
                kid = self.json_field(move, 'knotId')
                if kid == '':
                    break
                kmove = self.json_field(move, 'move')

                # FEN initialization
                if kid == 0:
                    kfen = self.json_field(move, 'fen')
                    if kfen == '':
                        break
                    try:
                        board.applyFen(kfen)
                    except Exception:
                        return None
                    game['Variant'] = 'Fischerandom'
                    game['SetUp'] = '1'
                    game['FEN'] = kfen
                    head_complete = True
                else:
                    if not head_complete:
                        return None

                    # Execution of the move
                    if kmove == '':
                        break
                    try:
                        if self.use_an:
                            kmove = parseAny(board, kmove)
                            game['_moves'] += toSAN(board, kmove) + ' '
                            board.applyMove(kmove)
                        else:
                            game['_moves'] += kmove + ' '
                    except Exception:
                        return None

            # Rebuild the PGN game
            return self.rebuild_pgn(game)
        return None
Example #18
0
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()
Example #19
0
    def download_game(self):
        # Check
        if self.id is None:
            return None

        # Fetch the page to retrieve the encrypted user name
        url = 'https://chess.org/play/%s' % self.id
        page = self.download(url)
        if page is None:
            return None
        lines = page.split("\n")
        name = ''
        for line in lines:
            pos1 = line.find('encryptedUsername')
            if pos1 != -1:
                pos1 = line.find("'", pos1)
                pos2 = line.find("'", pos1 + 1)
                if pos2 > pos1:
                    name = line[pos1 + 1:pos2]
                    break
        if name == '':
            return None

        # Random elements to get a unique URL
        rndI = random.randint(1, 1000)
        rndS = ''.join(random.choice(string.ascii_lowercase) for i in range(8))

        # Open a websocket to retrieve the chess data
        @asyncio.coroutine
        def coro():
            url = 'wss://chess.org:443/play-sockjs/%d/%s/websocket' % (rndI, rndS)
            log.debug('Websocket connecting to %s' % url)
            ws = yield from websockets.connect(url, origin="https://chess.org:443")
            try:
                # Server: Hello
                data = yield from ws.recv()
                if data != 'o':  # Open
                    yield from ws.close()
                    return None

                # Client: I am XXX, please open the game YYY
                yield from ws.send('["%s %s"]' % (name, self.id))
                data = yield from ws.recv()

                # Server: some data
                if data[:1] != 'a':
                    yield from ws.close()
                    return None
                return data[3:-2]
            finally:
                yield from ws.close()

        data = self.async_from_sync(coro())
        if data is None or data == '':
            return None

        # Parses the game
        chessgame = self.json_loads(data.replace('\\"', '"'))
        game = {}
        game['_url'] = url
        board = LBoard(variant=FISCHERRANDOMCHESS)

        # Player info
        if self.json_field(chessgame, 'creatorColor') == '1':  # White=1, Black=0
            creator = 'White'
            opponent = 'Black'
        else:
            creator = 'Black'
            opponent = 'White'
        game[creator] = self.json_field(chessgame, 'creatorId')
        elo = self.json_field(chessgame, 'creatorPoint')
        if elo not in ['', '0', 0]:
            game[creator + 'Elo'] = elo
        game[opponent] = self.json_field(chessgame, 'opponentId')
        elo = self.json_field(chessgame, 'opponentPoint')
        if elo not in ['', '0', 0]:
            game[opponent + 'Elo'] = elo

        # Game info
        startPos = self.json_field(chessgame, 'startPos')
        if startPos not in ['', 'startpos']:
            game['SetUp'] = '1'
            game['FEN'] = startPos
            game['Variant'] = 'Fischerandom'
            try:
                board.applyFen(startPos)
            except Exception:
                return None
        else:
            board.applyFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w AHah - 0 1')
        time = self.json_field(chessgame, 'timeLimitSecs')
        inc = self.json_field(chessgame, 'timeBonusSecs')
        if '' not in [time, inc]:
            game['TimeControl'] = '%s+%s' % (time, inc)
        resultTable = [(0, '*', 'Game started'),
                       (1, '1-0', 'White checkmated'),
                       (2, '0-1', 'Black checkmated'),
                       (3, '1/2-1/2', 'Stalemate'),
                       (5, '1/2-1/2', 'Insufficient material'),
                       (8, '1/2-1/2', 'Mutual agreement'),
                       (9, '0-1', 'White resigned'),
                       (10, '1-0', 'Black resigned'),
                       (13, '1-0', 'White out of time'),
                       (14, '0-1', 'Black out of time')]  # TODO List to be completed
        state = self.json_field(chessgame, 'state')
        result = '*'
        reason = 'Unknown reason %d' % state
        for rtID, rtScore, rtMsg in resultTable:
            if rtID == state:
                result = rtScore
                reason = rtMsg
                break
        game['Result'] = result
        game['_reason'] = reason

        # Moves
        game['_moves'] = ''
        moves = self.json_field(chessgame, 'lans')
        if moves == '':
            return None
        moves = moves.split(' ')
        for move in moves:
            try:
                if self.use_an:
                    move = parseAny(board, move)
                    game['_moves'] += toSAN(board, move) + ' '
                    board.applyMove(move)
                else:
                    game['_moves'] += move + ' '
            except Exception:
                return None

        # Rebuild the PGN game
        return self.rebuild_pgn(game)
Example #20
0
class OpeningTreePanel(Gtk.TreeView):
    def __init__(self, persp):
        GObject.GObject.__init__(self)
        self.persp = persp
        self.filtered = False

        self.persp.connect("chessfile_imported", self.on_chessfile_imported)

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

        self.liststore = Gtk.ListStore(int, str, int, int)
        self.modelsort = Gtk.TreeModelSort(self.liststore)

        self.modelsort.set_sort_column_id(2, Gtk.SortType.DESCENDING)
        self.set_model(self.modelsort)

        self.set_headers_visible(True)

        column = Gtk.TreeViewColumn(_("Move"), Gtk.CellRendererText(), text=1)
        column.set_sort_column_id(1)
        column.connect("clicked", self.column_clicked, 1)
        self.append_column(column)

        column = Gtk.TreeViewColumn(_("Games"), Gtk.CellRendererText(), text=2)
        column.set_sort_column_id(2)
        column.connect("clicked", self.column_clicked, 2)
        self.append_column(column)

        column = Gtk.TreeViewColumn(_("Winning %"),
                                    Gtk.CellRendererProgress(),
                                    value=3)
        column.set_min_width(80)
        column.set_sort_column_id(3)
        column.connect("clicked", self.column_clicked, 3)
        self.append_column(column)

        self.conid = self.connect_after("row-activated", self.row_activated)

        self.board = LBoard()
        self.board.applyFen(FEN_START)

        self.columns_autosize()

        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        sw.add(self)

        self.box.pack_start(sw, True, True, 0)

        #  buttons
        toolbar = Gtk.Toolbar()

        firstButton = Gtk.ToolButton(stock_id=Gtk.STOCK_MEDIA_PREVIOUS)
        toolbar.insert(firstButton, -1)

        prevButton = Gtk.ToolButton(stock_id=Gtk.STOCK_MEDIA_REWIND)
        toolbar.insert(prevButton, -1)

        self.filterButton = Gtk.ToggleToolButton(Gtk.STOCK_FIND)
        self.filterButton.set_tooltip_text(
            _("Filter game list by opening moves"))
        toolbar.insert(self.filterButton, -1)

        firstButton.connect("clicked", self.on_first_clicked)
        prevButton.connect("clicked", self.on_prev_clicked)
        self.filterButton.connect("clicked", self.on_filter_clicked)

        tool_box = Gtk.Box()
        tool_box.pack_start(toolbar, False, False, 0)
        self.box.pack_start(tool_box, False, False, 0)

        self.box.show_all()

    def on_chessfile_imported(self, persp, chessfile):
        self.update_tree()

    def on_first_clicked(self, widget):
        while self.board.hist_move:
            self.board.popMove()
        self.update_tree()

    def on_prev_clicked(self, widget):
        if self.board.hist_move:
            self.board.popMove()
        self.update_tree()

    def on_filter_clicked(self, button):
        self.filtered = button.get_active()
        if not self.filtered:
            self.persp.filter_panel.filterButton.set_sensitive(True)
            self.filtered = True
            while self.board.hist_move:
                self.board.popMove()
            self.update_tree()
            self.filtered = False
        else:
            self.persp.filter_panel.filterButton.set_sensitive(False)
            self.update_tree()

    def column_clicked(self, col, data):
        self.set_search_column(data)

    def row_activated(self, widget, path, col):
        lmove = self.liststore[self.modelsort.convert_path_to_child_path(path)
                               [0]][0]
        self.board.applyMove(lmove)
        self.update_tree()

    def update_tree(self, load_games=True):
        self.persp.gamelist.ply = self.board.plyCount
        if load_games and self.filtered:
            self.persp.chessfile.set_fen_filter(self.board.asFen())
            self.persp.gamelist.load_games()

        result = self.persp.chessfile.get_book_moves(self.board.asFen())
        self.clear_tree()
        for move, count, white_won, blackwon, draw in result:
            lmove = parseAN(self.board, move)
            perf = 0 if not count else round(
                (white_won * 100. + draw * 50.) / count)
            self.liststore.append(
                [lmove, toSAN(self.board, lmove), count, perf])

    def clear_tree(self):
        selection = self.get_selection()
        if self.conid is not None and selection.handler_is_connected(
                self.conid):
            with GObject.signal_handler_block(selection, self.conid):
                self.liststore.clear()
        else:
            self.liststore.clear()
Example #21
0
    def download_game(self):
        # Check
        if self.id is None:
            return None

        # Download
        url = 'https://gameknot.com/analyze-board.pl?bd=%s' % self.id
        page = self.download(url, userAgent=True)
        if page is None:
            return None

        # Header
        game = {}
        structure = [('anbd_movelist', 's', '_moves'),
                     ('anbd_result', 'i', 'Result'),
                     ('anbd_player_w', 's', 'White'),
                     ('anbd_player_b', 's', 'Black'),
                     ('anbd_rating_w', 'i', 'WhiteElo'),
                     ('anbd_rating_b', 'i', 'BlackElo'),
                     ('anbd_title', 's', 'Event'),
                     ('anbd_timestamp', 's', 'Date'),
                     ('export_web_input_result_text', 's', '_reason')]
        for var, type, tag in structure:
            game[tag] = ''
        game['_url'] = url
        lines = page.split(';')
        for line in lines:
            for var, type, tag in structure:
                if var not in line.lower():
                    continue
                if type == 's':
                    pos1 = line.find("'")
                    pos2 = line.find("'", pos1 + 1)
                    if pos2 > pos1:
                        game[tag] = line[pos1 + 1:pos2]
                elif type == 'i':
                    pos1 = line.find("=")
                    if pos1 != -1:
                        txt = line[pos1 + 1:].strip()
                        if txt not in ['', '0']:
                            game[tag] = txt
                else:
                    return None
        if game['Result'] == '1':
            game['Result'] = '1-0'
        elif game['Result'] == '2':
            game['Result'] = '1/2-1/2'
        elif game['Result'] == '3':
            game['Result'] = '0-1'
        else:
            game['Result'] = '*'

        # Body
        board = LBoard()
        board.applyFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1')
        moves = game['_moves'].split('-')
        game['_moves'] = ''
        for move in moves:
            if move == '':
                break
            try:
                if self.use_an:
                    kmove = parseAny(board, move)
                    game['_moves'] += toSAN(board, kmove) + ' '
                    board.applyMove(kmove)
                else:
                    game['_moves'] += move + ' '
            except Exception:
                return None

        # Rebuild the PGN game
        return self.rebuild_pgn(game)
Example #22
0
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
Example #23
0
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
Example #24
0
class OpeningTreePanel(Gtk.TreeView):
    def __init__(self, gamelist):
        GObject.GObject.__init__(self)
        self.gamelist = gamelist

        self.filtered = False

        self.persp = perspective_manager.get_perspective("database")
        self.persp.connect("chessfile_opened", self.on_chessfile_opened)
        self.persp.connect("chessfile_switched", self.on_chessfile_switched)
        self.persp.connect("chessfile_imported", self.on_chessfile_imported)

        self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

        self.liststore = Gtk.ListStore(int, str, int, int, int)
        self.modelsort = Gtk.TreeModelSort(self.liststore)

        self.modelsort.set_sort_column_id(2, Gtk.SortType.DESCENDING)
        self.set_model(self.modelsort)

        self.set_headers_visible(True)

        column = Gtk.TreeViewColumn(_("Move"), Gtk.CellRendererText(), text=1)
        column.set_sort_column_id(1)
        column.connect("clicked", self.column_clicked, 1)
        self.append_column(column)

        column = Gtk.TreeViewColumn(_("Games"), Gtk.CellRendererText(), text=2)
        column.set_sort_column_id(2)
        column.connect("clicked", self.column_clicked, 2)
        self.append_column(column)

        column = Gtk.TreeViewColumn(_("Result"),
                                    Gtk.CellRendererProgress(),
                                    value=3)
        column.set_min_width(80)
        column.set_sort_column_id(3)
        column.connect("clicked", self.column_clicked, 3)
        self.append_column(column)

        column = Gtk.TreeViewColumn(_("Elo Avg"),
                                    Gtk.CellRendererText(),
                                    text=4)
        column.set_sort_column_id(4)
        column.connect("clicked", self.column_clicked, 4)
        self.append_column(column)

        self.conid = self.connect_after("row-activated", self.row_activated)

        self.board = LBoard()
        self.board.applyFen(FEN_START)
        self.update_tree()

        self.columns_autosize()

        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        sw.add(self)

        self.box.pack_start(sw, True, True, 0)

        #  buttons
        toolbar = Gtk.Toolbar()

        firstButton = Gtk.ToolButton(Gtk.STOCK_MEDIA_PREVIOUS)
        toolbar.insert(firstButton, -1)

        prevButton = Gtk.ToolButton(Gtk.STOCK_MEDIA_REWIND)
        toolbar.insert(prevButton, -1)

        filterButton = Gtk.ToggleToolButton(Gtk.STOCK_FIND)
        toolbar.insert(filterButton, -1)

        firstButton.connect("clicked", self.on_first_clicked)
        prevButton.connect("clicked", self.on_prev_clicked)
        filterButton.connect("clicked", self.on_filter_clicked)

        tool_box = Gtk.Box()
        tool_box.pack_start(toolbar, False, False, 0)
        self.box.pack_start(tool_box, False, False, 0)

        self.box.show_all()

    def on_chessfile_opened(self, persp, chessfile):
        self.update_tree(load_games=False)

    def on_chessfile_switched(self, switcher, chessfile):
        if isinstance(chessfile, Database):
            self.update_tree()
        else:
            self.clear_tree()

    def on_chessfile_imported(self, persp, chessfile):
        self.update_tree()

    def on_first_clicked(self, widget):
        while self.board.hist_move:
            self.board.popMove()
        self.update_tree()

    def on_prev_clicked(self, widget):
        if self.board.hist_move:
            self.board.popMove()
        self.update_tree()

    def on_filter_clicked(self, button):
        self.filtered = button.get_active()

    def column_clicked(self, col, data):
        self.set_search_column(data)

    def row_activated(self, widget, path, col):
        lmove = self.liststore[self.modelsort.convert_path_to_child_path(path)
                               [0]][0]
        self.board.applyMove(lmove)
        self.update_tree()
        self.gamelist.update_counter(with_select=True)

    def update_tree(self, load_games=True):
        bb = self.board.friends[0] | self.board.friends[1]
        self.gamelist.ply = self.board.plyCount
        self.gamelist.chessfile.build_where_bitboards(self.board.plyCount, bb)
        self.gamelist.offset = 0
        self.gamelist.chessfile.build_query()
        if load_games and self.filtered:
            self.gamelist.load_games()

        bb_candidates = {}
        for lmove in genAllMoves(self.board):
            self.board.applyMove(lmove)
            if self.board.opIsChecked():
                self.board.popMove()
                continue
            bb_candidates[self.board.friends[0]
                          | self.board.friends[1]] = lmove
            self.board.popMove()

        result = []
        # print("get_bitboards() for %s bb_candidates" % len(bb_candidates))
        bb_list = self.gamelist.chessfile.get_bitboards(
            self.board.plyCount + 1, bb_candidates)

        for bb, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg in bb_list:
            try:
                result.append((bb_candidates[bb], count, white_won, blackwon,
                               draw, white_elo_avg, black_elo_avg))
                # print("OK      ", bb, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg)
            except KeyError:
                # print("KeyError", bb, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg)
                pass

        self.clear_tree()

        for lmove, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg in result:
            perf = 0 if not count else round(
                (white_won * 100. + draw * 50.) / count)
            elo_avg = white_elo_avg if self.board.color == WHITE else black_elo_avg
            self.liststore.append(
                [lmove, toSAN(self.board, lmove), count, perf, elo_avg])

    def clear_tree(self):
        selection = self.get_selection()
        if self.conid is not None and selection.handler_is_connected(
                self.conid):
            with GObject.signal_handler_block(selection, self.conid):
                self.liststore.clear()
        else:
            self.liststore.clear()
Example #25
0
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.worker = None
        
        self.features = {
            "setboard": 1,
            "analyze": 1,
            "san": 1,
            "usermove": 1,
            "reuse": 0,
            "draw": 1,
            "sigterm": 1,
            "colors": 1,
            "variants": "normal,nocastle,fischerandom",
            "myname": "PyChess %s" % pychess.VERSION
        }
    
    def makeReady(self):
        PyChess.makeReady(self)
    
    def run (self):
        while True:
            line = raw_input()
            if not line.strip(): continue
            lines = line.split()
            
            if lines[0] == "protover":
                stringPairs = ["=".join([k,repr(v)]) for k,v in self.features.iteritems()]
                print "feature %s done=1" % " ".join(stringPairs)
            
            elif lines[0] == "usermove":
                
                self.__stopSearching()
                
                move = parseAny (self.board, lines[1])
                
                if not validateMove(self.board, move):
                    print "Illegal move", lines[1]
                    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] == "sd":
                self.sd = int(lines[1])
                self.skipPruneChance = max(0, (5-self.sd)*0.02)
                if self.sd >= 5:
                    print "If the game has no timesettings, you probably don't want\n"+\
                          "to set a search depth much greater than 4"
            
            elif lines[0] == "egtb":
                # This is a crafty command interpreted a bit loose
                self.useegtb = True
            
            elif lines[0] == "level":
                moves = int(lines[1])
                self.increment = int(lines[3])
                minutes = lines[2].split(":")
                self.mytime = int(minutes[0])*60
                if len(minutes) > 1:
                    self.mytime += int(minutes[1])
                print "Playing %d moves in %d seconds + %d increment" % \
                        (moves, self.mytime, self.increment)
            
            elif lines[0] == "time":
                self.mytime = int(lines[1])/100.
            
            #elif lines[0] == "otim":
            #   self.optime = int(lines[1])
            
            elif lines[0] == "quit":
                sys.exit()
            
            elif lines[0] == "result":
                # We don't really care what the result is at the moment.
                sys.exit()
            
            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] == "undo":
                self.__stopSearching()
                self.board.popMove()
                if self.analyzing:
                    self.__analyze()
            
            elif lines[0] == "?":
                self.__stopSearching()
            
            elif lines[0] in ("black", "white"):
                newColor = lines[0] == "black" and BLACK or WHITE
                if self.playingAs != newColor:
                    self.__stopSearching()
                    self.playingAs = newColor
                    # It is dangerous to use the same table, when we change color
                    #lsearch.table.clear()
                    self.board.setColor(newColor)
                    # Concider the case:
                    # * White moves a pawn and creates a enpassant cord
                    # * Spy analyzer is not set back to white to analyze the
                    #   position. An attackable enpassant cord now no longer makes
                    #   sense.
                    # * Notice though, that when the color is shifted back to black
                    #   to make the actual move - and it is an enpassant move - the
                    #   cord won't be set in the lboard.
                    self.board.setEnpassant(None)
                    if self.analyzing:
                        self.__analyze()
            
            elif lines[0] == "analyze":
                self.playingAs = self.board.color
                self.analyzing = True
                self.__analyze()
                
            elif lines[0] == "draw":
                if self.scr <= 0:
                    print "offer draw"
                
            elif lines[0] == "random":
                leval.random = True
            
            elif lines[0] == "variant":
                if lines[1] == "fischerandom":
                    self.board.variant = FISCHERRANDOMCHESS
                
            elif lines[0] == "setboard":
                self.__stopSearching()
                self.board.applyFen(" ".join(lines[1:]))
                if self.analyzing:
                    self.__analyze()
            
            elif lines[0] in ("xboard", "otim", "hard", "easy", "nopost", "post",
                              "accepted", "rejected"):
                pass
            
            else: print "Warning (unknown command):", line
    
    def __stopSearching(self):
        lsearch.searching = False
        if self.worker:
            self.worker.cancel()
            glock.acquire()
            self.worker.get()
            glock.release()
            self.worker = None
    
    def __go (self):
        self.worker = GtkWorker(lambda worker: PyChess._PyChess__go(self, worker))
        def process (worker, messages): print "\n".join(messages)
        self.worker.connect("published", process)
        def ondone (worker, result):
            if not result: return
            self.board.applyMove(parseSAN(self.board,result))
            print "move %s" % result
        self.worker.connect("done", ondone)
        self.worker.execute()
    
    def __analyze (self):
        self.worker = GtkWorker(lambda worker: PyChess._PyChess__analyze(self, worker))
        def process (worker, messages): print "\n".join(messages)
        self.worker.connect("published", process)
        self.worker.execute()