Ejemplo n.º 1
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))
Ejemplo n.º 2
0
    def test_toSAN(self):
        """ Testing toSAN() with giveaway king move """

        board = LBoard(GIVEAWAYCHESS)
        board.applyFen("4R3/8/7B/2P5/8/8/PP5k/6k1 b - - 0 28")

        self.assertEqual(toSAN(board, newMove(G1, G2)), "Kgg2")
        self.assertEqual(toSAN(board, newMove(H2, G2)), "Khg2")
Ejemplo n.º 3
0
    def test_gating_san_move(self):
        board = LBoard(SCHESS)
        board.applyFen(SCHESSSTART)

        self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/H'))), 'g1f3h')
        self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/E'))), 'g1f3e')

        self.assertEqual(toSAN(board, newMove(G1, F3, HAWK_GATE)), "Nf3/H")
        self.assertEqual(toSAN(board, newMove(G1, F3, ELEPHANT_GATE)), "Nf3/E")
Ejemplo n.º 4
0
    def test_gating_castle_at_rook_wOOO_SAN(self):
        FEN = "r2ek2r/5pp1/1pp1pnp1/2pp1h2/3P4/2P1PP2/PP1N2PP/R3K1HR/E w KQHEAkq - 2 16"
        board = LBoard(SCHESS)
        board.applyFen(FEN)

        self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O'))), 'e1c1')
        self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ea1'))), 'a1e1e')
        self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ee1'))), 'e1c1e')

        self.assertEqual(toSAN(board, newMove(E1, C1, QUEEN_CASTLE)), "O-O-O")
        self.assertEqual(toSAN(board, newMove(A1, E1, ELEPHANT_GATE_AT_ROOK)), "O-O-O/Ea1")
        self.assertEqual(toSAN(board, newMove(E1, C1, ELEPHANT_GATE)), "O-O-O/Ee1")
Ejemplo n.º 5
0
def read_move(infoline, board, gamepgn, movenum):
    board = board.clone()
    #'info multipv 3 depth 4 seldepth 24 score cp 7 time 4995 nodes 4342 nps 0 tbhits 0 hashfull 0 pv c2c4 c7c5 f2f4 f7f5 e1f2',
    res = {}
    if ' pv ' not in infoline:
        return False
    if 'cp ' in infoline:
        value = ((-1) ** movenum) * int(re.compile(r'cp ([\-\d]+)').findall(infoline)[0])
    else:
        value = ''
    if 'mate' in infoline:
        mate = re.compile(r' mate ([\-\d]+)').findall(infoline)[0]
    else:
        mate = ''
    pv = re.compile(r' pv (.*)').findall(infoline)[0]
    ANmove = pv.split(' ')[0]
    LANmove = lmove.toLAN (board, lmove.parseAN(board, ANmove))
    LANmove = ''.join([c for c in LANmove.replace('x', '-') if c.lower()==c])
    SANmove = lmove.toSAN (board, lmove.parseAN(board, ANmove))
    res = {'nodes': re.compile(r'nodes ([\d]+)').findall(infoline)[0],
           'value': value,
           'mate': mate,
           'pv': movelist2san(board, pv.split()),
           'move': SANmove,
           'lanmove': LANmove,
           }
    return res
Ejemplo n.º 6
0
 def __movestr(self, board):
     move = board.lastMove
     if self.fan:           
         movestr = unicode(toFAN(board.prev, move))
     else:          
         movestr =  unicode(toSAN(board.prev, move, True))
     nagsymbols = "".join([nag2symbol(nag) for nag in board.nags])
     # To prevent wrap castling we will use hyphen bullet (U+2043)       
     return "%s%s%s" % (move_count(board), movestr.replace(u'-', u'\u2043'), nagsymbols)
