def test_gating_castle_at_rook_wOO(self): FEN = "r2qk2r/pppbbppp/4pn2/1N1p4/1n1P4/4PN2/PPPBBPPP/R2QK2R[heHE] w KQkq - 10 8" board = LBoard(SCHESS) board.applyFen(FEN) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) print("--------") print(board) self.assertIn("h1e1h", moves) self.assertIn("h1e1e", moves) self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O/Hh1'))), 'h1e1h') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O/Eh1'))), 'h1e1e') self.assertEqual(toSAN(board, newMove(H1, E1, HAWK_GATE_AT_ROOK)), "O-O/Hh1") self.assertEqual(toSAN(board, newMove(H1, E1, ELEPHANT_GATE_AT_ROOK)), "O-O/Eh1") fen = board.asFen() move = parseAN(board, "h1e1e") board.applyMove(move) print("h1e1e") print(board) self.assertEqual(placement(board.asFen()), "r2qk2r/pppbbppp/4pn2/1N1p4/1n1P4/4PN2/PPPBBPPP/R2Q1RKE[heH]") board.popMove() self.assertEqual(placement(board.asFen()), placement(fen))
def test_toSAN(self): """ Testing toSAN() with giveaway king move """ board = LBoard(GIVEAWAYCHESS) board.applyFen("4R3/8/7B/2P5/8/8/PP5k/6k1 b - - 0 28") self.assertEqual(toSAN(board, newMove(G1, G2)), "Kgg2") self.assertEqual(toSAN(board, newMove(H2, G2)), "Khg2")
def test_gating_san_move(self): board = LBoard(SCHESS) board.applyFen(SCHESSSTART) self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/H'))), 'g1f3h') self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/E'))), 'g1f3e') self.assertEqual(toSAN(board, newMove(G1, F3, HAWK_GATE)), "Nf3/H") self.assertEqual(toSAN(board, newMove(G1, F3, ELEPHANT_GATE)), "Nf3/E")
def test_gating_castle_at_rook_wOOO_SAN(self): FEN = "r2ek2r/5pp1/1pp1pnp1/2pp1h2/3P4/2P1PP2/PP1N2PP/R3K1HR/E w KQHEAkq - 2 16" board = LBoard(SCHESS) board.applyFen(FEN) self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O'))), 'e1c1') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ea1'))), 'a1e1e') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ee1'))), 'e1c1e') self.assertEqual(toSAN(board, newMove(E1, C1, QUEEN_CASTLE)), "O-O-O") self.assertEqual(toSAN(board, newMove(A1, E1, ELEPHANT_GATE_AT_ROOK)), "O-O-O/Ea1") self.assertEqual(toSAN(board, newMove(E1, C1, ELEPHANT_GATE)), "O-O-O/Ee1")
def 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
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)
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
def update_tree(self, load_games=True): self.persp.gamelist.ply = self.board.plyCount if load_games and self.filtered: self.persp.chessfile.set_fen_filter(self.board.asFen()) self.persp.gamelist.load_games() result = self.persp.chessfile.get_book_moves(self.board.asFen()) self.clear_tree() for move, count, white_won, blackwon, draw in result: lmove = parseAN(self.board, move) perf = 0 if not count else round( (white_won * 100. + draw * 50.) / count) self.liststore.append( [lmove, toSAN(self.board, lmove), count, perf])
def 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
def update_tree(self, load_games=True): bb = self.board.friends[0] | self.board.friends[1] self.gamelist.ply = self.board.plyCount self.gamelist.chessfile.build_where_bitboards(self.board.plyCount, bb) self.gamelist.offset = 0 self.gamelist.chessfile.build_query() if load_games and self.filtered: self.gamelist.load_games() bb_candidates = {} for lmove in genAllMoves(self.board): self.board.applyMove(lmove) if self.board.opIsChecked(): self.board.popMove() continue bb_candidates[self.board.friends[0] | self.board.friends[1]] = lmove self.board.popMove() result = [] # print("get_bitboards() for %s bb_candidates" % len(bb_candidates)) bb_list = self.gamelist.chessfile.get_bitboards( self.board.plyCount + 1, bb_candidates) for bb, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg in bb_list: try: result.append((bb_candidates[bb], count, white_won, blackwon, draw, white_elo_avg, black_elo_avg)) # print("OK ", bb, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg) except KeyError: # print("KeyError", bb, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg) pass self.clear_tree() for lmove, count, white_won, blackwon, draw, white_elo_avg, black_elo_avg in result: perf = 0 if not count else round( (white_won * 100. + draw * 50.) / count) elo_avg = white_elo_avg if self.board.color == WHITE else black_elo_avg self.liststore.append( [lmove, toSAN(self.board, lmove), count, perf, elo_avg])
def 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
def run(self): while True: try: line = raw_input() except EOFError: line = "quit" lines = line.split() try: if not lines: continue log.debug(line, extra={"task": "xboard"}) ########## CECP commands ########## # See http://home.hccnet.nl/h.g.muller/engine-intf.html if lines[0] == "xboard": pass elif lines[0] == "protover": stringPairs = [ "=".join( [k, '"%s"' % v if isinstance(v, str) else str(v)]) for k, v in self.features.items() ] self.print("feature %s" % " ".join(stringPairs)) self.print("feature done=1") elif lines[0] in ("accepted", "rejected"): # We only really care about one case: if tuple(lines) == ("rejected", "debug"): self.debug = False elif lines[0] == "new": self.__stopSearching() self.board = LBoard(NORMALCHESS) self.board.applyFen(FEN_START) self.outOfBook = False self.forced = False self.playingAs = BLACK self.clock[:] = self.basetime, self.basetime self.searchtime = 0 self.sd = MAXPLY if self.analyzing: self.__analyze() elif lines[0] == "variant": if len(lines) > 1: if lines[1] == "fischerandom": self.board.variant = FISCHERRANDOMCHESS elif lines[1] == "crazyhouse": self.board.variant = CRAZYHOUSECHESS self.board.iniHouse() elif lines[1] == "wildcastle": self.board.variant = WILDCASTLESHUFFLECHESS elif lines[1] == "losers": self.board.variant = LOSERSCHESS elif lines[1] == "suicide": self.board.variant = SUICIDECHESS elif lines[1] == "atomic": self.board.variant = ATOMICCHESS self.board.iniAtomic() elif lines[1] == "3check": self.board.variant = THREECHECKCHESS elif lines[1] == "kingofthehill": self.board.variant = KINGOFTHEHILLCHESS self.print("setup (PNBRQKpnbrqk) 8x8+0_fairy %s" % FEN_START) elif lines[1] == "asean": self.board = LBoard(ASEANCHESS) self.board.applyFen(ASEANSTART) elif lines[1] == "makruk": self.board = LBoard(MAKRUKCHESS) self.board.applyFen(MAKRUKSTART) elif lines[1] == "cambodian": self.board = LBoard(CAMBODIANCHESS) self.board.applyFen(KAMBODIANSTART) self.print( "setup (PN.R.M....SKpn.r.m....sk) 8x8+0_makruk %s" % KAMBODIANSTART) self.print("piece K& KiN") self.print("piece M& FifD") elif lines[1] == "sittuyin": self.board = LBoard(SITTUYINCHESS) self.board.applyFen(SITTUYINSTART) self.print( "setup (PN.R.F....SKpn.r.f....sk) 8x8+6_bughouse %s" % SITTUYINSTART) self.print("piece N& Nj@3") self.print("piece S& FfWj@3") self.print("piece F& Fjb@3") self.print("piece R& R@1") self.print("piece K& Kj@3") self.print("piece P& fmWfcFj@3") elif lines[0] == "quit": self.forced = True self.__stopSearching() sys.exit(0) elif lines[0] == "random": leval.random = True elif lines[0] == "force": if not self.forced and not self.analyzing: self.forced = True self.__stopSearching() elif lines[0] == "go": self.playingAs = self.board.color self.forced = False self.__go() elif lines[0] == "playother": self.playingAs = 1 - self.board.color self.forced = False # TODO: start pondering, if possible elif lines[0] in ("black", "white"): newColor = lines[0] == "black" and BLACK or WHITE self.__stopSearching() self.playingAs = 1 - newColor if self.board.color != newColor: self.board.setColor(newColor) self.board.setEnpassant(None) if self.analyzing: self.__analyze() elif lines[0] == "level": self.movestogo = int(lines[1]) inc = int(lines[3]) minutes = lines[2].split(":") # Per protocol spec, strip off any non-numeric suffixes. for i in range(len(minutes)): minutes[i] = re.match(r'\d*', minutes[i]).group() self.basetime = int(minutes[0]) * 60 if len(minutes) > 1 and minutes[1]: self.basetime += int(minutes[1]) self.clock[:] = self.basetime, self.basetime self.increment = inc, inc elif lines[0] == "st": self.searchtime = float(lines[1]) elif lines[0] == "sd": self.sd = int(lines[1]) # Unimplemented: nps elif lines[0] == "time": self.clock[self.playingAs] = float(lines[1]) / 100. elif lines[0] == "otim": self.clock[1 - self.playingAs] = float(lines[1]) / 100. elif lines[0] == "usermove": self.__stopSearching() try: move = parseAny(self.board, lines[1]) except ParsingError as e: self.print("Error (unknown command): %s" % lines[1]) self.print(self.board) continue if not validateMove(self.board, move): self.print("Illegal move: %s" % lines[1]) self.print(self.board) continue self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() elif lines[0] == "?": if not self.forced and not self.analyzing: self.__stopSearching() elif lines[0] == "ping": self.print("pong %s" % lines[1]) elif lines[0] == "draw": if self.__willingToDraw(): self.print("offer draw") elif lines[0] == "result": # We don't really care what the result is at the moment. pass elif lines[0] == "setboard": self.__stopSearching() try: self.board = LBoard(self.board.variant) fen = " ".join(lines[1:]) self.board.applyFen( fen.replace("[", "/").replace("]", "")) except SyntaxError as e: self.print("tellusererror Illegal position: %s" % str(e)) # "edit" is unimplemented. See docs. Exiting edit mode returns to analyze mode. elif lines[0] == "hint": pass # TODO: Respond "Hint: MOVE" if we have an expected reply elif lines[0] == "bk": entries = getOpenings(self.board) if entries: totalWeight = sum(entry[1] for entry in entries) for entry in entries: self.print("\t%s\t%02.2f%%" % (toSAN(self.board, entry[0]), entry[1] * 100.0 / totalWeight)) elif lines[0] == "undo": self.__stopSearching() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] == "remove": self.__stopSearching() self.board.popMove() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] in ("hard", "easy"): self.ponder = (lines[0] == "hard") elif lines[0] in ("post", "nopost"): self.post = (lines[0] == "post") elif lines[0] == "analyze": self.analyzing = True self.__analyze() elif lines[0] in ("name", "rating", "ics", "computer"): pass # We don't care. # Unimplemented: pause, resume elif lines[0] == "memory": # FIXME: this is supposed to control the *total* memory use. if lsearch.searching: self.print("Error (already searching):", line) else: limit = int(lines[1]) if limit < 1: self.print("Error (limit too low):", line) else: pass # TODO implement #lsearch.setHashSize(limit) elif lines[0] == "cores": pass # We aren't SMP-capable. elif lines[0] == "egtpath": if len(lines) >= 3 and lines[1] == "gaviota": conf.set("egtb_path", conf.get("egtb_path", lines[2])) from pychess.Utils.lutils.lsearch import enableEGTB enableEGTB() elif lines[0] == "option" and len(lines) > 1: name, eq, value = lines[1].partition("=") if value: value = int( value ) # CECP spec says option values are *always* numeric if name == "skipPruneChance": if 0 <= value <= 100: self.skipPruneChance = value / 100.0 else: self.print( "Error (argument must be an integer 0..100): %s" % line) ########## CECP analyze mode commands ########## # See http://www.gnu.org/software/xboard/engine-intf.html#11 elif lines[0] == "exit": if self.analyzing: self.__stopSearching() self.analyzing = False # Periodic updates (".") are not implemented. ########## Custom commands ########## elif lines[0] == "moves": self.print(self.board) self.print([ toSAN(self.board, move) for move in genAllMoves(self.board) ]) elif lines[0] == "captures": self.print(self.board) self.print([ toSAN(self.board, move) for move in genCaptures(self.board) ]) elif lines[0] == "evasions": self.print(self.board) self.print([ toSAN(self.board, move) for move in genCheckEvasions(self.board) ]) elif lines[0] == "benchmark": benchmark() elif lines[0] == "profile": if len(lines) > 1: import cProfile cProfile.runctx("benchmark()", locals(), globals(), lines[1]) else: self.print("Usage: profile outputfilename") elif lines[0] == "perft": root = "0" if len(lines) < 3 else lines[2] depth = "1" if len(lines) == 1 else lines[1] if root.isdigit() and depth.isdigit(): perft(self.board, int(depth), int(root)) else: self.print("Error (arguments must be integer") elif len(lines) == 1: # A GUI without usermove support might try to send a move. try: move = parseAny(self.board, line) except: self.print("Error (unknown command): %s" % line) continue if not validateMove(self.board, move): self.print("Illegal move: %s" % lines[0]) self.print(self.board) continue self.__stopSearching() self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() else: self.print("Error (unknown command): %s" % line) except IndexError: self.print("Error (missing argument): %s" % line)
def 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
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
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()
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
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
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
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
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
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)
def run(self): while True: try: line = get_input() except EOFError: line = "quit" lines = line.split() try: if not lines: continue log.debug(line, extra={"task": "xboard"}) # CECP commands # See http://home.hccnet.nl/h.g.muller/engine-intf.html if lines[0] == "xboard": pass elif lines[0] == "protover": stringPairs = ["=".join([k, '"%s"' % v if isinstance( v, str) else str(v)]) for k, v in self.features.items()] self.print("feature %s" % " ".join(stringPairs)) self.print("feature done=1") elif lines[0] in ("accepted", "rejected"): # We only really care about one case: if tuple(lines) == ("rejected", "debug"): self.debug = False elif lines[0] == "new": self.__stopSearching() self.board = LBoard(NORMALCHESS) self.board.applyFen(FEN_START) self.outOfBook = False self.forced = False self.playingAs = BLACK self.clock[:] = self.basetime, self.basetime self.searchtime = 0 self.sd = MAXPLY if self.analyzing: self.__analyze() elif lines[0] == "variant": if len(lines) > 1: if lines[1] == "fischerandom": self.board.variant = FISCHERRANDOMCHESS elif lines[1] == "crazyhouse": self.board.variant = CRAZYHOUSECHESS self.board.iniHouse() elif lines[1] == "wildcastle": self.board.variant = WILDCASTLESHUFFLECHESS elif lines[1] == "losers": self.board.variant = LOSERSCHESS elif lines[1] == "suicide": self.board.variant = SUICIDECHESS elif lines[1] == "giveaway": self.board.variant = GIVEAWAYCHESS elif lines[1] == "atomic": self.board.variant = ATOMICCHESS self.board.iniAtomic() elif lines[1] == "3check": self.board.variant = THREECHECKCHESS elif lines[1] == "kingofthehill": self.board.variant = KINGOFTHEHILLCHESS self.print("setup (PNBRQKpnbrqk) 8x8+0_fairy %s" % FEN_START) elif lines[1] == "horde": self.board = LBoard(HORDECHESS) self.board.applyFen(HORDESTART) elif lines[1] == "asean": self.board = LBoard(ASEANCHESS) self.board.applyFen(ASEANSTART) elif lines[1] == "makruk": self.board = LBoard(MAKRUKCHESS) self.board.applyFen(MAKRUKSTART) elif lines[1] == "cambodian": self.board = LBoard(CAMBODIANCHESS) self.board.applyFen(KAMBODIANSTART) self.print( "setup (PN.R.M....SKpn.r.m....sk) 8x8+0_makruk %s" % KAMBODIANSTART) self.print("piece K& KiN") self.print("piece M& FifD") elif lines[1] == "sittuyin": self.board = LBoard(SITTUYINCHESS) self.board.applyFen(SITTUYINSTART) self.print( "setup (PN.R.F....SKpn.r.f....sk) 8x8+6_bughouse %s" % SITTUYINSTART) self.print("piece N& Nj@3") self.print("piece S& FfWj@3") self.print("piece F& Fjb@3") self.print("piece R& R@1") self.print("piece K& Kj@3") self.print("piece P& fmWfcFj@3") elif lines[0] == "quit": self.forced = True self.__stopSearching() sys.exit(0) elif lines[0] == "random": leval.random = True elif lines[0] == "force": if not self.forced and not self.analyzing: self.forced = True self.__stopSearching() elif lines[0] == "go": self.playingAs = self.board.color self.forced = False self.__go() elif lines[0] == "playother": self.playingAs = 1 - self.board.color self.forced = False # TODO: start pondering, if possible elif lines[0] in ("black", "white"): newColor = lines[0] == "black" and BLACK or WHITE self.__stopSearching() self.playingAs = 1 - newColor if self.board.color != newColor: self.board.setColor(newColor) self.board.setEnpassant(None) if self.analyzing: self.__analyze() elif lines[0] == "level": self.movestogo = int(lines[1]) inc = int(lines[3]) minutes = lines[2].split(":") # Per protocol spec, strip off any non-numeric suffixes. for i in range(len(minutes)): minutes[i] = re.match(r'\d*', minutes[i]).group() self.basetime = int(minutes[0]) * 60 if len(minutes) > 1 and minutes[1]: self.basetime += int(minutes[1]) self.clock[:] = self.basetime, self.basetime self.increment = inc, inc elif lines[0] == "st": self.searchtime = float(lines[1]) elif lines[0] == "sd": self.sd = int(lines[1]) # Unimplemented: nps elif lines[0] == "time": self.clock[self.playingAs] = float(lines[1]) / 100. elif lines[0] == "otim": self.clock[1 - self.playingAs] = float(lines[1]) / 100. elif lines[0] == "usermove": self.__stopSearching() try: move = parseAny(self.board, lines[1]) except ParsingError as err: self.print("Error (unknown command): %s" % lines[1]) self.print(self.board.prepr(ascii=ASCII)) continue if not validateMove(self.board, move): self.print("Illegal move: %s" % lines[1]) self.print(self.board.prepr(ascii=ASCII)) continue self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() elif lines[0] == "?": if not self.forced and not self.analyzing: self.__stopSearching() elif lines[0] == "ping": self.print("pong %s" % lines[1]) elif lines[0] == "draw": if self.__willingToDraw(): self.print("offer draw") elif lines[0] == "result": # We don't really care what the result is at the moment. pass elif lines[0] == "setboard": self.__stopSearching() try: self.board = LBoard(self.board.variant) fen = " ".join(lines[1:]) self.board.applyFen(fen.replace("[", "/").replace("]", "")) except SyntaxError as err: self.print("tellusererror Illegal position: %s" % str(err)) # "edit" is unimplemented. See docs. Exiting edit mode returns to analyze mode. elif lines[0] == "hint": pass # TODO: Respond "Hint: MOVE" if we have an expected reply elif lines[0] == "bk": entries = getOpenings(self.board) if entries: totalWeight = sum(entry[1] for entry in entries) for entry in entries: self.print("\t%s\t%02.2f%%" % (toSAN(self.board, entry[0]), entry[1] * 100.0 / totalWeight)) elif lines[0] == "undo": self.__stopSearching() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] == "remove": self.__stopSearching() self.board.popMove() self.board.popMove() if self.analyzing: self.__analyze() elif lines[0] in ("hard", "easy"): self.ponder = (lines[0] == "hard") elif lines[0] in ("post", "nopost"): self.post = (lines[0] == "post") elif lines[0] == "analyze": self.analyzing = True self.__analyze() elif lines[0] in ("name", "rating", "ics", "computer"): pass # We don't care. # Unimplemented: pause, resume elif lines[0] == "memory": # FIXME: this is supposed to control the *total* memory use. if lsearch.searching: self.print("Error (already searching):", line) else: limit = int(lines[1]) if limit < 1: self.print("Error (limit too low):", line) else: pass # TODO implement # lsearch.setHashSize(limit) elif lines[0] == "cores": pass # We aren't SMP-capable. elif lines[0] == "egtpath": if len(lines) >= 3 and lines[1] == "gaviota": conf.set("egtb_path", conf.get("egtb_path", lines[2])) from pychess.Utils.lutils.lsearch import enableEGTB enableEGTB() elif lines[0] == "option" and len(lines) > 1: name, eq, value = lines[1].partition("=") if value: value = int( value ) # CECP spec says option values are *always* numeric if name == "skipPruneChance": if 0 <= value <= 100: self.skipPruneChance = value / 100.0 else: self.print( "Error (argument must be an integer 0..100): %s" % line) # CECP analyze mode commands # See http://www.gnu.org/software/xboard/engine-intf.html#11 elif lines[0] == "exit": if self.analyzing: self.__stopSearching() self.analyzing = False # Periodic updates (".") are not implemented. # Custom commands elif lines[0] == "moves": self.print(self.board.prepr(ascii=ASCII)) self.print([toSAN(self.board, move) for move in genAllMoves(self.board)]) elif lines[0] == "captures": self.print(self.board.prepr(ascii=ASCII)) self.print([toSAN(self.board, move) for move in genCaptures(self.board)]) elif lines[0] == "evasions": self.print(self.board.prepr(ascii=ASCII)) self.print([toSAN(self.board, move) for move in genCheckEvasions(self.board)]) elif lines[0] == "benchmark": if len(lines) > 1: benchmark(int(lines[1])) else: benchmark() elif lines[0] == "profile": if len(lines) > 1: import cProfile cProfile.runctx("benchmark()", locals(), globals(), lines[1]) else: self.print("Usage: profile outputfilename") elif lines[0] == "perft": root = "0" if len(lines) < 3 else lines[2] depth = "1" if len(lines) == 1 else lines[1] if root.isdigit() and depth.isdigit(): perft(self.board, int(depth), int(root)) else: self.print("Error (arguments must be integer") elif lines[0] == "stop_unittest": break elif len(lines) == 1: # A GUI without usermove support might try to send a move. try: move = parseAny(self.board, line) except ParsingError: self.print("Error (unknown command): %s" % line) continue if not validateMove(self.board, move): self.print("Illegal move: %s" % lines[0]) self.print(self.board.prepr(ascii=ASCII)) continue self.__stopSearching() self.board.applyMove(move) self.playingAs = self.board.color if not self.forced and not self.analyzing: self.__go() if self.analyzing: self.__analyze() else: self.print("Error (unknown command): %s" % line) except IndexError: self.print("Error (missing argument): %s" % line)
def 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)
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()