# region Constants # White Constants W_KIN, W_QUE, W_BIS, W_NHT, W_ROO, W_PAW = range(6) # Black Constans B_KIN, B_QUE, B_BIS, B_NHT, B_ROO, B_PAW = range(6,12) # Turn Constants WHITE, BLACK = 0, 1 # Piece Constants KIN, QUE, BIS, NHT, ROO, PAW = range(6) NO_PIECE = 15 PIECE_TO_STR = ["wK", "wQ", "wB", "wN", "wR", "wP", "bK", "bQ", "bB", "bN", "bR", "bP"] # Square Constants A8, B8, C8, D8, E8, F8, G8, H8 = range(56, 64) A7, B7, C7, D7, E7, F7, G7, H7 = range(48, 56) A6, B6, C6, D6, E6, F6, G6, H6 = range(40, 48) A5, B5, C5, D5, E5, F5, G5, H5 = range(32, 40) A4, B4, C4, D4, E4, F4, G4, H4 = range(24, 32) A3, B3, C3, D3, E3, F3, G3, H3 = range(16, 24) A2, B2, C2, D2, E2, F2, G2, H2 = range(8, 16) A1, B1, C1, D1, E1, F1, G1, H1 = range(8) # Move Flags QUIET = 0 DOUBLE_PAWN_PUSH = 1 KING_CASTLE = 2 QUEEN_CASTLE = 3 CAPTURE = 4 EP_CAPTURE = 5 # Falls Capture +4 (z.B. BIS Capture -> 13) PROMO_KNIGHT = 8 PROMO_BISHOP = 9 PROMO_ROOK = 10 PROMO_QUEEN = 11 # Masks NOT_A = 0xfefefefefefefefe NOT_H = 0x7f7f7f7f7f7f7f7f NOT_AB = 0xfcfcfcfcfcfcfcfc NOT_GH = 0x3f3f3f3f3f3f3f3f FULL_MASK = 0xffffffffffffffff # Attacks Movesets & Rays KIN_ATTACKS = [0] * 64 NHT_ATTACKS = [0] * 64 PAW_ATTACKS = [0] * 128 # 0-63 für Weiße Bauern, 64-127 für Schwarze Bauern RAYS_N = [0] * 64 RAYS_NE = [0] * 64 RAYS_E = [0] * 64 RAYS_SE = [0] * 64 RAYS_S = [0] * 64 RAYS_SW = [0] * 64 RAYS_W = [0] * 64 RAYS_NW = [0] * 64 # endregion class Bitboard(): def __init__(self) -> None: # WHITE 0:King, 1:Queen, 2:Bishop, 3:Knight, 4:Rook, 5:Pawn # BLACK 6:King, 7:Queen, 8:Bishop, 9:Knight, 10:Rook, 11:Pawn self.bitboards = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] self.side_to_move = WHITE self.bitboards[W_KIN] = 1 << E1 self.bitboards[W_QUE] = 1 << D1 self.bitboards[W_BIS] = (1 << C1) | (1 << F1) self.bitboards[W_NHT] = (1 << B1) | (1 << G1) self.bitboards[W_ROO] = (1 << A1) | (1 << H1) self.bitboards[W_PAW] = 0xFF << 8 self.bitboards[B_KIN] = 1 << E8 self.bitboards[B_QUE] = 1 << D8 self.bitboards[B_BIS] = (1 << C8) | (1 << F8) self.bitboards[B_NHT] = (1 << B8) | (1 << G8) self.bitboards[B_ROO] = (1 << A8) | (1 << H8) self.bitboards[B_PAW] = 0xFF << 48 self.all_pieces = [0, 0] self.all_pieces[0] = self.bitboards[W_KIN] | self.bitboards[W_QUE] | self.bitboards[W_BIS] | self.bitboards[W_NHT] | self.bitboards[W_ROO] | self.bitboards[W_PAW] self.all_pieces[1] = self.bitboards[B_KIN] | self.bitboards[B_QUE] | self.bitboards[B_BIS] | self.bitboards[B_NHT] | self.bitboards[B_ROO] | self.bitboards[B_PAW] self.move_list = [] def get_all_moves(self): moves = [] captures = [] turn = self.side_to_move opp = turn ^ 1 turn_offset = turn * 6 opp_offset = opp *6 # region import self & global Varibles for speed optimization bbs = self.bitboards opponent_pcs = self.all_pieces[opp] friendly_pcs = self.all_pieces[turn] occupancy = opponent_pcs | friendly_pcs kin, que, bis, nht, roo, paw, no_piece = KIN, QUE, BIS, NHT, ROO, PAW, NO_PIECE nht_attacks_list, paw_attacks_list = NHT_ATTACKS, PAW_ATTACKS quiet, dpp, k_castle, q_castle, capture, ep_capture = QUIET, DOUBLE_PAWN_PUSH, KING_CASTLE, QUEEN_CASTLE, CAPTURE, EP_CAPTURE promo_knight, promo_bishop, promo_rook, promo_queen = PROMO_KNIGHT, PROMO_BISHOP, PROMO_ROOK, PROMO_QUEEN rays_n, rays_ne, rays_e, rays_se, rays_s, rays_sw, rays_w, rays_nw = RAYS_N, RAYS_NE, RAYS_E, RAYS_SE, RAYS_S, RAYS_SW, RAYS_W, RAYS_NW # endregion # region --- King Moves --- kin_sq = bbs[kin + turn_offset].bit_length() - 1 kin_attacks = KIN_ATTACKS[kin_sq] kin_attacks &= ~friendly_pcs while kin_attacks: to_sq = (kin_attacks & -kin_attacks).bit_length() - 1 to_mask = 1 << to_sq kin_attacks &= (kin_attacks - 1) if to_mask & opponent_pcs: if to_mask & bbs[paw + opp_offset]: captured = paw + opp_offset elif to_mask & bbs[nht + opp_offset]: captured = nht + opp_offset elif to_mask & bbs[bis + opp_offset]: captured = bis + opp_offset elif to_mask & bbs[roo + opp_offset]: captured = roo + opp_offset else: captured = que + opp_offset captures.append(to_sq | (kin_sq << 6) | ((kin + turn_offset) << 12) | (captured << 16) | capture << 20) else: moves.append(to_sq | (kin_sq << 6) | ((kin + turn_offset) << 12) | (no_piece << 16) | quiet << 20) # endregion # region --- Knight Moves --- nht_bb = bbs[nht + turn_offset] while nht_bb: nht_sq = (nht_bb & -nht_bb).bit_length() - 1 nht_bb &= (nht_bb - 1) nht_attacks = nht_attacks_list[nht_sq] nht_attacks &= ~friendly_pcs while nht_attacks: to_sq = (nht_attacks & -nht_attacks).bit_length() - 1 to_mask = 1 << to_sq nht_attacks &= (nht_attacks - 1) if to_mask & opponent_pcs: if to_mask & bbs[paw + opp_offset]: captured = paw + opp_offset elif to_mask & bbs[nht + opp_offset]: captured = nht + opp_offset elif to_mask & bbs[bis + opp_offset]: captured = bis + opp_offset elif to_mask & bbs[roo + opp_offset]: captured = roo + opp_offset else: captured = que + opp_offset captures.append(to_sq | (nht_sq << 6) | ((nht + turn_offset) << 12) | (captured << 16) | capture << 20) else: moves.append(to_sq | (nht_sq << 6) | ((nht + turn_offset) << 12) | (no_piece << 16) | quiet << 20) # endregion # region --- Pawn Moves --- paw_bb = bbs[paw + turn_offset] while paw_bb: paw_sq = (paw_bb & -paw_bb).bit_length() - 1 paw_bb &= (paw_bb -1) paw_attacks = paw_attacks_list[paw_sq + turn_offset] # Irgendwann noch En Passant einfügen paw_attacks &= opponent_pcs while paw_attacks: flag = capture to_sq = (paw_attacks & -paw_attacks).bit_length() - 1 to_mask = 1 << to_sq paw_attacks &= (paw_attacks - 1) if to_mask & bbs[paw + opp_offset]: captured = paw + opp_offset elif to_mask & bbs[nht + opp_offset]: captured = nht + opp_offset elif to_mask & bbs[bis + opp_offset]: captured = bis + opp_offset elif to_mask & bbs[roo + opp_offset]: captured = roo + opp_offset else: captured = que + opp_offset captures.append(to_sq | (paw_sq << 6) | ((paw + turn_offset) << 12) | (captured << 16) | flag << 20) if turn == 0: to_sq = paw_sq + 8 to_mask = 1 << to_sq if not to_mask & occupancy: moves.append(to_sq | (paw_sq << 6) | ((paw + turn_offset) << 12) | (no_piece << 16) | quiet << 20) if 8 <= paw_sq <= 15: to_sq_push = paw_sq + 16 to_mask_push = 1 << to_sq_push if not to_mask_push & occupancy: moves.append(to_sq_push | (paw_sq << 6) | ((paw + turn_offset) << 12) | (no_piece << 16) | dpp << 20) else: to_sq = paw_sq - 8 to_mask = 1 << to_sq if not to_mask & occupancy: moves.append(to_sq | (paw_sq << 6) | ((paw + turn_offset) << 12) | (no_piece << 16) | quiet << 20) if 48 <= paw_sq <= 55: to_sq_push = paw_sq - 16 to_mask_push = 1 << to_sq_push if not to_mask_push & occupancy: moves.append(to_sq_push | (paw_sq << 6) | ((paw + turn_offset) << 12) | (no_piece << 16) | dpp << 20) # endregion Pawn Moves # region --- Bishop Moves --- bis_bb = bbs[bis + turn_offset] while bis_bb: bis_sq = (bis_bb & -bis_bb).bit_length() - 1 bis_bb &= (bis_bb - 1) # region North-East ray = rays_ne[bis_sq] blockers = ray & occupancy if blockers: blocker_sq = (blockers & -blockers).bit_length() - 1 ray_to_del = rays_ne[blocker_sq] bis_attacks = (ray ^ ray_to_del) & ~friendly_pcs else: bis_attacks = ray while bis_attacks: to_sq = (bis_attacks & -bis_attacks).bit_length() - 1 to_mask = 1 << to_sq bis_attacks &= (bis_attacks - 1) if to_mask & opponent_pcs: if to_mask & bbs[paw + opp_offset]: captured = paw + opp_offset elif to_mask & bbs[nht + opp_offset]: captured = nht + opp_offset elif to_mask & bbs[bis + opp_offset]: captured = bis + opp_offset elif to_mask & bbs[roo + opp_offset]: captured = roo + opp_offset else: captured = que + opp_offset captures.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (captured << 16) | capture << 20) else: moves.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (no_piece << 16) | quiet << 20) # endregion # region North-West ray = rays_nw[bis_sq] blockers = ray & occupancy if blockers: blocker_sq = (blockers & -blockers).bit_length() - 1 ray_to_del = rays_nw[blocker_sq] bis_attacks = (ray ^ ray_to_del) & ~friendly_pcs else: bis_attacks = ray while bis_attacks: to_sq = (bis_attacks & -bis_attacks).bit_length() - 1 to_mask = 1 << to_sq bis_attacks &= (bis_attacks - 1) if to_mask & opponent_pcs: if to_mask & bbs[paw + opp_offset]: captured = paw + opp_offset elif to_mask & bbs[nht + opp_offset]: captured = nht + opp_offset elif to_mask & bbs[bis + opp_offset]: captured = bis + opp_offset elif to_mask & bbs[roo + opp_offset]: captured = roo + opp_offset else: captured = que + opp_offset captures.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (captured << 16) | capture << 20) else: moves.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (no_piece << 16) | quiet << 20) # endregion # region South-East ray = rays_se[bis_sq] blockers = ray & occupancy if blockers: blocker_sq = blockers.bit_length() - 1 ray_to_del = rays_se[blocker_sq] bis_attacks = (ray ^ ray_to_del) & ~friendly_pcs else: bis_attacks = ray while bis_attacks: to_sq = (bis_attacks & -bis_attacks).bit_length() - 1 to_mask = 1 << to_sq bis_attacks &= (bis_attacks - 1) if to_mask & opponent_pcs: if to_mask & bbs[paw + opp_offset]: captured = paw + opp_offset elif to_mask & bbs[nht + opp_offset]: captured = nht + opp_offset elif to_mask & bbs[bis + opp_offset]: captured = bis + opp_offset elif to_mask & bbs[roo + opp_offset]: captured = roo + opp_offset else: captured = que + opp_offset captures.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (captured << 16) | capture << 20) else: moves.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (no_piece << 16) | quiet << 20) # endregion # region South-West ray = rays_sw[bis_sq] blockers = ray & occupancy if blockers: blocker_sq = blockers.bit_length() - 1 ray_to_del = rays_sw[blocker_sq] bis_attacks = (ray ^ ray_to_del) & ~friendly_pcs else: bis_attacks = ray while bis_attacks: to_sq = (bis_attacks & -bis_attacks).bit_length() - 1 to_mask = 1 << to_sq bis_attacks &= (bis_attacks - 1) if to_mask & opponent_pcs: if to_mask & bbs[paw + opp_offset]: captured = paw + opp_offset elif to_mask & bbs[nht + opp_offset]: captured = nht + opp_offset elif to_mask & bbs[bis + opp_offset]: captured = bis + opp_offset elif to_mask & bbs[roo + opp_offset]: captured = roo + opp_offset else: captured = que + opp_offset captures.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (captured << 16) | capture << 20) else: moves.append(to_sq | (bis_sq << 6) | ((bis + turn_offset) << 12) | (no_piece << 16) | quiet << 20) # endregion # endregion Bishop Moves def make_move(self, move): # Setup # 6 Bit (0-5): Target (Index: 0-63) # 6 Bit (6-11): Source (Index: 0-63) # 4 Bit (12-15): Piece moved (0-11: W_KIN-B_PAW) # 4 Bit (16-19): Piece Captured (0-11: W_KIN-B_PAW, 15: Nothing) # Bit 20-23: Flags (4 Bit) # 0000: 0 Quiet 0100: 4 Capture 1000: 8 Promo NHT # 0001: 1 Double Pawn Push 0101: 5 En Passant 1001: 9 Promo BIS # 0010: 2 King Castle 0110: 1010: 10 Promo ROO # 0011: 3 Queen Castle 0111: 1011: 11 Promo QUE end = move & 0x3F start = (move >> 6) & 0x3F piece = (move >> 12) & 0xF captured = (move >> 16) & 0xF flag = (move >> 20) & 0xF turn = self.side_to_move self.bitboards[piece] ^= (1 << start) | (1 << end) self.all_pieces[turn] ^= (1 << start) | (1 << end) if flag == CAPTURE: self.bitboards[captured] ^= (1 << end) self.all_pieces[turn ^ 1] ^= (1 << end) self.side_to_move ^= 1 # Seite Wechseln def print_board(self): board = [" ."] * 64 for pce in range(12): locations = self.bitboards[pce] while locations: index = (locations & -locations).bit_length() - 1 board[index] = PIECE_TO_STR[pce] locations &= (locations - 1) for c in range(7, -1, -1): print(f"{c+1} ", end="") for r in range(8): print(board[r + (c * 8)], end=" ") print("") print(" A B C D E F G H /n") def initialize(): ''' Calculate all Attack patterns and Rays ''' for sq in range(64): bit = 1 << sq # --- Pieces --- # King kin_attacks = (bit & NOT_A) << 7 | bit << 8 | (bit & NOT_H) << 9 | (bit & NOT_H) << 1 | \ (bit & NOT_H) >> 7 | bit >> 8 | (bit & NOT_A) >> 9 | (bit & NOT_A) >> 1 KIN_ATTACKS[sq] = kin_attacks & FULL_MASK # Knight nht_attacks = (bit & NOT_AB) << 6 | (bit & NOT_A) << 15 | (bit & NOT_H) << 17 | (bit & NOT_GH) << 10 | \ (bit & NOT_GH) >> 6 | (bit & NOT_H) >> 15 | (bit & NOT_A) >> 17 | (bit & NOT_AB) >> 10 NHT_ATTACKS[sq] = nht_attacks & FULL_MASK # Pawns Attack w_attacks = (bit & NOT_A) << 7 | (bit & NOT_H) << 9 PAW_ATTACKS[sq] = w_attacks & FULL_MASK b_attacks = (bit & NOT_A) >> 9 | (bit & NOT_H) >> 7 PAW_ATTACKS[sq + 64] = b_attacks & FULL_MASK # --- Rays --- r, f = sq // 8, sq % 8 # North (r+) RAYS_N[sq] = 0 for i in range(r + 1, 8): RAYS_N[sq] |= (1 << (i * 8 + f)) # South (r-) RAYS_S[sq] = 0 for i in range(r - 1, -1, -1): RAYS_S[sq] |= (1 << (i * 8 + f)) # East (f+) RAYS_E[sq] = 0 for i in range(f + 1, 8): RAYS_E[sq] |= (1 << (r * 8 + i)) # West (f-) RAYS_W[sq] = 0 for i in range(f - 1, -1, -1): RAYS_W[sq] |= (1 << (r * 8 + i)) # North-East (r+, f+) RAYS_NE[sq] = 0 i, j = r + 1, f + 1 while i < 8 and j < 8: RAYS_NE[sq] |= (1 << (i * 8 + j)) i += 1; j += 1 # North-West (r+, f-) RAYS_NW[sq] = 0 i, j = r + 1, f - 1 while i < 8 and j >= 0: RAYS_NW[sq] |= (1 << (i * 8 + j)) i += 1; j -= 1 # South-East (r-, f+) RAYS_SE[sq] = 0 i, j = r - 1, f + 1 while i >= 0 and j < 8: RAYS_SE[sq] |= (1 << (i * 8 + j)) i -= 1; j += 1 # South-West (r-, f-) RAYS_SW[sq] = 0 i, j = r - 1, f - 1 while i >= 0 and j >= 0: RAYS_SW[sq] |= (1 << (i * 8 + j)) i -= 1; j -= 1 initialize() board = Bitboard() board.print_board() board.make_move(1835036) #e2-e4 board.print_board() board.make_move(1837347) #(d7-d5) board.print_board() board.make_move(4590371) #(exd5 - Capture!) board.print_board() board.make_move(4245795) #(Qxd5 - Capture!) board.print_board() board.make_move(804882) #(Nc3) board.print_board() board.make_move(829216) #(Qa5) ''' Board Index: a b c d e f g h 8 [56] [57] [58] [59] [60] [61] [62] [63] 7 [48] [49] [50] [51] [52] [53] [54] [55] 6 [40] [41] [42] [43] [44] [45] [46] [47] 5 [32] [33] [34] [35] [36] [37] [38] [39] 4 [24] [25] [26] [27] [28] [29] [30] [31] 3 [16] [17] [18] [19] [20] [21] [22] [23] 2 [08] [09] [10] [11] [12] [13] [14] [15] 1 [00] [01] [02] [03] [03] [05] [06] [07] '''