Ejemplo n.º 7
0
Archivo: pgn.py Proyecto: btrent/knave
def walk(node, result):
    """Prepares a game data for .pgn storage.
       Recursively walks the node tree to collect moves and comments
       into a resulting movetext string.
       
       Arguments:
       node - list (a tree of lboards created by the pgn parser)
       result - str (movetext strings)"""

    def store(text):
        if len(result) > 1 and result[-1] == "(":
            result[-1] = "(%s" % text
        elif text == ")":
            result[-1] = "%s)" % result[-1]
        else:
            result.append(text)

    while True: 
        if node is None:
            break
        
        # Initial game or variation comment
        if node.prev is None:
            for child in node.children:
                if isinstance(child, basestring):
                    store("{%s}" % child)
            node = node.next
            continue

        movecount = move_count(node)
        if movecount:
            store(movecount)

        move = node.lastMove
        store(toSAN(node.prev, move))

        for nag in node.nags:
            if nag:
                store(nag)

        for child in node.children:
            if isinstance(child, basestring):
                # comment
                store("{%s}" % child)
            else:
                # variations
                store("(")
                walk(child[0], result)
                store(")")

        if node.next:
            node = node.next
        else:
            break
Ejemplo n.º 8
0
    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])
Ejemplo n.º 9
0
def movelist2san(board, pv):
    #import ipdb;ipdb.set_trace()
    res = []
    moved = 0
    stfen = board.asFen()
    for mv in pv:
        nummove = lmove.parseAN(board, mv)
        san = lmove.toSAN(board, nummove)
        res.append(san)
        board.applyMove(nummove)
        moved += 1
    for n in range(moved):
        board.popMove()
    assert board.asFen() == stfen
    return res
Ejemplo n.º 10
0
    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])
Ejemplo n.º 11
0
def walk(node, result, model, save_emt=False, save_eval=False, vari=False):
    """Prepares a game data for .pgn storage.
       Recursively walks the node tree to collect moves and comments
       into a resulting movetext string.

       Arguments:
       node - list (a tree of lboards created by the pgn parser)
       result - str (movetext strings)"""

    def store(text):
        if len(result) > 1 and result[-1] == "(":
            result[-1] = "(%s" % text
        elif text == ")":
            result[-1] = "%s)" % result[-1]
        else:
            result.append(text)

    while True:
        if node is None:
            break

        # Initial game or variation comment
        if node.prev is None:
            for child in node.children:
                if isinstance(child, str):
                    store("{%s}" % child)
            node = node.next
            continue

        movecount = move_count(node,
                               black_periods=(save_emt or save_eval) and
                               "TimeControl" in model.tags)
        if movecount is not None:
            if movecount:
                store(movecount)
            move = node.lastMove
            store(toSAN(node.prev, move))
            if (save_emt or save_eval) and not vari:
                emt_eval = ""
                if "TimeControl" in model.tags and save_emt:
                    elapsed = model.timemodel.getElapsedMoveTime(
                        node.plyCount - model.lowply)
                    emt_eval = "[%%emt %s]" % formatTime(elapsed, clk2pgn=True)
                if node.plyCount in model.scores and save_eval:
                    moves, score, depth = model.scores[node.plyCount]
                    if node.color == BLACK:
                        score = -score
                    emt_eval += "[%%eval %0.2f/%s]" % (score / 100.0, depth)
                if emt_eval:
                    store("{%s}" % emt_eval)

        for nag in node.nags:
            if nag:
                store(nag)

        for child in node.children:
            if isinstance(child, str):
                child = re.sub("\[%.*?\]", "", child)
                # comment
                if child:
                    store("{%s}" % child)
            else:
                # variations
                if node.fen_was_applied:
                    store("(")
                    walk(child[0],
                         result,
                         model,
                         save_emt,
                         save_eval,
                         vari=True)
                    store(")")
                    # variation after last played move is not valid pgn
                    # but we will save it as in comment
                else:
                    store("{Analyzer's primary variation:")
                    walk(child[0],
                         result,
                         model,
                         save_emt,
                         save_eval,
                         vari=True)
                    store("}")

        if node.next:
            node = node.next
        else:
            break
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
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
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
    def perft(self, board, depth, prevmoves):
        if depth == 0:
            self.count += 1
            return
        
        if board.isChecked():
            # If we are checked we can use the genCheckEvasions function as well
            # as genAllMoves. Here we try both functions to ensure they return
            # the same result.
            nmoves = []
            for nmove in genAllMoves(board):
                board.applyMove(nmove)
                if board.opIsChecked():
                    board.popMove()
                    continue


                nmoves.append(nmove)
                board.popMove()
                # Validator test
                self.assertTrue(validateMove(board, nmove))
            
            cmoves = []
            
            for move in genCheckEvasions(board):
                board.applyMove(move)
                cmoves.append(move)
                board.popMove()
                # Validator test
                self.assertTrue(validateMove(board, move))
            
            # This is not any kind of alphaBeta sort. Only int sorting, to make
            # comparison possible
            nmoves.sort()
            cmoves.sort()
            
            if nmoves == cmoves:
                for move in cmoves:
                    prevmoves.append(toSAN (board, move))
                    board.applyMove(move)
                    self.perft(board, depth-1, prevmoves)
                    board.popMove()
            else:
                print board
                print "nmoves"
                for move in nmoves:
                    print toSAN (board, move)
                print "cmoves"
                for move in cmoves:
                    print toSAN (board, move)
                self.assertEqual(nmoves, cmoves)
                
        else:
            for move in genAllMoves(board):
                board.applyMove(move)
                if board.opIsChecked():
                    board.popMove()
                    continue
                
                # Validator test
                board.popMove()
                self.assertTrue(validateMove(board, move))
                
                # San test
                san = toSAN (board, move)
                try:
                    move2 = parseSAN(board, san)
                except ParsingError, e:
                    print prevmoves
                    raise ParsingError, e
                self.assertEqual (move, move2)
                
                board.applyMove (move)
                
                self.perft(board, depth-1, prevmoves)
                board.popMove()
