def test_free_rows_and_columns(self): b = Board(2,2) self.assertEquals(free_rows_and_columns(b.data),(2,2)) b2 = b.add_piece([("N",0,0)]) self.assertEquals(free_rows_and_columns(b2.data),(1,1)) b3 = b2.add_piece([("N",0,1)]) self.assertEquals(free_rows_and_columns(b3.data),(1,0), b3)
def repertoire(id=1): if request.args.get('practice'): print(practice(id)) board = Board() form = MoveForm() if request.method == 'GET': position = Position.query.filter_by(id=id).first() moves = Move.query.join(User.moves).filter(User.id == current_user.id, Move.source_position_id == position.id).all() return render_template('tools/repertoire.html', fen=position.fen + ' 0 1', moves=moves, form=form, id=id) if request.method == 'POST': fen = form.append_fen.data san = form.append_san.data quality_id = form.quality.data board = Board(fen) board.push_san(san) source_position = Position.query.filter_by(fen=' '.join(fen.split()[:-2])).first() end_position_fen = ' '.join(board.fen().split()[:-2]) end_position = Position.query.filter_by(fen=end_position_fen).first() if end_position is None: end_position = Position(fen=end_position_fen) db.session.add(end_position) move = Move.query.filter_by(source_position_id=source_position.id, destination_position_id=end_position.id).first() if move is None: move = Move(source_position_id=source_position.id, destination_position_id=end_position.id, san=san) um = UserMove(quality_id=quality_id) um.move = move current_user.moves.append(um) db.session.commit() moves = Move.query.join(User.moves).filter(User.id == current_user.id, Move.source_position_id == source_position.id).all() return render_template('tools/repertoire.html', fen=source_position.fen + ' 0 1', moves=moves, form=form, id=id)
def display_move_on_clock(self, move, fen, side, beep=False, left_dots=0, right_dots=0): bit_board = Board(fen) move_text = bit_board.san(move) if side == ClockSide.RIGHT: move_text = move_text.rjust(11) text = self.dgttranslate.move(move_text) self._display_on_dgt_pi(text, beep, left_dots, right_dots)
def pv_to_san(self): if(self.san_arr == None): return "" else: try: pv_san = [] board = Board(self.san_arr[0]) moves = self.san_arr[1] for uci in moves: move = Move.from_uci(uci) if(move in board.pseudo_legal_moves): pv_san.append(board.san(move)) board.push(move) if(len(pv_san) > 0): s = "" white_moves = True move_no = (self.no_game_halfmoves//2)+1 if(self.no_game_halfmoves % 2 == 1): white_moves = False s += str(move_no)+". ... " move_no += 1 for san in pv_san: if(white_moves): s += " "+str(move_no)+". "+san move_no +=1 else: s += " "+san white_moves = not white_moves return s else: return "" except ValueError: return ""
def display_move_on_clock(self, move, fen, beep=False): if self.enable_dgt_3000: bit_board = Board(fen) text = self.dgttranslate.move(bit_board.san(move)) self._display_on_dgt_3000(text, beep) else: text = ' ' + move.uci() self._display_on_dgt_xl(text, beep)
def test_eq(self): a = Board(2,2) b = Board(2,2) c = a.add_piece([("N",0,0)]) self.assertTrue(a == a) self.assertTrue(a == b) self.assertTrue(b == a) self.assertTrue(a != c)
def test_rotations(self): b = Board(2,2) #.add_piece([("N",0,0)]) print b.__dict__ rots = list(b.rotations()) for r in rots: print r.__dict__ self.assertTrue(all(a==b for a in rots for b in rots)) self.assertItemsEqual(rots, [b])
def display_move_on_clock(self, move, fen, beep=BeepLevel.CONFIG): beep = self.get_beep_level(beep) if self.enable_dgt_3000: bit_board = Board(fen) text = bit_board.san(move) self._display_on_dgt_3000(text, beep) else: text = ' ' + move.uci() self._display_on_dgt_xl(text, beep)
def run_engine(self, fen, move_time): #self.base_engine.setoption({'Clear': 'Hash'}) board = Board() board.set_fen(fen) self.base_engine.position(board) self.base_engine.go(movetime=move_time, async_callback=False) while self.base_engine.bestmove is None: pass return self.info_handler_base.info["score"][1].cp
def _oldstyle_fen(game: chess.Board): builder = [] builder.append(game.board_fen()) builder.append('w' if game.turn == chess.WHITE else 'b') builder.append(game.castling_xfen()) builder.append(chess.SQUARE_NAMES[game.ep_square] if game.ep_square else '-') builder.append(str(game.halfmove_clock)) builder.append(str(game.fullmove_number)) return ' '.join(builder)
def test_board(self): # .. NN # .. => NN b = Board(2,2) print b.free self.assertTrue(b.add_piece([("N",0,0)])) b2 = b.add_piece([("N",0,0),("N",0,1),("N",1,0),("N",1,1)]) self.assertTrue(b2) self.assertEquals(len(b2.free), 0, "\n%s\n\nfree:%s"%(b2, b2.free)) self.assertFalse(b.add_piece([("Q",0,0),("Q",1,1)]))
def deep_copy_board_pos(self, board): fresh = Board() for i in range(0, 8): for j in range(0, 8): piece = board.piece_at(j * 8 + i) if piece: sym = piece.symbol() fresh.set_piece_at(j * 8 + i, Piece.from_symbol(sym)) else: fresh.remove_piece_at(j * 8 + i) return fresh
def run_engine(self, epd, max_depth=0, move_time=0): self.modified_engine.setoption({'Clear': 'Hash'}) board = Board() board.set_epd(epd) self.modified_engine.position(board) if max_depth != 0: self.modified_engine.go(depth=max_depth, async_callback=False) else: self.modified_engine.go(movetime=move_time, async_callback=False) while self.modified_engine.bestmove is None: pass result = self.info_handler_modified.info["score"][1].cp return result
def test_get_fen(self): #test castling self.my_board = Board('rn1qkbnr/ppp2ppp/3p4/4p3/2B1P1b1/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 4') self.my_board.move_algebraic('Rg1') self.assertEqual(self.my_board.get_fen(), 'rn1qkbnr/ppp2ppp/3p4/4p3/2B1P1b1/5N2/PPPP1PPP/RNBQK1R1 b Qkq - 0 4') #test helper self.my_board = Board('8/8/4kr2/3p4/8/1N1K1N2/8/8 w - - 0 1') self.my_board.move_algebraic('Nbd4+') self.assertEqual(self.my_board.get_fen(), '8/8/4kr2/3p4/3N4/3K1N2/8/8 b - - 0 1') #test enpassant self.my_board = Board('r1bqkbnr/p1pp1ppp/n7/1p1PpP2/8/8/PPP1P1PP/RNBQKBNR w KQkq e6 0 5') self.my_board.move_algebraic('dxe6') self.assertEqual(self.my_board.get_fen(), 'r1bqkbnr/p1pp1ppp/n3P3/1p3P2/8/8/PPP1P1PP/RNBQKBNR b KQkq - 0 5')
def from_board(cls: Type[GameT], board: chess.Board) -> GameT: """Creates a game from the move stack of a :class:`~chess.Board()`.""" # Setup the initial position. game = cls() game.setup(board.root()) node = game # type: GameNode # Replay all moves. for move in board.move_stack: node = node.add_variation(move) game.headers["Result"] = board.result() return game
def setUp(self): self.notation = [["WR", "WN", "WB", "WQ", "WK", "WB", "WN", "WR"], ["WP", "WP", "WP", "WP", "", "WP", "WP", "WP"], [ "", "", "", "", "", "", "", ""], [ "", "", "", "", "WP", "", "", ""], [ "", "", "", "", "BP", "", "", ""], [ "", "", "", "", "", "", "", ""], ["BP", "BP", "BP", "BP", "", "BP", "BP", "BP"], ["BR", "BN", "BB", "BQ", "BK", "BB", "BN", "BR"]] self.flat_notation = list(itertools.chain(*self.notation)) self.num_pieces = len([square for square in self.flat_notation if square != ""]) self.empty_board = Board() self.board = Board.from_notation(self.notation)
def display_move_on_clock(self, move, fen, side, beep=False, left_dots=0, right_dots=0): if self.enable_dgt_3000: bit_board = Board(fen) move_text = bit_board.san(move) if side == ClockSide.RIGHT: move_text = move_text.rjust(8) text = self.dgttranslate.move(move_text) self._display_on_dgt_3000(text, beep, left_dots, right_dots) else: move_text = move.uci() if side == ClockSide.RIGHT: move_text = move_text.rjust(6) self._display_on_dgt_xl(move_text, beep, left_dots, right_dots)
def hash_castling(self, board: chess.Board) -> int: zobrist_hash = 0 # Hash in the castling flags. if board.has_kingside_castling_rights(chess.WHITE): zobrist_hash ^= self.array[768] if board.has_queenside_castling_rights(chess.WHITE): zobrist_hash ^= self.array[768 + 1] if board.has_kingside_castling_rights(chess.BLACK): zobrist_hash ^= self.array[768 + 2] if board.has_queenside_castling_rights(chess.BLACK): zobrist_hash ^= self.array[768 + 3] return zobrist_hash
def _accept_node(self, parent_board: chess.Board, visitor: "BaseVisitor[ResultT]") -> None: if self.starting_comment: visitor.visit_comment(self.starting_comment) visitor.visit_move(parent_board, self.move) parent_board.push(self.move) visitor.visit_board(parent_board) parent_board.pop() for nag in sorted(self.nags): visitor.visit_nag(nag) if self.comment: visitor.visit_comment(self.comment)
def display_move_on_clock(self, message): left_dots = message.ld if hasattr(message, 'ld') else ClockDots.NONE right_dots = message.rd if hasattr(message, 'rd') else ClockDots.NONE display_m = self.enable_dgt_3000 and not self.dgtserial.enable_revelation_leds if display_m: bit_board = Board(message.fen) move_text = bit_board.san(message.move) if message.side == ClockSide.RIGHT: move_text = move_text.rjust(8) text = self.dgttranslate.move(move_text) self._display_on_dgt_3000(text, message.beep, left_dots, right_dots) else: move_text = message.move.uci() if message.side == ClockSide.RIGHT: move_text = move_text.rjust(6) self._display_on_dgt_xl(move_text, message.beep, left_dots, right_dots)
def main(): args = docopt(__doc__) game, title = choose_game(args["<PGNfile>"]) b = Board() moves, annotations = [], [] for i, move in enumerate(game.mainline_moves()): if i % 2 == 0: moves.append(f"{int(i/2)+1}. {b.san(move)}") else: moves[-1] += f" {b.san(move)} " b.push(move) display(b, title, moves) annotate = input("> ") if annotate: annotations.append((int(i / 2) + 1, annotate)) print(annotate) print("FINISHED")
def __init__(self, board=None, parent=None): super(QWidget, self).__init__() policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setSizePolicy(policy) if board == None: board = Board() board.castling_rights = 0 self.board = board self.parent = parent self.borderWidth = 12 self.pieceImages = PieceImages() self.selected_xy = None self.pcs = [["P", "R", "B", "N", "Q", "K"], ["p", "r", "b", "n", "q", "k"]] self.initUI()
def visit_move(self, board: chess.Board, move: chess.Move) -> None: if self.variations or not self.variation_depth: # Write the move number. if board.turn == chess.WHITE: self.write_token(str(board.fullmove_number) + ". ") elif self.force_movenumber: self.write_token(str(board.fullmove_number) + "... ") # Write the SAN. self.write_token(board.san(move) + " ") self.force_movenumber = False
def get_san(self, message, is_xl=False): """Create a chess.board plus a text ready to display on clock.""" def move(text: str, language: str, capital: bool, short: bool): """Return move text for clock display.""" if short: directory = {} if language == 'de': directory = {'R': 'T', 'N': 'S', 'B': 'L', 'Q': 'D'} if language == 'nl': directory = {'R': 'T', 'N': 'P', 'B': 'L', 'Q': 'D'} if language == 'fr': directory = {'R': 'T', 'N': 'C', 'B': 'F', 'Q': 'D', 'K': '@'} if language == 'es': directory = {'R': 'T', 'N': 'C', 'B': 'A', 'Q': 'D', 'K': '@'} if language == 'it': directory = {'R': 'T', 'N': 'C', 'B': 'A', 'Q': 'D', 'K': '@'} for i, j in directory.items(): text = text.replace(i, j) text = text.replace('@', 'R') # replace the King "@" from fr, es, it languages if capital: return text.upper() else: return text bit_board = Board(message.fen, chess960=message.uci960) if bit_board.is_legal(message.move): if message.long: move_text = message.move.uci() else: move_text = bit_board.san(message.move) else: logging.warning('[%s] illegal move %s found - uci960: %s fen: %s', self.get_name(), message.move, message.uci960, message.fen) move_text = 'er{}' if is_xl else 'err {}' move_text = move_text.format(message.move.uci()[:4]) if message.side == ClockSide.RIGHT: move_text = move_text.rjust(6 if is_xl else 8) return bit_board, move(move_text, message.lang, message.capital and not is_xl, not message.long)
class PawnTest(unittest.TestCase): def setUp(self): self.board = Board() self.wpawn = self.board.add(Color.white, Pawn, Position(1,4)) self.bpawn = self.board.add(Color.black, Pawn, Position(2,3)) def test_possible_moves(self): self.assertListEqual(self.wpawn.possible_moves, [Position(2, 4), Position(3, 4)]) self.assertListEqual(self.bpawn.possible_moves, [Position(1, 3)]) def test_possible_attacks(self): self.assertListEqual(self.wpawn.possible_attacks, [self.bpawn.position]) self.assertListEqual(self.bpawn.possible_attacks, [self.wpawn.position])
def setUp(self): self.board = Board.from_notation([[ "", "", "", "", "", "", "", ""], [ "", "WR", "", "", "", "", "", ""], [ "", "", "", "", "", "", "", ""], [ "", "", "", "", "", "", "", ""], [ "", "WR", "", "BR", "", "WR", "", ""], [ "", "", "", "", "", "", "", ""], [ "", "", "", "", "", "", "", "BR"], [ "", "", "", "", "", "", "", ""]]) self.upper_rook = self.board[1][1] self.left_rook = self.board[4][1] self.right_rook = self.board[4][5] self.middle_rook = self.board[4][3] self.lower_rook = self.board[6][7]
def read(f): format = Algebra() board = Board() player = 'white' for move in parse_pgn(f): if type(move) == types.TupleType and move[0] == "done": print "winner", move[1] board = Board() player = 'white' continue elif type(move) == types.TupleType and move[0] == 'metadata': print move[1] continue parsed = format.interpret(move, board, player) print '...' if player == 'black' else '', move, parsed # legal_moves = [x for x in board.legal_moves(player)] # if not parsed in legal_moves: # raise Exception, "%s is not legal: not in %s" % (repr(parsed), repr(sorted([board.format_move(m) for m in legal_moves]))) board.apply_move(parsed) if player == 'white': player = 'black' else: player = 'white'
def parse_san(self, board: chess.Board, san: str) -> chess.Move: """ When the visitor is used by a parser, this is called to parse a move in standard algebraic notation. You can override the default implementation to work around specific quirks of your input format. """ # Replace zeros with correct castling notation. if san == "0-0": san = "O-O" elif san == "0-0-0": san = "O-O-O" return board.parse_san(san)
def test_board(self): self.my_board = Board() moves = [ ['e4', 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'], ['e5', 'rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2'], ['Nf3', 'rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 0 2'], ['d6', 'rnbqkbnr/ppp2ppp/3p4/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 3'], ['Bc4', 'rnbqkbnr/ppp2ppp/3p4/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 0 3'], ['Nf6', 'rnbqkb1r/ppp2ppp/3p1n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 4'], ['O-O', 'rnbqkb1r/ppp2ppp/3p1n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 0 4'] ] for move in moves: self.my_board.move_algebraic(move[0]) fen = self.my_board.get_fen() self.assertEqual(fen, move[1], 'erro no movimento %s \n esperado: %s \n recebido: %s' % (move[0], move[1], fen))
def __init__(self): super(Example, self).__init__() self.initUI() self.font = QtGui.QFont("times", 24) self.chess_piece_font = QtGui.QFont("console", 30) self.fm = QtGui.QFontMetrics(self.font) self.chess_piece_fm = QtGui.QFontMetrics(self.chess_piece_font) self.vertical_lines_x_corrds = [] self.horizontal_lines_y_coords = [] self.board = Board(knorre_vs_neumann()) self.pieces_images = {} self.load_pieces(self.img_path) self.active_cell = None self.selected_piece = None
def give_score(board: chess.Board): value = 0 for piece in chess.PIECE_TYPES: value += len(board.pieces(piece, chess.WHITE)) * VALUE[piece] value -= len(board.pieces(piece, chess.BLACK)) * VALUE[piece] return 0 if value < 0 else 0.5 if value == 0 else 1
def minimax_nn(self, board: chess.Board, depth, maximimize_white, limit_time): """Minimax algorithm from Wikipedia with NN.""" self.nodes += 1 if depth == 0 or board.is_game_over(): evaluation = self.evaluate(board) # evaluation = self.evaluate(board) """attackers = board.attackers(board.turn, board.peek().to_square) if len(attackers) > 0: # Quiescent if board.turn == chess.WHITE: evaluation += PIECES_VALUES[board.piece_map() [board.peek().to_square]. symbol().lower()] else: evaluation -= PIECES_VALUES[board.piece_map() [board.peek().to_square]. symbol().lower()]""" return evaluation, chess.Move.from_uci("0000") if maximimize_white: book_move = None if self.own_book and board.fullmove_number < 15 and ( book_move := self.get_book_move(board)): self.obhits += 1 return 10000, book_move if self.syzygy_online and len(board.piece_map()) <= 7: formatted_fen = board.fen().replace(" ", "_") data = requests.get( f"http://tablebase.lichess.ovh/standard?fen={formatted_fen}" ).json() good_move = chess.Move.from_uci(data["moves"][0]["uci"]) if data["category"] in ("win", "maybe_win", "cursed-win"): self.tbhits += 1 return 10000, good_move elif data["category"] in ("loss", "maybe-loss", "blessed-loss"): self.tbhits += 1 return -10000, good_move elif data["category"] == "draw": self.tbhits += 1 return 0, good_move else: pass if self.syzygy_tb and len(board.piece_map()) <= 6: try: return self.get_syzygy(board) except chess.syzygy.MissingTableError: pass value = -float("inf") legal_moves = list(board.legal_moves) list_best_moves = [legal_moves[0]] for move in self.nn_select_best_moves(board): if time.time() > limit_time: return float("inf"), chess.Move.from_uci("0000") test_board = chess.Board(fen=board.fen()) test_board.push(move) hash = chess.polyglot.zobrist_hash(test_board) if hash in self.tb and self.tb[hash][0] >= depth: evaluation: int = self.tb[hash][1] else: evaluation = self.minimax_nn(test_board, depth - 1, False, limit_time)[0] self.tb[hash] = (copy.copy(depth), copy.copy(evaluation)) if int(sys.getsizeof(self.tb) / 1024 / 1024) >= self.hashlimit / 2: del self.tb[list(self.tb.keys())[0]] self.hashfull += 1 if value == evaluation: list_best_moves.append(move) elif value < evaluation: value = evaluation list_best_moves = [move] return value, random.choice(list_best_moves)
def __init__(self, fen: Optional[str] = STARTING_FEN, *, chess960: bool = False): Board.__init__(self)
def __init__(self, board_fen: str = None): if board_fen: self.b = Board(board_fen) else: # Not sure why this is needed. self.b = Board()
def from_board(cls, board: chess.Board): return cls(board.fen())
def get_fen(board: chess.Board) -> str: return board.fen() # type: ignore
def chess_plugin(client, channel, nick, message, cmd, args): """ Command for helga-chess. Usage: <bigjust> !chess board <helga> <url to dpaste.com> <bigjust> !chess newgame <helga> I chose black, white to move <helga> I chose white, e5 Between each move, the game is saved and persisted on file. board and move commands always assume latest game in the gamefile. Multiple games will be stored per channel/user in history. """ engine = uci.popen_engine(ENGINE) headers = OrderedDict() current_game = None game = load_game(channel) if not game: game = pgn.Game() board = game.end().board() if args[0] in ['newgame', 'move', 'board']: if args[0] == 'board': return 'http://localhost:8080/chess/{}/'.format(channel) if args[0] == 'move': if len(args) < 2: return 'usage: move e3e5' try: board.push(Move.from_uci(args[1])) except ValueError: return 'not a valid move. valid moves: {}'.format(', '.join( [str(move) for move in board.legal_moves])) engine.position(board) move = engine.go(movetime=THINK_TIME).bestmove client.msg(channel, 'my move: {}'.format(str(move))) board.push(move) if args[0] == 'newgame': # setup a new game, choose a random side, persist after # setup(), and possibly first move engine.ucinewgame() board = Board() engine.position(board) next_round, stockfish_level = next_game_stats(channel) bot_turn = randrange(2) if not bot_turn: # we're white headers.update({ 'White': NICK, 'Black': channel, }) best_move = engine.go(movetime=THINK_TIME).bestmove board.push(best_move) next_turn = 'Black' else: headers.update({ 'Black': NICK, 'White': channel, }) next_turn = 'White' now = datetime.datetime.now() headers.update({ 'Date': '{}.{}.{}'.format(now.year, now.month, now.day), 'Round': next_round, 'Event': stockfish_level, }) client.msg(channel, '{} to move'.format(next_turn)) # persist the game game = pgn.Game.from_board(board) game.headers = headers save_game(channel, game)
def board_to_planes(board: chess.Board, board_occ, normalize=True, last_moves=None): """ Gets the plane representation of a given board state. ## Chess: Feature | Planes --- | --- P1 piece | 6 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING) P2 piece | 6 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING) Repetitions | 2 (two planes (full zeros/ones) indicating how often the board positions has occurred) En-passant square | 1 (Binary map indicating the square where en-passant capture is possible) --- 15 planes * * * P1 castling | 2 (One if castling is possible, else zero) P2 castling | 2 (One if castling is possible, else zero) No-progress count | 1 (Setting the no progress counter as integer values, (described by uci halfmoves format) --- 5 planes * * * Last 8 moves | 16 (indicated by origin and destination square, the most recent move is described by first 2 planes) --- 16 planes * * * is960 = | 1 (boolean, 1 when active) --- 1 plane --- P1 pieces | 1 | A grouped mask of all WHITE pieces | P2 pieces | 1 | A grouped mask of all BLACK pieces | Checkerboard | 1 | A chess board pattern | P1 Material Diff | 5 | (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN), normalized with 8, + means positive, - means negative | Opposite Color Bishops | 1 | Indicates if they are only two bishops and the bishops are opposite color | Checkers | 1 | Indicates all pieces giving check | P1 Material Count | 5 | (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN), normalized with 8 | --- 15 planes The total number of planes is calculated as follows: # -------------- 15 + 5 + 16 + 1 + 15 Total: 52 planes :param board: Board handle (Python-chess object) :param board_occ: Number of board occurences :param normalize: True if the inputs shall be normalized to the range [0.-1.] :param last_moves: List of last last moves. The most recent move is the first entry. :return: planes - the plane representation of the current board state """ # return the plane representation of the given board # return variants.board_to_planes(board, board_occ, normalize, mode=MODE_CHESS) planes = np.zeros((NB_CHANNELS_TOTAL, BOARD_HEIGHT, BOARD_WIDTH)) # channel will be incremented by 1 at first plane channel = 0 me = board.turn you = not board.turn colors = [me, you] # mirror all bitboard entries for the black player mirror = board.turn == chess.BLACK assert channel == CHANNEL_PIECES # Fill in the piece positions # Channel: 0 - 11 # Iterate over both color starting with WHITE for color in colors: # the PIECE_TYPE is an integer list in python-chess for piece_type in chess.PIECE_TYPES: # iterate over the piece mask and receive every position square of it for pos in board.pieces(piece_type, color): row, col = get_row_col(pos, mirror=mirror) # set the bit at the right position planes[channel, row, col] = 1 channel += 1 assert channel == CHANNEL_REPETITION # Channel: 12 - 13 # set how often the position has already occurred in the game (default 0 times) # this is used to check for claiming the 3 fold repetition rule if board_occ >= 1: planes[channel, :, :] = 1 if board_occ >= 2: planes[channel + 1, :, :] = 1 channel += 2 # Channel: 14 # En Passant Square assert channel == CHANNEL_EN_PASSANT if board.ep_square and board.has_legal_en_passant(): # is not None: row, col = get_row_col(board.ep_square, mirror=mirror) planes[channel, row, col] = 1 channel += 1 # Channel: 15 - 18 assert channel == CHANNEL_CASTLING for color in colors: # check for King Side Castling if board.has_kingside_castling_rights(color): planes[channel, :, :] = 1 channel += 1 # check for Queen Side Castling if board.has_queenside_castling_rights(color): planes[channel, :, :] = 1 channel += 1 # Channel: 19 # (IV.4) No Progress Count # define a no 'progress' counter # it gets incremented by 1 each move # however, whenever a piece gets dropped, a piece is captured or a pawn is moved, it is reset to 0 # halfmove_clock is an official metric in fen notation # -> see: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation # check how often the position has already occurred in the game assert channel == CHANNEL_NO_PROGRESS planes[ channel, :, :] = board.halfmove_clock / NORMALIZE_50_MOVE_RULE if normalize else board.halfmove_clock channel += 1 # Channel: 20 - 35 assert channel == CHANNEL_LAST_MOVES # Last 8 moves if last_moves: assert (len(last_moves) == NB_LAST_MOVES) for move in last_moves: if move: from_row, from_col = get_row_col(move.from_square, mirror=mirror) to_row, to_col = get_row_col(move.to_square, mirror=mirror) planes[channel, from_row, from_col] = 1 channel += 1 planes[channel, to_row, to_col] = 1 channel += 1 else: channel += 2 else: channel += NB_LAST_MOVES * NB_CHANNELS_PER_HISTORY_ITEM # Channel: 36 # Chess960 assert channel == CHANNEL_IS_960 if board.chess960: planes[channel + 1, :, :] = 1 channel += 1 # Channel: 37 - 38 # All white pieces and black pieces in a single map assert channel == CHANNEL_PIECE_MASK for color in colors: # the PIECE_TYPE is an integer list in python-chess for piece_type in chess.PIECE_TYPES: # iterate over the piece mask and receive every position square of it for pos in board.pieces(piece_type, color): row, col = get_row_col(pos, mirror=mirror) # set the bit at the right position planes[channel, row, col] = 1 channel += 1 # Channel: 39 # Checkerboard assert (channel == CHANNEL_CHECKERBOARD) planes[channel, :, :] = checkerboard() channel += 1 # Channel: 40 - 44 # Relative material difference (negative if less pieces than opponent and positive if more) # iterate over all pieces except the king assert channel == CHANNEL_MATERIAL_DIFF for piece_type in chess.PIECE_TYPES[:-1]: material_count = len(board.pieces(piece_type, me)) - len( board.pieces(piece_type, you)) planes[ channel, :, :] = material_count / NORMALIZE_PIECE_NUMBER if normalize else material_count channel += 1 # Channel: 45 # Opposite color bishops assert (channel == CHANNEL_OPP_BISHOPS) if opposite_colored_bishops(board): planes[channel, :, :] = 1 channel += 1 # Channel: 46 # Checkers assert channel == CHANNEL_CHECKERS board_checkers = checkers(board) if board_checkers: # iterate over the piece mask and receive every position square of it for pos in chess.SquareSet(board_checkers): row, col = get_row_col(pos, mirror=mirror) # set the bit at the right position planes[channel, row, col] = 1 channel += 1 # Channel: 47 - 51 # Material assert channel == CHANNEL_MATERIAL_COUNT for piece_type in chess.PIECE_TYPES[:-1]: material_count = len(board.pieces(piece_type, me)) planes[ channel, :, :] = material_count / NORMALIZE_PIECE_NUMBER if normalize else material_count channel += 1 assert channel == NB_CHANNELS_TOTAL return planes
def make_gif( self, gif_file_path:str = 'chess.gif' ): ''' Makes the gif of the loaded pgn at the specified destination file path. Parameters ---------- gif_file_path : str Destination directory to store the gif file. ''' pgn_file = open(self.pgn_file_path) chess_game = pgn_reader.read_game(pgn_file) # Stores the headers in the pgn self.header_info = chess_game.headers # self.white_player = self.header_info['White'] # self.black_player = self.header_info['Black'] # self.BlackElo = self.header_info['BlackElo'] # self.WhiteElo = self.header_info['WhiteElo'] # print('White Player:',self.white_player) # print('Black Player:',self.black_player,'\n') # print( 'WhiteElo:', self.WhiteElo ) # print( 'BlackElo:', self.BlackElo ) # get string notation of the game # print(str( chess_game.game()).replace('\n','') ) #get the start position # print(chess_game.board() ) self.white_timeline = [] self.black_timeline = [] self.board_states = [Board()] main_line_itr = chess_game.mainline() for node in main_line_itr: if node.parent is not None: current_move = node.san() time_left = node.clock() current_board = node.board() self.board_states.append(current_board) if current_board.turn: self.white_timeline.append(time_left) else: self.black_timeline.append(time_left) # White's perspective # print('\n',current_board) # # Black's perspective # print( '\n',current_board.transform( flip_vertical )) obj = Chess_Image( colors = self.kwargs['colors'], side = self.kwargs['side'], piece_theme = self.kwargs['piece_theme'], h_margin=self.kwargs['h_margin'], v_margin=self.kwargs['v_margin'] ) frames = list( map(lambda x:obj.create_position(x) , self.board_states ) ) mimwrite( gif_file_path, frames, duration = self.kwargs['delay'], subrectangles = True , palettesize = 256 # default = 256 ) optimize_gif(gif_file_path)
import os from time import clock import random import numpy as np from chess import Board, Move from stockfish import Stockfish # Learning to beat stockfish # Fra hver posisjon et steg fram spill sf vs sf til slutt og se resultatet. # Tren opp paa stockfish til stockfish er representert som policy # Deretter tren med rf learning board = Board() stockfish = Stockfish(path='{}/stockfish-10-linux/stockfish_10_x64_modern'.format(os.getcwd()), depth=) stockfish.set_fen_position(board.fen()) #print([str(p) for p in board.piece_map().values()]) def play_stockfish_move(): stockfish.set_fen_position(board.fen()) uci = stockfish.get_best_move() if uci: move = Move.from_uci(uci) board.push(move) def play_random_move(): uci = str(random.choice(list(board.legal_moves))) if uci:
def game_over(fen: str) -> bool: """ Determine if the game is over and why """ return Board(fen=fen).is_game_over()
def norm_fen(fen): return Board(fen).fen(en_passant='fen')
# -*- coding: utf-8 -*- import os import bottle from chess import Board, Moves, Piece gameboard = Board() gameboard.start_game() path = os.path.abspath("views") @bottle.route('/static/<filepath:path>') def server_static(filepath): return bottle.static_file(filepath, root=path) @bottle.get('/') def print_board(): return bottle.template('test_web.tpl', playboard=gameboard) @bottle.get('/move_end') def print_board1(): return bottle.template('test_web_end.tpl', playboard=gameboard) @bottle.get('/promote') def print_promotion(): return bottle.template('test_web_promotion.tpl')
def say_last_move(game: chess.Board): """Take a chess.BitBoard instance and speaks the last move from it.""" move_parts = { 'K': 'king.ogg', 'B': 'bishop.ogg', 'N': 'knight.ogg', 'R': 'rook.ogg', 'Q': 'queen.ogg', 'P': 'pawn.ogg', '+': '', '#': '', 'x': 'takes.ogg', '=': 'promote.ogg', 'a': 'a.ogg', 'b': 'b.ogg', 'c': 'c.ogg', 'd': 'd.ogg', 'e': 'e.ogg', 'f': 'f.ogg', 'g': 'g.ogg', 'h': 'h.ogg', '1': '1.ogg', '2': '2.ogg', '3': '3.ogg', '4': '4.ogg', '5': '5.ogg', '6': '6.ogg', '7': '7.ogg', '8': '8.ogg' } bit_board = game.copy() move = bit_board.pop() san_move = bit_board.san(move) voice_parts = [] if san_move.startswith('O-O-O'): voice_parts += ['castlequeenside.ogg'] elif san_move.startswith('O-O'): voice_parts += ['castlekingside.ogg'] else: for part in san_move: try: sound_file = move_parts[part] except KeyError: logging.warning('unknown char found in san: [%s : %s]', san_move, part) sound_file = '' if sound_file: voice_parts += [sound_file] if game.is_game_over(): if game.is_checkmate(): wins = 'whitewins.ogg' if game.turn == chess.BLACK else 'blackwins.ogg' voice_parts += ['checkmate.ogg', wins] elif game.is_stalemate(): voice_parts += ['stalemate.ogg'] else: if game.is_seventyfive_moves(): voice_parts += ['75moves.ogg', 'draw.ogg'] elif game.is_insufficient_material(): voice_parts += ['material.ogg', 'draw.ogg'] elif game.is_fivefold_repetition(): voice_parts += ['repetition.ogg', 'draw.ogg'] else: voice_parts += ['draw.ogg'] elif game.is_check(): voice_parts += ['check.ogg'] if bit_board.is_en_passant(move): voice_parts += ['enpassant.ogg'] return voice_parts
def launchSf( self, pars, fen, tablebases, ): board = Board(fen, chess960=False) wdl = None drawPlyCnt, resignPlyCnt = 0, 0 whiteIdx = 1 turnIdx = whiteIdx ^ (board.turn == chess.BLACK) uciEngines = init_engines(pars) info_handler = uci.InfoHandler() for u in uciEngines: u.info_handlers.append(info_handler) u.ucinewgame() try: while (not board.is_game_over(claim_draw=True)): if board.castling_rights == 0: if len(re.findall(r"[rnbqkpRNBQKP]", board.board_fen())) < 6: wdl = tablebases.probe_wdl(board) if wdl is not None: break uciEngines[turnIdx].position(board) bestmove, score = uciEngines[turnIdx].go(depth=9) score = info_handler.info["score"][1].cp # print(score) if score is not None: # Resign adjudication if abs(score) >= Resign['score']: resignPlyCnt += 1 if resignPlyCnt >= 2 * Resign['movecount']: break else: resignPlyCnt = 0 # Draw adjudication if abs(score ) <= Draw['score'] and board.halfmove_clock > 0: drawPlyCnt += 1 if drawPlyCnt >= 2 * Draw['movecount'] \ and board.fullmove_number >= Draw['movenumber']: break else: drawPlyCnt = 0 else: # Disable adjudication over mate scores drawPlyCnt, resignPlyCnt = 0, 0 board.push(bestmove) turnIdx ^= 1 result = board.result(True) if result == '*': if resignPlyCnt >= 2 * Resign['movecount']: if score > 0: result = '1-0' if board.turn == chess.WHITE else '0-1' else: result = '0-1' if board.turn == chess.WHITE else '1-0' elif wdl is not None: if wdl <= -1: result = '1-0' if board.turn == chess.WHITE else '0-1' elif wdl >= 1: result = '0-1' if board.turn == chess.WHITE else '1-0' else: result = '1/2-1/2' # print('tb draw') else: result = '1/2-1/2' # print('draw') # print(board.fen()) # print(re.findall(r"[rnbqkpRNBQKP]", board.board_fen())) for u in uciEngines: u.quit(0) except (MemoryError, SystemError, KeyboardInterrupt, OverflowError, OSError, ResourceWarning): for u in uciEngines: u.quit(1) return result # print(result) exit(0)
def set_position(self, fen): self.node = Board(fen)
def evaluate_position(board: chess.Board): # collect material of players white_material = 0 black_material = 0 white_pawns = board.pieces(chess.PAWN, chess.WHITE) white_knights = board.pieces(chess.KNIGHT, chess.WHITE) white_bishops = board.pieces(chess.BISHOP, chess.WHITE) white_rooks = board.pieces(chess.ROOK, chess.WHITE) white_queen = board.pieces(chess.QUEEN, chess.WHITE) for square in white_pawns: square_value = get_positional_value_for_piece('pawn', square, chess.WHITE) white_material += piece_values[chess.PAWN] + square_value for square in white_knights: square_value = get_positional_value_for_piece('knight', square, chess.WHITE) white_material += piece_values[chess.KNIGHT] + square_value for square in white_bishops: square_value = get_positional_value_for_piece('bishop', square, chess.WHITE) white_material += piece_values[chess.BISHOP] + square_value for square in white_rooks: square_value = get_positional_value_for_piece('rook', square, chess.WHITE) white_material += piece_values[chess.ROOK] + square_value for square in white_queen: square_value = get_positional_value_for_piece('queen', square, chess.WHITE) white_material += piece_values[chess.QUEEN] + square_value black_pawns = board.pieces(chess.PAWN, chess.BLACK) black_knights = board.pieces(chess.KNIGHT, chess.BLACK) black_bishops = board.pieces(chess.BISHOP, chess.BLACK) black_rooks = board.pieces(chess.ROOK, chess.BLACK) black_queen = board.pieces(chess.QUEEN, chess.BLACK) for square in black_pawns: square_value = get_positional_value_for_piece('pawn', square, chess.BLACK) black_material += piece_values[chess.PAWN] + square_value for square in black_knights: square_value = get_positional_value_for_piece('knight', square, chess.BLACK) black_material += piece_values[chess.KNIGHT] + square_value for square in black_bishops: square_value = get_positional_value_for_piece('bishop', square, chess.BLACK) black_material += piece_values[chess.BISHOP] + square_value for square in black_rooks: square_value = get_positional_value_for_piece('rook', square, chess.BLACK) black_material += piece_values[chess.ROOK] + square_value for square in black_queen: square_value = get_positional_value_for_piece('queen', square, chess.BLACK) black_material += piece_values[chess.QUEEN] + square_value # add kings white_material += piece_values[ chess.KING] + get_positional_value_for_piece( 'king', board.king(chess.WHITE), chess.WHITE, board) black_material += piece_values[ chess.KING] + get_positional_value_for_piece( 'king', board.king(chess.BLACK), chess.BLACK, board) evaluation = white_material - black_material if board.turn == chess.BLACK: evaluation = evaluation * -1 return evaluation
from flask import Flask from flask import render_template, redirect, request from chess import WebInterface, Board, InputError, Move, MoveHistory app = Flask(__name__) ui = WebInterface() game = Board() movehis = MoveHistory() @app.route('/') def root(): return render_template('index.html') @app.route('/newgame') def newgame(): # Note that in Python, objects and variables # in the global space are available to # top-level functions game.start() ui.board = game.display() ui.inputlabel = f'{game.turn} player: ' ui.errmsg = None ui.btnlabel = 'Move' return redirect('/play') @app.route('/undo', methods=["POST", "GET"]) def undo(): try:
def tt_store(self, board: Board, entry: TTEntry): key = board._transposition_key() self.hashtable[key] = entry
'q': 'bQ', 'n': 'bN', 'k': 'bK', 'p': 'bP', 'b': 'bB', 'R': 'wR', 'Q': 'wQ', 'N': 'wN', 'K': 'wK', 'P': 'wP', 'B': 'wB' } # Reads the available piece theme's .svg images and save them as .png images of appropraite size for piece in self.pieces_map: piece_path = os.path.join(piece_dir, self.pieces_map[piece] + '.svg') image = VipsImage.thumbnail(piece_path, size, height=size) image.write_to_file(f"Images/{ self.pieces_map[piece]}.png") #: Dictionary where pieces are the keys and corresponding PIL Images are the values self.piece_imgs = dict() for piece in self.pieces_map: self.piece_imgs[piece] = Image.open( f"Images/{ self.pieces_map[piece]}.png") if __name__ == "__main__": obj = Chess_Image(('#ffe0b3', '#802b00'), side='2', piece_theme='dog') obj.create_position(Board())
def tt_lookup(self, board: Board) -> "TTEntry": key = board._transposition_key() return self.hashtable.get(key, TTEntry.default())
if v[0] < cur_move_pair[0]: cur_move_pair = v return cur_move_pair # c_depth is the current depth # depth is the search tree depth def max_f(board, c_depth, depth, top_level_move=None): if board.is_game_over() or c_depth == 0: return w.evaluation(board), top_level_move else: boards = [(create_new_board_with_move(board, move), move) for move in board.legal_moves] cur_move_pair = (-1001, None) for b, m in boards: if c_depth != depth: m = top_level_move v = min_f(b, c_depth-1, depth, m) if v[0] > cur_move_pair[0]: cur_move_pair = v return cur_move_pair if __name__ == '__main__': board = Board('rnb1kbnr/ppp2ppp/8/3P4/4p3/5P2/PPPQ2PP/RNB1KB1R b KQkq - 0 6') print(max_f(board, 5, 5)) print(min_f(board, 2, 2))
class ChessGUI(App): def __init__(self, model_path, verbose=False, **kwargs): super().__init__(**kwargs) self.board_size = 8 self.button_positions = {} self.board = Board() moves_folder = Path("moves/") if not moves_folder.exists(): moves_folder.mkdir(exist_ok=False) svg2png(bytestring=chess.svg.board(self.board, size=350), write_to=f'moves/chess.png') self.move_str = "" self.engine = ChessModel(model_path, verbose=verbose) def build(self): self.layout = GridLayout(cols=self.board_size, padding="31px") with self.layout.canvas: self.rect = Rectangle(source='chess.png') Clock.schedule_interval(self.update, 1) self.add_board_buttons() return self.layout def update(self, *args): self.rect.size = self.root.size self.rect.pos = self.root.pos self.rect.size = (self.rect.size[0] + 0.1, self.rect.size[1]) def reset_board(self): [ Path(m).unlink(missing_ok=True) for m in list(Path("moves/").glob("chess*")) ] self.board = Board() self.move_str = "" save_name = 'chess.png' svg2png(bytestring=chess.svg.board(self.board, size=350), write_to=save_name) self.rect.source = save_name def check_pawn_promotion(self, move): move_str = str(move) move_from = move_str[:2] piece_to_move = str(self.board.piece_at(chess.parse_square(move_from))) if piece_to_move not in ["p", "P"]: return move move_to = move_str[2:] if move_from[1] in ["2", "7"] and move_to[1] in ["1", "8"]: move.promotion = chess.QUEEN return move def update_button(self, button): self.move_str += button.id # print(self.move_str) if len(self.move_str) == 4: Cache.remove('kv.image') Cache.remove('kv.texture') move = chess.Move(from_square=chess.parse_square( self.move_str[:2]), to_square=chess.parse_square(self.move_str[2:4])) move = self.check_pawn_promotion(move) print(f"Your move: {move}") if move in self.board.legal_moves: self.board.push(move) [ Path(m).unlink(missing_ok=True) for m in list(Path("moves/").glob("chess*")) ] save_name = f'moves/chess_{str(move)}_{self.board.turn}.png' svg2png(bytestring=chess.svg.board(self.board, size=350), write_to=save_name) self.rect.source = save_name if self.board.is_game_over(): return self.declare_winner() # ai_move = np.random.choice(list(self.board.legal_moves)) # ai_move = list(self.board.legal_moves)[0] # print(ai_move) ai_move = self.engine.make_move(self.board) print(f"AI move: {ai_move}") if isinstance(ai_move, chess.Move): self.board.push(ai_move) else: return self.declare_winner() if self.board.is_game_over(): return self.declare_winner() [ Path(m).unlink(missing_ok=True) for m in list(Path("moves/").glob("chess*")) ] save_name = f'moves/chess_{str(move)}_{self.board.turn}.png' svg2png(bytestring=chess.svg.board(self.board, size=350), write_to=save_name) self.rect.source = save_name self.move_str = "" def board_to_game(self): board = self.board game = chess.pgn.Game() # Undo all moves. switchyard = collections.deque() while board.move_stack: switchyard.append(board.pop()) game.setup(board) node = game # Replay all moves. while switchyard: move = switchyard.pop() node = node.add_variation(move) board.push(move) game.headers["Result"] = board.result() Path("games").open("a").write(str(game)) return game def declare_winner(self): popup = Popup(title='Test popup', content=Label(text=f"{self.board.result()}"), size_hint=(None, None), size=(400, 400)) popup.open() self.board_to_game() self.reset_board() def add_board_buttons(self): for i in reversed(range(8)): for j in range(8): k = i * 8 + j self.layout.add_widget( ChessButton(id=str(target_to_move(k))[:2], background_color=(0.1, 0.1, 0.1, 0.6), on_press=self.update_button))
class Viridithas(): def __init__( self, human: bool = False, fen: str = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', pgn: str = '', time_limit: float = 15, fun: bool = False, contempt: int = 3000, book: bool = True, advancedTC: list = [], ): if pgn == '': self.node = Board(fen) else: self.node = Board() for move in pgn.split(): try: self.node.push_san(move) except Exception: continue self.time_limit = time_limit if advancedTC: self.endpoint = time.time()+advancedTC[0]*60 self.increment = advancedTC[1] else: self.endpoint = 0 self.increment = 0 self.fun = fun self.contempt = contempt self.human = human self.nodes = 0 self.advancedTC = advancedTC self.hashtable: dict[Hashable, TTEntry] = dict() self.inbook = book def set_position(self, fen): self.node = Board(fen) def __repr__(self) -> str: return str(self.node) + '\n' + self.__class__.__name__+"-engine at position " + str(self.node.fen()) def __str__(self) -> str: return self.__class__.__name__ def user_setup(self): if input("Do you want to configure the bot? (Y/N) ").upper() != 'Y': return myname = self.__class__.__name__.upper() print(f"BEGINNING USER CONFIGURATION OF {myname}-BOT") datadict = get_engine_parameters() self.__init__( human=datadict["human"], fen=datadict["fen"], pgn=datadict["pgn"], time_limit=datadict["time_limit"], fun=datadict["fun"], contempt=datadict["contempt"], book=datadict["book"], advancedTC=datadict["advancedTC"] ) def gameover_check_info(self): checkmate = self.node.is_checkmate() draw = self.node.is_stalemate() or \ self.node.is_insufficient_material( ) or \ self.node.is_repetition(2) or self.node.is_seventyfive_moves() or not any(self.node.generate_legal_moves()) return checkmate or draw, checkmate, draw # @profile def evaluate(self, depth: float, checkmate: bool, draw: bool) -> float: self.nodes += 1 if checkmate: return MATE_VALUE * int(max(depth+1, 1)) * (1 if self.node.turn else -1) if draw: return -self.contempt * (1 if self.node.turn else -1) rating: float = 0 rating += pst_eval(self.node) # rating += see_eval(self.node) # rating += mobility(self.node) * MOBILITY_FACTOR # rating += piece_attack_counts(self.node) * ATTACK_FACTOR # rating += king_safety(self.node) * KING_SAFETY_FACTOR # rating += space(self.node) * SPACE_FACTOR return rating def single_hash_iterator(self, best): yield best def captures_piece(self, p): # concentrate on MVV, then LVA return itertools.chain( self.node.generate_pseudo_legal_moves(self.node.pawns, p), self.node.generate_pseudo_legal_moves(self.node.knights, p), self.node.generate_pseudo_legal_moves(self.node.bishops, p), self.node.generate_pseudo_legal_moves(self.node.rooks, p), self.node.generate_pseudo_legal_moves(self.node.queens, p), self.node.generate_pseudo_legal_moves(self.node.kings, p), ) #@profile def captures(self): # (MVV/LVA) return (m for m in itertools.chain( self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.queens), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.rooks), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.bishops), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.knights), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.pawns), ) if self.node.is_legal(m)) def winning_captures(self): # (MVV/LVA) target_all = self.node.occupied_co[not self.node.turn] target_3 = target_all & ~self.node.pawns target_5 = target_3 & (~self.node.bishops | ~self.node.knights) target_9 = target_5 & ~self.node.rooks return itertools.chain( self.node.generate_pseudo_legal_moves(self.node.pawns, target_all), self.node.generate_pseudo_legal_moves(self.node.knights, target_3), self.node.generate_pseudo_legal_moves(self.node.bishops, target_3), self.node.generate_pseudo_legal_moves(self.node.rooks, target_5), self.node.generate_pseudo_legal_moves(self.node.queens, target_9), self.node.generate_pseudo_legal_moves(self.node.kings, target_9), ) def losing_captures(self): # (MVV/LVA) target_pawns = self.node.pawns target_pnb = target_pawns | self.node.bishops | self.node.knights target_pnbr = target_pnb | self.node.rooks return itertools.chain( self.node.generate_pseudo_legal_moves(self.node.knights, target_pawns), self.node.generate_pseudo_legal_moves(self.node.bishops, target_pawns), self.node.generate_pseudo_legal_moves(self.node.rooks, target_pnb), self.node.generate_pseudo_legal_moves(self.node.queens, target_pnbr), self.node.generate_pseudo_legal_moves(self.node.kings, target_pnbr), ) def ordered_moves(self): return (m for m in itertools.chain( # self.winning_captures(), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.queens), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.rooks), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.bishops), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.knights), self.captures_piece( self.node.occupied_co[not self.node.turn] & self.node.pawns), self.node.generate_pseudo_legal_moves( 0xffff_ffff_ffff_ffff, ~self.node.occupied_co[not self.node.turn]), # self.losing_captures() ) if self.node.is_legal(m)) def pass_turn(self) -> None: self.node.push(Move.from_uci("0000")) #@profile def qsearch(self, alpha: float, beta: float, depth: float, colour: int, gameover: bool, checkmate: bool, draw: bool) -> float: val = self.evaluate(1, checkmate, draw) * colour if gameover: return val if val >= beta: return beta if (val < alpha - QUEEN_VALUE): return alpha alpha = max(val, alpha) for capture in self.captures(): self.node.push(capture) gameover, checkmate, draw = self.gameover_check_info() score = -self.qsearch(-beta, -alpha, depth - 1, -colour, gameover, checkmate, draw) self.node.pop() if score >= beta: return score alpha = max(score, alpha) return alpha def tt_lookup(self, board: Board) -> "TTEntry": key = board._transposition_key() return self.hashtable.get(key, TTEntry.default()) def tt_store(self, board: Board, entry: TTEntry): key = board._transposition_key() self.hashtable[key] = entry #@profile def negamax(self, depth: float, colour: int, alpha: float, beta: float) -> float: initial_alpha = alpha # (* Transposition Table Lookup; self.node is the lookup key for ttEntry *) tt_entry = self.tt_lookup(self.node) if not tt_entry.is_null() and tt_entry.depth >= depth: if tt_entry.type == EXACT: return tt_entry.value elif tt_entry.type == LOWERBOUND: alpha = max(alpha, tt_entry.value) elif tt_entry.type == UPPERBOUND: beta = min(beta, tt_entry.value) if alpha >= beta: return tt_entry.value if self.node.is_legal(tt_entry.best): moves = itertools.chain([tt_entry.best], filter(lambda x: x != tt_entry.best, self.ordered_moves())) else: moves = self.ordered_moves() else: moves = self.ordered_moves() gameover, checkmate, draw = self.gameover_check_info() if gameover: return colour * self.evaluate(depth, checkmate, draw) if depth < 1: return self.qsearch(alpha, beta, depth, colour, gameover, checkmate, draw) current_pos_is_check = self.node.is_check() if not current_pos_is_check and depth >= 3 and abs(alpha) < MATE_VALUE and abs(beta) < MATE_VALUE: # MAKE A NULL MOVE self.node.push(Move.null()) # PERFORM A LIMITED SEARCH value = - self.negamax(depth - 3, -colour, -beta, -beta + 1) # UNMAKE NULL MOVE self.node.pop() if value >= beta: return beta do_prune = self.pruning(depth, colour, alpha, beta, current_pos_is_check) best_move = Move.null() search_pv = True value = float("-inf") for move_idx, move in enumerate(moves): if move_idx == 0: best_move = move gives_check = self.node.gives_check(move) is_capture = self.node.is_capture(move) is_promo = bool(move.promotion) depth_reduction = search_reduction_factor( move_idx, current_pos_is_check, gives_check, is_capture, is_promo, depth) if do_prune: if not gives_check and not is_capture: continue self.node.push(move) if search_pv: r = -self.negamax(depth - depth_reduction, -colour, -beta, -alpha) value = max(value, r) else: r = -self.negamax(depth - depth_reduction, -colour, -alpha-1, -alpha) if (r > alpha): # // in fail-soft ... & & value < beta) is common r = -self.negamax(depth - depth_reduction, -colour, -beta, -alpha) #// re-search value = max(value, r) self.node.pop() if value > alpha: alpha = value best_move = move alpha = max(alpha, value) if alpha >= beta: break search_pv = False # (* Transposition Table Store; self.node is the lookup key for ttEntry *) # ttEntry = TTEntry() tt_entry.value = value if value <= initial_alpha: tt_entry.type = UPPERBOUND elif value >= beta: tt_entry.type = LOWERBOUND else: tt_entry.type = EXACT tt_entry.depth = depth tt_entry.best = best_move tt_entry.null = False self.tt_store(self.node, tt_entry) return value def pruning(self, depth, colour, alpha, beta, current_pos_is_check): if not (not current_pos_is_check and abs( alpha) < MATE_VALUE / 2 and abs(beta) < MATE_VALUE / 2) or depth > 2: return False see = see_eval(self.node) * colour DO_D1_PRUNING = depth <= 1 and see + FUTILITY_MARGIN < alpha DO_D2_PRUNING = depth <= 2 and see + FUTILITY_MARGIN_2 < alpha return DO_D1_PRUNING or DO_D2_PRUNING def move_sort(self, moves: list, ratings: list): pairs = zip(*sorted(zip(moves, ratings), key=operator.itemgetter(1))) moves, ratings = [list(pair) for pair in pairs] return moves, ratings def pv_string(self): count = 0 moves = [] while True: e = self.tt_lookup(self.node) if e.is_null() or not self.node.is_legal(e.best): break # print(self.node.__str__()) # print(self.node.__repr__()) # print(e.best) # print(self.node.san(e.best)) moves.append(self.node.san(e.best)) self.node.push(e.best) count += 1 for _ in moves: self.node.pop() if count == 0: return "" return " ".join(moves) def turnmod(self) -> int: return -1 if self.node.turn else 1 def show_iteration_data(self, moves: list, values: list, depth: float, start: float) -> tuple: t = round(time.time()-start, 2) print(f"{self.node.san(moves[0])} | {-round((self.turnmod()*values[0])/1000, 3)} | {str(t)}s at depth {str(depth + 1)}, {str(self.nodes)} nodes processed, at {str(int(self.nodes / (t+0.00001)))}NPS.\n", f"PV: {self.pv_string()}\n", end="") return (self.node.san(moves[0]), self.turnmod()*values[0], self.nodes, depth+1, t) def search(self, ponder: bool = False, readout: bool = True): val = float("-inf") start_time = time.time() self.nodes = 0 moves = [next(self.ordered_moves())] saved_position = deepcopy(self.node) alpha, beta = float("-inf"), float("inf") valWINDOW = PAWN_VALUE / 4 WINDOW_FAILED = False try: depth = 1 while depth < 40: best = self.tt_lookup(self.node).best time_elapsed = time.time() - start_time # check if we aren't going to finish the next search in time if time_elapsed > 0.5 * self.time_limit and not ponder and not WINDOW_FAILED: return best, val val = self.negamax( depth, self.turnmod(), alpha=alpha, beta=beta) # print(val) if ((val <= alpha) or (val >= beta)): # We fell outside the window, so try again with a # full-width window (and the same depth). alpha = float("-inf") beta = float("inf") WINDOW_FAILED = True continue WINDOW_FAILED = False best = self.tt_lookup(self.node).best # check if we've run out of time if time_elapsed > self.time_limit and not ponder: return best, val moves = [self.tt_lookup(self.node).best] values = [self.tt_lookup(self.node).value] if readout: self.show_iteration_data(moves, values, depth, start_time) alpha = val - valWINDOW # Set up the window for the next iteration. beta = val + valWINDOW depth += 1 except KeyboardInterrupt: self.node = saved_position pass return moves[0], val def ponder(self) -> None: self.origin = self.node.copy() self.search(ponder=True) def get_book_move(self): # book = chess.polyglot.open_reader( # r"ProDeo292/ProDeo292/books/elo2500.bin") book = chess.polyglot.open_reader( r"books/elo2500.bin") main_entry = book.find(self.node) choice = book.weighted_choice(self.node) book.close() return main_entry.move, choice.move def engine_move(self) -> Move: # add flag_func for egtb mode if self.advancedTC: self.time_limit = (self.endpoint-time.time())/20 print("Time for move: " + str(round(self.time_limit, 2)) + "s") if self.inbook: try: best, choice = self.get_book_move() if self.fun: self.node.push(choice) return choice else: self.node.push(best) print(chess.pgn.Game.from_board(self.node)[-1]) except IndexError: self.time_limit = self.time_limit*2 best, _ = self.search() self.node.push(best) print(chess.pgn.Game.from_board(self.node)[-1]) self.inbook = False self.time_limit = self.time_limit/2 else: best, _ = self.search() self.node.push(best) print(chess.pgn.Game.from_board(self.node)[-1]) # self.record_stack() self.endpoint += self.increment return best def user_move(self) -> str: move = input("enter move: ") while True: try: self.node.push_san(move) break except Exception: move = input("enter move: ") return move def display_ending(self) -> None: if self.node.is_stalemate(): print('END BY STALEMATE') elif self.node.is_insufficient_material(): print('END BY INSUFFICIENT MATERIAL') elif self.node.is_fivefold_repetition(): print('END BY FIVEFOLD REPETITION') elif self.node.is_checkmate: print("BLACK" if self.node.turn == BLACK else "WHITE", 'WINS ON TURN', self.node.fullmove_number) else: print('END BY UNKNOWN REASON') def run_game(self, indefinite=True) -> str: while not self.node.is_game_over(): print(self.__repr__()) if self.human and self.node.turn: try: self.ponder() except KeyboardInterrupt: self.node = self.origin self.user_move() else: # SWAP THESE ASAP self.engine_move() if not indefinite: break self.display_ending() try: return str(chess.pgn.Game.from_board(self.node)[-1]) except Exception: return "PGN ERROR" def play_viri(self, fen=None): player_colour = input( "Enter the human player's colour in the form b/w\n--> ") while player_colour not in ['b', 'w']: player_colour = input( "Enter the human player's colour in the form b/w\n--> ") player_colour = WHITE if player_colour == 'w' else BLACK timeControl = int( input("how many seconds should viri get per move?\n--> ")) self.__init__(human=True, time_limit=timeControl, fen=fen, book=True, fun=False) self.fun = False while not self.node.is_game_over(): print(self.__repr__()) if player_colour == self.node.turn: # try: # self.ponder() # except KeyboardInterrupt: # self.node = self.origin # self.user_move() self.user_move() else: self.engine_move() self.display_ending() def perftx(self, n): if n == 0: self.nodes += 1 else: for move in self.ordered_moves(): self.node.push(move) self.perftx(n - 1) self.node.pop() def perft(self, n): self.nodes = 0 self.perftx(n) print(self.nodes) def uci(self): start = input() while start != "uci": start = input() print("id", end="") while True: command = input() if command == "ucinewgame": board = Board() elif command.split()[0] == "position": fen = command[command.index( "fen") + 3: command.index("moves") - 1] moves = command[command.index("moves"):].split() if fen == "startpos": fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1' board = Board(fen) for move in moves: board.push(Move.from_uci(move))
def material_count(board: Board, side: Color) -> int: values = { chess.PAWN: 1, chess.KNIGHT: 3, chess.BISHOP: 3, chess.ROOK: 5, chess.QUEEN: 9 } return sum(len(board.pieces(piece_type, side)) * value for piece_type, value in values.items())
The chess motor reads Polyglot (.bin) files, and uses Stockfish to analyze legal moves' scores. """ from typing import List, Dict import random import chess import chess.engine import chess.polyglot import chess.svg from chess import Board import backend.database as database engine = chess.engine.SimpleEngine.popen_uci('/usr/bin/stockfish') b: Board = Board() cursor = database.find_cursor(b.fen()) def board_step(move_uci: str): """ Updates board and cursor to step by given UCI """ global cursor print('Stepping from:') print(cursor) found = False for reply in cursor['theory'] + cursor['moves']: if reply['uci'] == move_uci: old_cursor = cursor.copy() cursor = database.find_cursor(reply['leads_to']) if cursor is None or \ 'score' not in cursor or \
from chess import Board, King, Queen, Bishop, Knight, Rook, Pawn from interface import ConsoleInterface, TextInterface ui = TextInterface() game = Board(debug=False, outputf=ui.set_msg, inputf=ui.get_player_input, setboardf=ui.set_board) game.start() while game.winner is None: game.display() start, end = game.prompt() game.update(start, end) game.next_turn() print(f"Game over. {game.winner} player wins!")
def minimax( depth: int, board: chess.Board, alpha: float, beta: float, is_maximising_player: bool, ) -> float: """ Core minimax logic. https://en.wikipedia.org/wiki/Minimax """ debug_info["nodes"] += 1 if board.is_checkmate(): # The previous move resulted in checkmate return -MATE_SCORE if is_maximising_player else MATE_SCORE # When the game is over and it's not a checkmate it's a draw # In this case, don't evaluate. Just return a neutral result: zero elif board.is_game_over(): return 0 if depth == 0: return evaluate_board(board) if is_maximising_player: best_move = -float("inf") moves = get_ordered_moves(board) for move in moves: board.push(move) curr_move = minimax(depth - 1, board, alpha, beta, not is_maximising_player) # Each ply after a checkmate is slower, so they get ranked slightly less # We want the fastest mate! if curr_move > MATE_THRESHOLD: curr_move -= 1 elif curr_move < -MATE_THRESHOLD: curr_move += 1 best_move = max( best_move, curr_move, ) board.pop() alpha = max(alpha, best_move) if beta <= alpha: return best_move return best_move else: best_move = float("inf") moves = get_ordered_moves(board) for move in moves: board.push(move) curr_move = minimax(depth - 1, board, alpha, beta, not is_maximising_player) if curr_move > MATE_THRESHOLD: curr_move -= 1 elif curr_move < -MATE_THRESHOLD: curr_move += 1 best_move = min( best_move, curr_move, ) board.pop() beta = min(beta, best_move) if beta <= alpha: return best_move return best_move
def write_json(self, board: chess.Board): """ Writes all of the board info in json """ best_move = self.get_best_move(board) output = OrderedDict([ ('fen', board.fen()), ('fullmoveNumber', board.fullmove_number), ('result', board.result()), ('isGameOver', board.is_game_over()), ('isCheckmate', board.is_checkmate()), ('isStalemate', board.is_stalemate()), ('isInsufficientMaterial', board.is_insufficient_material()), ('isSeventyfiveMoves', board.is_seventyfive_moves()), ('isFivefoldRepetition', board.is_fivefold_repetition()), ('white', OrderedDict([ ('hasKingsideCastlingRights', board.has_kingside_castling_rights(chess.WHITE)), ('hasQueensideCastlingRights', board.has_queenside_castling_rights(chess.WHITE)), ])), ('black', OrderedDict([ ('hasKingsideCastlingRights', board.has_kingside_castling_rights(chess.BLACK)), ('hasQueensideCastlingRights', board.has_queenside_castling_rights(chess.BLACK)), ])), ('turn', OrderedDict([ ('color', 'white' if board.turn is chess.WHITE else 'black'), ('isInCheck', board.is_check()), ('bestMove', best_move), ('legalMoves', [move.uci() for move in board.legal_moves]), ('canClaimDraw', board.can_claim_draw()), ('canClaimFiftyMoves', board.can_claim_fifty_moves()), ('canClaimThreefoldRepetition', board.can_claim_threefold_repetition()), ])), ]) self.finish(output)
configure_logging(level=logging.INFO) else: configure_logging(level=logging.DEBUG) def print_puzzle_pgn(puzzle, pgn_headers=None): puzzle_pgn = puzzle.to_pgn(pgn_headers=pgn_headers) log(Color.MAGENTA, "NEW PUZZLE GENERATED\n") print(Color.CYAN + puzzle_pgn + "\n\n" + Color.ENDC) # load a FEN and try to create a puzzle from it if settings.fen: log(Color.DIM, AnalysisEngine.name()) puzzle = Puzzle(Board(settings.fen)) puzzle.generate(depth=settings.search_depth) if puzzle.is_complete(): print_puzzle_pgn(puzzle) engine.quit() exit(0) # load games from a PGN and scan them for puzzles n_positions = 0 # number of positions considered n_puzzles = 0 # number of puzzles generated game_id = 0 pgn = open(settings.pgn, "r") while game_id < settings.start_index: game = chess.pgn.read_game(pgn)