def _parse_game(game: Game) -> List[str]: positions: List[str] = [] board = game.board() for move in game.mainline_moves(): board.push(move) positions.append(board.fen()) return positions
def start_new_game(temporary=False): base = 'tmp' if temporary else str(int(time.time())) fn = os.path.extsep.join([base, 'pgn']) path = games_path(fn) _logger().info("Starting new game '{}'...".format(path)) game = Game() game.headers["Date"] = time.strftime("%Y.%m.%d") game.headers["White"] = "Human" game.headers["Black"] = "Raspberry Turk" _save_game(game, path) enter_game(fn)
def mate_in(game: Game) -> Optional[TagKind]: if not game.end().board().is_checkmate(): return None moves_to_mate = int(len(list(game.mainline_moves())) / 2) if moves_to_mate == 2: return "mateIn2" elif moves_to_mate == 2: return "mateIn2" elif moves_to_mate == 3: return "mateIn3" elif moves_to_mate == 4: return "mateIn4" elif moves_to_mate == 5: return "mateIn5" return "mateIn6+"
def get_puzzle(self, fen: str, prev_score: Score, move: str, current_score: Score, moves: str) -> None: board = Board(fen) game = Game.from_board(board) node = game.add_main_variation(Move.from_uci(move)) current_eval = PovScore(current_score, not board.turn) result = self.gen.analyze_position(node, prev_score, current_eval, tier=10) self.assert_is_puzzle_with_moves(result, [Move.from_uci(x) for x in moves.split()])
def learn(self, iters=100, c=10): """ Run the Q-learning algorithm. Play greedy on the final iter Args: iters: int amount of games to train c: int update the network every c games Returns: pgn (str) pgn string describing final game """ for k in range(iters): if k % c == 0: print("iter", k) self.agent.fix_model() greedy = True if k == iters - 1 else False self.env.reset() self.play_game(k, greedy=greedy) pgn = Game.from_board(self.env.board) reward_smooth = pd.DataFrame(self.reward_trace) reward_smooth.rolling(window=10, min_periods=0).mean().plot() return pgn
def analyze_game(server: Server, engine: SimpleEngine, game: Game, args: argparse.Namespace) -> Optional[Puzzle]: logger.debug("Analyzing game {}...".format(game.headers.get("Site"))) prev_score: Score = Cp(20) for node in game.mainline(): current_eval = node.eval() if not current_eval: logger.debug("Skipping game without eval on ply {}".format( node.ply())) return None result = analyze_position(server, engine, node, prev_score, current_eval, args) if isinstance(result, Puzzle): return result prev_score = -result logger.debug("Found nothing from {}".format(game.headers.get("Site"))) return None
def read(doc) -> Puzzle: board = Board(doc["fen"]) node = Game.from_board(board) for uci in doc["moves"]: move = Move.from_uci(uci) node = node.add_main_variation(move) return Puzzle(doc["_id"], node.game())
def not_puzzle(self, fen: str, prev_score: Score, move: str, current_score: Score) -> None: board = Board(fen) game = Game.from_board(board) node = game.add_main_variation(Move.from_uci(move)) current_eval = PovScore(current_score, not board.turn) result = self.gen.analyze_position( node, prev_score, current_eval, tier=10) self.assertIsInstance(result, Score)
def read(doc) -> Puzzle: board = Board(doc["fen"]) node: GameNode = Game.from_board(board) for uci in (doc["line"].split(' ') if "line" in doc else doc["moves"]): move = Move.from_uci(uci) node = node.add_main_variation(move) return Puzzle(doc["_id"], node.game(), int(doc["cp"]))
def clean(game: Game) -> None: for node in game.mainline(): for variation in reversed(node.variations): if not variation.is_mainline(): node.remove_variation(variation.move) node.nags = set() node.comment = ""
def analyze_game(self, game: Game, tier: int) -> Optional[Puzzle]: logger.debug(f'Analyzing tier {tier} {game.headers.get("Site")}...') prev_score: Score = Cp(20) seen_epds: Set[str] = set() board = game.board() skip_until_irreversible = False for node in game.mainline(): if skip_until_irreversible: if board.is_irreversible(node.move): skip_until_irreversible = False seen_epds.clear() else: board.push(node.move) continue current_eval = node.eval() if not current_eval: logger.debug("Skipping game without eval on ply {}".format( node.ply())) return None board.push(node.move) epd = board.epd() if epd in seen_epds: skip_until_irreversible = True continue seen_epds.add(epd) if board.castling_rights != maximum_castling_rights(board): continue result = self.analyze_position(node, prev_score, current_eval, tier) if isinstance(result, Puzzle): return result prev_score = -result logger.debug("Found nothing from {}".format(game.headers.get("Site"))) return None
def export(puzzle, include_first_move=True): fen = puzzle.last_pos.fen() board = chess.Board(fen) game = Game().from_board(board) result = PgnExporter.determine_result_tag(board) moves = puzzle.positions.move_list() if include_first_move: first_move = puzzle.last_move else: # simulate the move (blunder) board.push(puzzle.last_move) board.clear_stack() # take resulting board and create new game game = Game().from_board(board) first_move = Move.from_uci(moves.pop(0)) # start the line node = game.add_main_variation(first_move) # add the rest of the moves for m in moves: node = node.add_variation(Move.from_uci(m)) # copy headers from the original game and override result tag for h in puzzle.game.headers: game.headers[h] = puzzle.game.headers[h] game.headers['Result'] = result return str(game)
def export(self, pgn_headers=None) -> Game: """ pgn_headers - PGN headers to include in the exported PGN """ fen = self.puzzle.initial_board.fen() board = chess.Board(fen) game = Game().from_board(board) game_node = game game_node.comment = "score: %s -> %s" % (_score_to_str( self.puzzle.initial_score), _score_to_str(self.puzzle.final_score)) comment = self._candidate_moves_annotations(self.puzzle.analyzed_moves) for position in self.puzzle.positions: game_node = game_node.add_variation( chess.Move.from_uci(position.initial_move.uci())) if comment: game_node.comment = comment comment = self._candidate_moves_annotations( position.candidate_moves) if pgn_headers: for h in pgn_headers: if h == "FEN": continue game.headers[h] = pgn_headers[h] game.headers['PuzzleCategory'] = self.puzzle.category() puzzle_winner = self.puzzle.winner() if puzzle_winner: game.headers['PuzzleWinner'] = puzzle_winner game.headers['PuzzleEngine'] = AnalysisEngine.name() game.headers['PuzzleMakerVersion'] = __version__ return game
def pgn_game_to_serializable_game(pgn_game: pgn.Game) -> SerializableGame: current_index = 1 all_positions: List[SerializablePosition] = [] def parse_position(pgn_position: pgn.ChildNode, is_mainline: bool) -> SerializablePosition: nonlocal current_index nonlocal all_positions index = current_index current_index += 1 next_pos_index = None if pgn_position.variations: next_pos_index = parse_position(pgn_position.variations[0], True).index variations_indexes = [] if len(pgn_position.variations) > 1: variations_indexes = [ parse_position(p, False).index for i, p in enumerate(pgn_position.variations) if i > 0 ] position = SerializablePosition( index=index, next_position_index=next_pos_index, variations_indexes=variations_indexes, nags=list(pgn_position.nags), fen=pgn_position.board().fen(), comment=pgn_position.comment, commentBefore=pgn_position.starting_comment, san=pgn_position.san(), is_mainline=is_mainline, move=Move(from_square=square_name(pgn_position.move.from_square), to=square_name(pgn_position.move.to_square), promotion=pgn_position.move.promotion)) all_positions.insert(0, position) return position first_pos = SerializablePosition(index=0, fen=pgn_game.board().fen(), is_mainline=True, nags=[], san="", variations_indexes=[], next_position_index=1) all_positions.append(first_pos) for i, position in enumerate(pgn_game.variations): if i != 0: first_pos.variations_indexes.append(current_index) parse_position(position, i == 0) headers: Dict[str, str] = {} for key in pgn_game.headers.keys(): headers[key] = pgn_game.headers[key] game = SerializableGame( headers=headers, comment=pgn_game.starting_comment, positions=all_positions, ) return game
def advanced_pawn(game: Game) -> bool: for node in game.mainline(): if is_pawn_move(node): rank = square_rank(node.move.to_square) if rank > 5 and node.turn() == BLACK: return True if rank < 3 and node.turn() == WHITE: return True return False
def test_minimax(self): board = chess.Board() brain = MiniMax(self.umka) while not board.is_game_over(): move = brain.run(board, DEPTH) board.push(move) print(str(chess.pgn.Game().from_board(board)).split("\n\n")[1]) game = Game().from_board(board) print(game)
def configure_pgn(board): """Return a PGN representation of a completed chess game.""" pgn = Game.from_board(board) pgn.headers['Event'] = 'Eternal Chess' pgn.headers['Site'] = 'www.eternalchess.com' pgn.headers['Date'] = datetime.now().strftime(DATE_FORMAT) pgn.headers['Round'] = str(int(get_n_of_games()) + 1) pgn.headers['White'] = 'Random' pgn.headers['Black'] = 'Random' return str(pgn)
def play(brain): board = chess.Board() while not board.is_game_over(): move = brain.make_move(board, time_to_think=15 * 100) print(move, brain.best_val, brain.best_move, brain.root_moves) board.push(move) pprint.pprint(str(board)) print(str(chess.pgn.Game().from_board(board)).split("\n\n")[1]) game = Game().from_board(board) print(game)
def generate_pgn(self, game: Game): """ Gets PGN for given game :param game: Current game :type game: Game :return: Bot message formatted with PGN :rtype: str """ chess_game = ChessGame() chess_game.headers["White"] = str(game.player1.name) chess_game.headers["Black"] = str(game.player2.name) chess_game.headers["Result"] = str(game.result) last_node = chess_game for move in game.board.move_stack: last_node = last_node.add_variation(move) result = f"```\n{str(chess_game)}\n```" if game.id: result += f'Game id: `{game.id}`' return result
def classify(game: Game) -> int: """Attempt to classify the opening in a PGN game. Args: game (chess.pgn.Game): The game to classify. Returns: int: The ply count of the last book move. If no opening is identified, returns 0. """ epds = list( enumerate((node.board().epd() for node in game.mainline()), start=0)) with open("data/eco.tsv") as eco: reader = csv.DictReader(eco, delimiter="\t") for opening in reader: for ply, epd in reversed(epds): if epd == opening["fen"]: game.headers["ECO"] = opening["eco"] game.headers["Opening"] = opening["name"] return ply return 0
def parse_pgn_game(pgn: Game): """ Parse a PGN game object and add it and its moves to DB """ print('Parsing PGN game...') game_id = bson.ObjectId() def is_int(var: str) -> bool: """ Converter for ELO strings """ try: int(var) except ValueError: return False return True db_game = { '_id': game_id, 'date': pgn.headers['Date'] if 'Date' in pgn.headers else '???', 'white': pgn.headers['White'] if 'White' in pgn.headers else '???', 'white_elo': int(pgn.headers['WhiteElo']) if ( 'WhiteElo' in pgn.headers and is_int(pgn.headers['WhiteElo'])) else 0, 'black': pgn.headers['Black'] if 'Black' in pgn.headers else '???', 'black_elo': int(pgn.headers['BlackElo']) if ( 'BlackElo' in pgn.headers and is_int(pgn.headers['BlackElo'])) else 0, 'result': pgn.headers['Result'] if 'Result' in pgn.headers else '???' } if db.games.find_one({k: v for k, v in db_game.items() if k != '_id'}): print('SKIPPED!') return print('Inserting:', db_game) db.games.insert_one(db_game) white_theory = db_game['white_elo'] >= 2500 black_theory = db_game['black_elo'] >= 2500 turn = True b = chess.Board() for i, move in enumerate(pgn.mainline_moves()): san = b.san(move) b.push(move) db_move = { 'leads_to': b.fen(), 'uci': move.uci(), 'san': san, 'score_diff': None } b.pop() # ugly, but we need the original position if (turn and white_theory) or (not turn and black_theory): idatabase.insert_board(b.fen(), [db_move], [], game_id if i > 5 else None) else: idatabase.insert_board(b.fen(), [], [db_move], game_id if i > 5 else None) b.push(move) # so now we put it back turn = not turn if i > database.MAX_DEPTH: break
def reportpgn(self): rootboard = self.getrootboard() game = Game() game.setup(rootboard) game.comment = self.rootnode().comment() game = self.addmovesrecursive(self.rootnode(), game) exporter = StringExporter(headers=True, variations=True, comments=True) pgn = game.accept(exporter) return pgn
def analyze_game(engine: SimpleEngine, game: Game) -> Optional[Tuple[GameNode, List[Move], Kind]]: """ Evaluate the moves in a game looking for puzzles """ game_url = game.headers.get("Site", "?") logger.debug("Analyzing game {}...".format(game_url)) prev_score: Score = Cp(0) for node in game.mainline(): ev = node.eval() if not ev: logger.debug("Skipping game without eval on move {}".format( node.board().fullmove_number)) return None winner = node.board().turn score = ev.pov(winner) # was the opponent winning until their last move if prev_score > Cp(-300) or not is_down_in_material( node.board(), winner): pass elif mate_soon < score < Mate(1): logger.info("Mate found on {}#{}. Probing...".format( game_url, ply_of(node.board()))) solution = cook_mate(engine, node, winner) if solution is not None: return node, solution, Kind("mate") elif score > juicy_advantage: logger.info("Advantage found on {}#{}. Probing...".format( game_url, ply_of(node.board()))) solution = cook_advantage(engine, node, winner) if solution is not None: return node, solution, Kind("mate") else: print(score) prev_score = score return None
def main() -> None: sys.setrecursionlimit(10000) # else node.deepcopy() sometimes fails? parser = argparse.ArgumentParser(prog='tagger.py', description='automatically tags lichess puzzles') parser.add_argument("--verbose", "-v", help="increase verbosity", action="count") args = parser.parse_args() if args.verbose == 1: logger.setLevel(logging.DEBUG) mongo = pymongo.MongoClient() db = mongo['puzzler'] puzzle_coll = db['puzzle2'] tag_coll = db['tag'] for puzzle in puzzle_coll.find(): # prev = tag_coll.find_one({"_id":puzzle._id}) board = Board(puzzle["fen"]) node = Game.from_board(board) for uci in puzzle["moves"]: move = Move.from_uci(uci) node = node.add_main_variation(move) puzzle = Puzzle(puzzle["_id"], node.game()) tags = cook.cook(puzzle) for tag in tags: tag_coll.update_one({"_id":puzzle.id},{"$addToSet":{tag: "lichess"}}, upsert = True)
def learn(self, iters=100, c=10): """ Run the Q-learning algorithm. Play greedy on the final iter Args: iters: int amount of games to train c: int update the network every c games Returns: pgn (str) pgn string describing final game """ for k in range(iters): self.env.reset() states, actions, rewards, action_spaces = self.play_game(k) self.reinforce_agent(states, actions, rewards, action_spaces) pgn = Game.from_board(self.env.board) reward_smooth = pd.DataFrame(self.reward_trace) reward_smooth.rolling(window=10, min_periods=0).mean().plot() return pgn
import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv) import matplotlib.pyplot as plt import os from RLC.real_chess import agent, environment, learn, tree import chess from chess.pgn import Game opponent = agent.GreedyAgent() env = environment.Board(opponent, FEN=None) player = agent.Agent(lr=0.001, network='big') player.fix_model() learner = learn.TD_search(env, player, gamma=0.8, search_time=1.5) node = tree.Node(learner.env.board, gamma=learner.gamma) w_before = learner.agent.model.get_weights() n_iters = 105 print(opponent.predict(np.expand_dims(env.layer_board, axis=0))) learner.search_time = 60 learner.play_game(n_iters) pgn = Game.from_board(learner.env.board) with open("rlc_pgn","w") as log: log.write(str(pgn))