Ejemplo n.º 16
0
    def __go(self, ondone=None):
        """ Finds and prints the best move from the current position """

        mv = False if self.outOfBook else self.__getBestOpening()
        if mv:
            mvs = [mv]

        if not mv:

            lsearch.skipPruneChance = self.skipPruneChance
            lsearch.searching = True

            timed = self.basetime > 0

            if self.searchtime > 0:
                usetime = self.searchtime
            else:
                usetime = self.clock[self.playingAs] / self.__remainingMovesA()
                if self.clock[self.playingAs] > 10:
                    # If we have time, we assume 40 moves rather than 80
                    usetime *= 2
                # The increment is a constant. We'll use this always
                usetime += self.increment[self.playingAs]

            prevtime = 0
            starttime = time()
            lsearch.endtime = starttime + usetime if timed else sys.maxsize
            if self.debug:
                if timed:
                    print("# Time left: %3.2f s; Planing to think for %3.2f s" %
                          (self.clock[self.playingAs], usetime))
                else:
                    print("# Searching to depth %d without timelimit" % self.sd)

            for depth in range(1, self.sd + 1):
                # Heuristic time saving
                # Don't waste time, if the estimated isn't enough to complete
                # next depth
                if timed and usetime <= prevtime * 4 and usetime > 1:
                    break
                lsearch.timecheck_counter = lsearch.TIMECHECK_FREQ
                search_result = alphaBeta(self.board, depth)
                if lsearch.searching:
                    mvs, self.scr = search_result
                    if time() > lsearch.endtime:
                        break
                    if self.post:
                        pv1 = " ".join(listToSan(self.board, mvs))
                        time_cs = int(100 * (time() - starttime))
                        print("%s %s %s %s %s" % (
                            depth, self.scr, time_cs, lsearch.nodes, pv1))
                else:
                    # We were interrupted
                    if depth == 1:
                        mvs, self.scr = search_result
                    break
                prevtime = time() - starttime - prevtime

                self.clock[self.playingAs] -= time(
                ) - starttime - self.increment[self.playingAs]

            if not mvs:
                if not lsearch.searching:
                    # We were interupted
                    lsearch.nodes = 0
                    return

                # This should only happen in terminal mode

                if self.scr == 0:
                    print("result %s" % reprResult[DRAW])
                elif self.scr < 0:
                    if self.board.color == WHITE:
                        print("result %s" % reprResult[BLACKWON])
                    else:
                        print("result %s" % reprResult[WHITEWON])
                else:
                    if self.board.color == WHITE:
                        print("result %s" % reprResult[WHITEWON])
                    else:
                        print("result %s" % reprResult[BLACKWON])
                return

            lsearch.nodes = 0
            lsearch.searching = False

        move = mvs[0]
        sanmove = toSAN(self.board, move)
        if ondone:
            ondone(sanmove)
        return sanmove
