diff --git a/chess/chess_64.py b/chess/chess_64.py index db6c3f1..369813c 100644 --- a/chess/chess_64.py +++ b/chess/chess_64.py @@ -1,3 +1,5 @@ +# region Constants + # White Constants W_KIN, W_QUE, W_BIS, W_NHT, W_ROO, W_PAW = range(6) # Black Constans @@ -8,6 +10,18 @@ WHITE, BLACK = 0, 1 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) + # 0 = Normaler Zug (Quiet Move) QUIET = 0 @@ -39,22 +53,29 @@ PROMO_B_CAPTURE = 13 PROMO_R_CAPTURE = 14 PROMO_Q_CAPTURE = 15 -# 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) - +# 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: @@ -79,76 +100,307 @@ class Bitboard(): self.bitboards[B_ROO] = (1 << A8) | (1 << H8) self.bitboards[B_PAW] = 0xFF << 48 - self.all_pieces = [] + 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[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): - turn_offset = self.side_to_move * 6 - opponent_pcs = self.all_pieces[not self.side_to_move] - friendly_pcs = self.all_pieces[self.side_to_move] + moves = [] + captures = [] + turn = self.side_to_move + opp = turn ^ 1 + turn_offset = turn * 6 + opp_offset = opp *6 + # import local Varibles + bbs = self.bitboards + opponent_pcs = self.all_pieces[opp] + friendly_pcs = self.all_pieces[turn] + occupancy = opponent_pcs | friendly_pcs + + # 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]: + capture = PAW + opp_offset + elif to_mask & bbs[NHT + opp_offset]: + capture = NHT + opp_offset + elif to_mask & bbs[BIS + opp_offset]: + capture = BIS + opp_offset + elif to_mask & bbs[ROO + opp_offset]: + capture = ROO + opp_offset + else: + capture = QUE + opp_offset + captures.append(to_sq | (kin_sq << 6) | ((KIN + turn_offset) << 12) | (capture << 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[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]: + capture = PAW + opp_offset + elif to_mask & bbs[NHT + opp_offset]: + capture = NHT + opp_offset + elif to_mask & bbs[BIS + opp_offset]: + capture = BIS + opp_offset + elif to_mask & bbs[ROO + opp_offset]: + capture = ROO + opp_offset + else: + capture = QUE + opp_offset + captures.append(to_sq | (nht_sq << 6) | ((NHT + turn_offset) << 12) | (capture << 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[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]: + capture = PAW + opp_offset + elif to_mask & bbs[NHT + opp_offset]: + capture = NHT + opp_offset + elif to_mask & bbs[BIS + opp_offset]: + capture = BIS + opp_offset + elif to_mask & bbs[ROO + opp_offset]: + capture = ROO + opp_offset + else: + capture = QUE + opp_offset + captures.append(to_sq | (paw_sq << 6) | ((PAW + turn_offset) << 12) | (capture << 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) | DOUBLE_PAWN_PUSH << 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) | DOUBLE_PAWN_PUSH << 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]: + capture = PAW + opp_offset + elif to_mask & bbs[NHT + opp_offset]: + capture = NHT + opp_offset + elif to_mask & bbs[BIS + opp_offset]: + capture = BIS + opp_offset + elif to_mask & bbs[ROO + opp_offset]: + capture = ROO + opp_offset + else: + capture = QUE + opp_offset + captures.append(to_sq | (bis_sq << 6) | ((BIS + turn_offset) << 12) | (capture << 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]: + capture = PAW + opp_offset + elif to_mask & bbs[NHT + opp_offset]: + capture = NHT + opp_offset + elif to_mask & bbs[BIS + opp_offset]: + capture = BIS + opp_offset + elif to_mask & bbs[ROO + opp_offset]: + capture = ROO + opp_offset + else: + capture = QUE + opp_offset + captures.append(to_sq | (bis_sq << 6) | ((BIS + turn_offset) << 12) | (capture << 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]: + capture = PAW + opp_offset + elif to_mask & bbs[NHT + opp_offset]: + capture = NHT + opp_offset + elif to_mask & bbs[BIS + opp_offset]: + capture = BIS + opp_offset + elif to_mask & bbs[ROO + opp_offset]: + capture = ROO + opp_offset + else: + capture = QUE + opp_offset + captures.append(to_sq | (bis_sq << 6) | ((BIS + turn_offset) << 12) | (capture << 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]: + capture = PAW + opp_offset + elif to_mask & bbs[NHT + opp_offset]: + capture = NHT + opp_offset + elif to_mask & bbs[BIS + opp_offset]: + capture = BIS + opp_offset + elif to_mask & bbs[ROO + opp_offset]: + capture = ROO + opp_offset + else: + capture = QUE + opp_offset + captures.append(to_sq | (bis_sq << 6) | ((BIS + turn_offset) << 12) | (capture << 16) | CAPTURE << 20) + else: + moves.append(to_sq | (bis_sq << 6) | ((BIS + turn_offset) << 12) | (NO_PIECE << 16) | QUIET << 20) + # endregion + # endregion Bishop Moves - move_list = [] - - # King Moves - kin_sq = self.bitboards[KIN + turn_offset].bit_length() - 1 - kin_moves = KIN_ATTACKS[kin_sq] - kin_moves &= ~friendly_pcs - - while kin_moves: - flag = 0 - to_sq = (kin_moves & -kin_moves).bit_length() - 1 - move_list.append(to_sq | (kin_sq << 6) | flag << 12) def make_move(self, move): # Setup - # Bit 0-5: Target (6 Bit, Index: 0-63) - # Bit 6-11: Source (6 Bit, Index: 0-63) - # Bit 12-15: Piece moved (4 Bit) 0-11: W_KIN-B_PAW - # Bit 16-19: Piece Captured (4 Bit) 0-11: W_KIN-B_PAW, 15: Nothing - # + # 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 - pass + 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 encode_move(start_sq, end_sq, flag): - move = 0 - move |= end_sq - move |= start_sq << 6 - move |= flag << 12 - return move - + 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) - - - - -# move_constants.py - -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 + 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(): ''' @@ -167,7 +419,7 @@ def initialize(): (bit & NOT_GH) >> 6 | (bit & NOT_H) >> 15 | (bit & NOT_A) >> 17 | (bit & NOT_AB) >> 10 NHT_ATTACKS[sq] = nht_attacks & FULL_MASK - # Pawns + # Pawns Attack w_attacks = (bit & NOT_A) << 7 | (bit & NOT_H) << 9 PAW_ATTACKS[sq] = w_attacks & FULL_MASK @@ -225,6 +477,10 @@ def initialize(): RAYS_SW[sq] |= (1 << (i * 8 + j)) i -= 1; j -= 1 +initialize() +board = Bitboard() +board.print_board() + ''' Board Index: a b c d e f g h 8 [56] [57] [58] [59] [60] [61] [62] [63] @@ -236,6 +492,4 @@ def initialize(): 2 [08] [09] [10] [11] [12] [13] [14] [15] 1 [00] [01] [02] [03] [03] [05] [06] [07] -''' - - +''' \ No newline at end of file