Ejemplo n.º 17
0
    def __go (self, ondone=None):
        """ Finds and prints the best move from the current position """
        
        mv = self.__getBestOpening()
        if mv:
            mvs = [mv]
        
        if not mv:
               
            lsearch.skipPruneChance = self.skipPruneChance
            lsearch.searching = True
            
            timed = self.basetime > 0
            
            if self.searchtime > 0:
                usetime = self.searchtime
            else:
                usetime = self.clock[self.playingAs] / self.__remainingMovesA()
                if self.clock[self.playingAs] < 6*60+self.increment[self.playingAs]*40:
                    # If game is blitz, we assume 40 moves rather than 80
                    usetime *= 2
                # The increment is a constant. We'll use this always
                usetime += self.increment[self.playingAs]
                if usetime < 0.5:
                    # We don't wan't to search for e.g. 0 secs
                    usetime = 0.5

            prevtime = 0
            starttime = time()
            lsearch.endtime = starttime + usetime if timed else sys.maxint
            if self.debug:
                if timed:
                    print "# Time left: %3.2f s; Planing to think for %3.2f s" % (self.clock[self.playingAs], usetime)
                else:
                    print "# Searching to depth %d without timelimit" % self.sd

            for depth in range(1, self.sd+1):
                # Heuristic time saving
                # Don't waste time, if the estimated isn't enough to complete next depth
                if timed and usetime <= prevtime*4 and usetime > 1:
                    break
                lsearch.timecheck_counter = lsearch.TIMECHECK_FREQ
                search_result = alphaBeta(self.board, depth)
                if lsearch.searching:
                    mvs, self.scr = search_result
                    if time() > lsearch.endtime:
                        break
                    if self.post:
                        pv = " ".join(listToSan(self.board, mvs))
                        time_cs = int(100 * (time()-starttime))
                        print depth, self.scr, time_cs, lsearch.nodes, pv
                else:
                    # We were interrupted
                    if depth == 1:
                        mvs, self.scr = search_result
                    break
                prevtime = time()-starttime - prevtime
                
                self.clock[self.playingAs] -= time() - starttime - self.increment[self.playingAs]
            
            if not mvs:
                if not lsearch.searching:
                    # We were interupted
                    lsearch.nodes = 0
                    return
                
                # This should only happen in terminal mode
                
                if self.scr == 0:
                    print "result %s" % reprResult[DRAW]
                elif self.scr < 0:
                    if self.board.color == WHITE:
                        print "result %s" % reprResult[BLACKWON]
                    else: print "result %s" % reprResult[WHITEWON]
                else:
                    if self.board.color == WHITE:
                        print "result %s" % reprResult[WHITEWON]
                    else: print "result %s" % reprResult[BLACKWON]
                return
            
            lsearch.nodes = 0
            lsearch.searching = False
        
        move = mvs[0]
        sanmove = toSAN(self.board, move)
        if ondone: ondone(sanmove)
        return sanmove
Ejemplo n.º 18
0
def walk(node, result, model, save_emt=False, save_eval=False, vari=False):
    """Prepares a game data for .pgn storage.
       Recursively walks the node tree to collect moves and comments
       into a resulting movetext string.

       Arguments:
       node - list (a tree of lboards created by the pgn parser)
       result - str (movetext strings)"""

    while True:
        if node is None:
            break

        # Initial game or variation comment
        if node.prev is None:
            for child in node.children:
                if isinstance(child, str):
                    result.append("{%s}%s" % (child, os.linesep))
            node = node.next
            continue

        movecount = move_count(node,
                               black_periods=(save_emt or save_eval)
                               and "TimeControl" in model.tags)
        if movecount is not None:
            if movecount:
                result.append(movecount)
            move = node.lastMove
            result.append(toSAN(node.prev, move))
            if (save_emt or save_eval) and not vari:
                emt_eval = ""
                if "TimeControl" in model.tags and save_emt:
                    elapsed = model.timemodel.getElapsedMoveTime(
                        node.plyCount - model.lowply)
                    emt_eval = "[%%emt %s]" % formatTime(elapsed, clk2pgn=True)
                if node.plyCount in model.scores and save_eval:
                    moves, score, depth = model.scores[node.plyCount]
                    if node.color == BLACK:
                        score = -score
                    emt_eval += "[%%eval %0.2f/%s]" % (score / 100.0, depth)
                if emt_eval:
                    result.append("{%s}" % emt_eval)

        for nag in node.nags:
            if nag:
                result.append(nag)

        for child in node.children:
            if isinstance(child, str):
                # comment
                if child:
                    result.append("{%s}" % child)
            else:
                # variations
                if node.fen_was_applied:
                    result.append("(")
                    walk(child[0],
                         result,
                         model,
                         save_emt,
                         save_eval,
                         vari=True)
                    result.append(")")
                    # variation after last played move is not valid pgn
                    # but we will save it as in comment
                else:
                    result.append("{%s:" % _("Analyzer's primary variation"))
                    walk(child[0],
                         result,
                         model,
                         save_emt,
                         save_eval,
                         vari=True)
                    result.append("}")

        if node.next:
            node = node.next
        else:
            break
Ejemplo n.º 19
0
def defencive_moves_tactic(model, ply, phase):

    # ------------------------------------------------------------------------ #
    # Test if we threat something, or at least put more pressure on it         #
    # ------------------------------------------------------------------------ #

    # We set bishop value down to knight value, as it is what most people expect
    bishopBackup = PIECE_VALUES[BISHOP]
    PIECE_VALUES[BISHOP] = PIECE_VALUES[KNIGHT]

    board = model.getBoardAtPly(ply).board
    oldboard = model.getBoardAtPly(ply - 1).board
    move = model.getMoveAtPly(ply - 1).move
    fcord = FCORD(move)
    tcord = TCORD(move)

    found_threatens = []
    found_increases = []

    # What do we attack now?
    board.setColor(1 - board.color)
    for ncap in genCaptures(board):

        # getCaptures also generate promotions
        if FLAG(ncap) in PROMOTIONS:
            continue

        # We are only interested in the attacks of the piece we just moved
        if FCORD(ncap) != TCORD(move):
            continue

        # We don't want to move back
        if TCORD(ncap) == FCORD(move):
            continue

        # We don't thread the king. We check him! (in another function)
        if board.arBoard[TCORD(ncap)] == KING:
            continue

        # If we also was able to attack that cord last time, we don't care
        if validateMove(oldboard, newMove(FCORD(move), TCORD(ncap))):
            continue

        # Test if we threats our enemy, at least more than before
        see0 = staticExchangeEvaluate(oldboard, TCORD(ncap),
                                      1 - oldboard.color)
        see1 = staticExchangeEvaluate(board, TCORD(ncap), 1 - oldboard.color)
        if see1 > see0:

            # If a new winning capture has been created
            if see1 > 0:
                # Find the easiest attack
                attacks = getAttacks(board, TCORD(ncap), board.color)
                v, cord = min((PIECE_VALUES[board.arBoard[fc]], fc)
                              for fc in iterBits(attacks))
                easiestAttack = newMove(cord, TCORD(ncap))
                found_threatens.append(toSAN(board, easiestAttack, True))

            # Even though we might not yet be strong enough, we might still
            # have strengthened another friendly attack
            else:
                found_increases.append(reprCord[TCORD(ncap)])

    board.setColor(1 - board.color)

    # -------------------------------------------------------------------- #
    # Test if we defend a one of our pieces                                #
    # -------------------------------------------------------------------- #

    found_defends = []

    # Test which pieces were under attack
    used = []
    for ncap in genCaptures(board):

        # getCaptures also generate promotions
        if FLAG(ncap) in PROMOTIONS:
            continue

        # We don't want to know about the same cord more than once
        if TCORD(ncap) in used:
            continue
        used.append(TCORD(ncap))

        # If the attack was poining on the piece we just moved, we ignore it
        if TCORD(ncap) == FCORD(move) or TCORD(ncap) == TCORD(move):
            continue

        # If we were already defending the piece, we don't send a new
        # message
        if defends(oldboard, FCORD(move), TCORD(ncap)):
            continue

        # If the attack was not strong, we ignore it
        see = staticExchangeEvaluate(oldboard, ncap)
        if see < 0:
            continue

        v = defends(board, TCORD(move), TCORD(ncap))

        # If the defend didn't help, it doesn't matter. Like defending a
        # bishop, threatened by a pawn, with a queen.
        # But on the other hand - it might still be a defend...
        # newsee = staticExchangeEvaluate(board, ncap)
        # if newsee <= see: continue

        if v:
            found_defends.append(reprCord[TCORD(ncap)])

    # ------------------------------------------------------------------------ #
    # Test if we are rescuing an otherwise exposed piece                       #
    # ------------------------------------------------------------------------ #

    # Rescuing is only an option, if our own move wasn't an attack
    if oldboard.arBoard[tcord] == EMPTY:
        see0 = staticExchangeEvaluate(oldboard, fcord, oldboard.color)
        see1 = staticExchangeEvaluate(board, tcord, oldboard.color)
        if see1 > see0 and see1 > 0:
            yield _("rescues a %s") % reprPiece[board.arBoard[tcord]].lower()

    if found_threatens:
        yield _("threatens to win material by %s") % join(found_threatens)
    if found_increases:
        yield _("increases the pressure on %s") % join(found_increases)
    if found_defends:
        yield _("defends %s") % join(found_defends)

    PIECE_VALUES[BISHOP] = bishopBackup
Ejemplo n.º 20
0
def walk(node, result, model, vari=False):
    """Prepares a game data for .pgn storage.
       Recursively walks the node tree to collect moves and comments
       into a resulting movetext string.
       
       Arguments:
       node - list (a tree of lboards created by the pgn parser)
       result - str (movetext strings)"""

    enhanced_save = conf.get("enhanced_save_check", False)

    def store(text):
        if len(result) > 1 and result[-1] == "(":
            result[-1] = "(%s" % text
        elif text == ")":
            result[-1] = "%s)" % result[-1]
        else:
            result.append(text)

    while True:
        if node is None:
            break

        # Initial game or variation comment
        if node.prev is None:
            for child in node.children:
                if isinstance(child, basestring):
                    store("{%s}" % child)
            node = node.next
            continue

        movecount = move_count(node,
                               black_periods=enhanced_save
                               and "TimeControl" in model.tags)
        if movecount is not None:
            if movecount:
                store(movecount)
            move = node.lastMove
            store(toSAN(node.prev, move))
            if enhanced_save and not vari:
                emt_eval = ""
                if "TimeControl" in model.tags:
                    elapsed = model.timemodel.getElapsedMoveTime(
                        node.plyCount - model.lowply)
                    emt_eval = "[%%emt %s]" % formatTime(elapsed, clk2pgn=True)
                if node.plyCount in model.scores:
                    moves, score, depth = model.scores[node.plyCount]
                    emt_eval += "[%%eval %0.2f/%s]" % (score, depth)
                if emt_eval:
                    store("{%s}" % emt_eval)

        for nag in node.nags:
            if nag:
                store(nag)

        for child in node.children:
            if isinstance(child, basestring):
                child = re.sub("\[%.*?\]", "", child)
                # comment
                if child:
                    store("{%s}" % child)
            else:
                # variations
                if node.fen_was_applied:
                    store("(")
                    walk(child[0], result, model, vari=True)
                    store(")")
                    # variation after last played move is not valid pgn
                    # but we will save it as in comment
                else:
                    store("{Analyzer's primary variation:")
                    walk(child[0], result, model, vari=True)
                    store("}")

        if node.next:
            node = node.next
        else:
            break
Ejemplo n.º 21
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)
Ejemplo n.º 22
0
    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)
Ejemplo n.º 23
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)
Ejemplo n.º 24
0
    def perft(self, board, depth, prevmoves):
        if depth == 0:
            self.count += 1
            return

        if board.isChecked():
            # If we are checked we can use the genCheckEvasions function as well
            # as genAllMoves. Here we try both functions to ensure they return
            # the same result.
            nmoves = []
            for nmove in genAllMoves(board):
                board.applyMove(nmove)
                if board.opIsChecked():
                    board.popMove()
                    continue

                nmoves.append(nmove)
                board.popMove()
                # Validator test
                self.assertTrue(validateMove(board, nmove))

            cmoves = []

            for move in genCheckEvasions(board):
                board.applyMove(move)
                if board.opIsChecked():
                    board.popMove()
                    continue

                cmoves.append(move)
                board.popMove()
                # Validator test
                self.assertTrue(validateMove(board, move))

            # This is not any kind of alphaBeta sort. Only int sorting, to make
            # comparison possible
            nmoves.sort()
            cmoves.sort()

            if nmoves == cmoves:
                for move in cmoves:
                    prevmoves.append(toSAN(board, move))
                    board.applyMove(move)
                    self.perft(board, depth - 1, prevmoves)
                    board.popMove()
            else:
                print(board)
                print("nmoves")
                for move in nmoves:
                    print(toSAN(board, move))
                print("cmoves")
                for move in cmoves:
                    print(toSAN(board, move))
                self.assertEqual(nmoves, cmoves)

        else:
            for move in genAllMoves(board):
                board.applyMove(move)
                if board.opIsChecked():
                    board.popMove()
                    continue

                # Validator test
                board.popMove()
                self.assertTrue(validateMove(board, move))

                # San test
                san = toSAN(board, move)
                # print(san)
                try:
                    move2 = parseSAN(board, san)
                except ParsingError as e:
                    print(prevmoves)
                    raise ParsingError(e)
                self.assertEqual(move, move2)

                board.applyMove(move)

                self.perft(board, depth - 1, prevmoves)
                board.popMove()
Ejemplo n.º 25
0
def defencive_moves_tactic(model, ply, phase):

    # ------------------------------------------------------------------------ #
    # Test if we threat something, or at least put more pressure on it         #
    # ------------------------------------------------------------------------ #

    # We set bishop value down to knight value, as it is what most people expect
    bishopBackup = PIECE_VALUES[BISHOP]
    PIECE_VALUES[BISHOP] = PIECE_VALUES[KNIGHT]

    board = model.getBoardAtPly(ply).board
    oldboard = model.getBoardAtPly(ply - 1).board
    move = model.getMoveAtPly(ply - 1).move
    fcord = FCORD(move)
    tcord = TCORD(move)

    found_threatens = []
    found_increases = []

    # What do we attack now?
    board.setColor(1 - board.color)
    for ncap in genCaptures(board):

        # getCaptures also generate promotions
        if FLAG(ncap) in PROMOTIONS:
            continue

        # We are only interested in the attacks of the piece we just moved
        if FCORD(ncap) != TCORD(move):
            continue

        # We don't want to move back
        if TCORD(ncap) == FCORD(move):
            continue

        # We don't thread the king. We check him! (in another function)
        if board.arBoard[TCORD(ncap)] == KING:
            continue

        # If we also was able to attack that cord last time, we don't care
        if validateMove(oldboard, newMove(FCORD(move), TCORD(ncap))):
            continue

        # Test if we threats our enemy, at least more than before
        see0 = staticExchangeEvaluate(oldboard, TCORD(ncap),
                                      1 - oldboard.color)
        see1 = staticExchangeEvaluate(board, TCORD(ncap), 1 - oldboard.color)
        if see1 > see0:

            # If a new winning capture has been created
            if see1 > 0:
                # Find the easiest attack
                attacks = getAttacks(board, TCORD(ncap), board.color)
                v, cord = min((PIECE_VALUES[board.arBoard[fc]], fc)
                              for fc in iterBits(attacks))
                easiestAttack = newMove(cord, TCORD(ncap))
                found_threatens.append(toSAN(board, easiestAttack, True))

            # Even though we might not yet be strong enough, we might still
            # have strengthened another friendly attack
            else:
                found_increases.append(reprCord[TCORD(ncap)])

    board.setColor(1 - board.color)

    # -------------------------------------------------------------------- #
    # Test if we defend a one of our pieces                                #
    # -------------------------------------------------------------------- #

    found_defends = []

    # Test which pieces were under attack
    used = []
    for ncap in genCaptures(board):

        # getCaptures also generate promotions
        if FLAG(ncap) in PROMOTIONS:
            continue

        # We don't want to know about the same cord more than once
        if TCORD(ncap) in used:
            continue
        used.append(TCORD(ncap))

        # If the attack was poining on the piece we just moved, we ignore it
        if TCORD(ncap) == FCORD(move) or TCORD(ncap) == TCORD(move):
            continue

        # If we were already defending the piece, we don't send a new
        # message
        if defends(oldboard, FCORD(move), TCORD(ncap)):
            continue

        # If the attack was not strong, we ignore it
        see = staticExchangeEvaluate(oldboard, ncap)
        if see < 0:
            continue

        v = defends(board, TCORD(move), TCORD(ncap))

        # If the defend didn't help, it doesn't matter. Like defending a
        # bishop, threatened by a pawn, with a queen.
        # But on the other hand - it might still be a defend...
        # newsee = staticExchangeEvaluate(board, ncap)
        # if newsee <= see: continue

        if v:
            found_defends.append(reprCord[TCORD(ncap)])

    # ------------------------------------------------------------------------ #
    # Test if we are rescuing an otherwise exposed piece                       #
    # ------------------------------------------------------------------------ #

    # Rescuing is only an option, if our own move wasn't an attack
    if oldboard.arBoard[tcord] == EMPTY:
        see0 = staticExchangeEvaluate(oldboard, fcord, oldboard.color)
        see1 = staticExchangeEvaluate(board, tcord, oldboard.color)
        if see1 > see0 and see1 > 0:
            yield _("rescues a %s") % reprPiece[board.arBoard[tcord]].lower()

    if found_threatens:
        yield _("threatens to win material by %s") % join(found_threatens)
    if found_increases:
        yield _("increases the pressure on %s") % join(found_increases)
    if found_defends:
        yield _("defends %s") % join(found_defends)

    PIECE_VALUES[BISHOP] = bishopBackup