708 lines
26 KiB
Python
708 lines
26 KiB
Python
from cmd import PROMPT
|
|
import os
|
|
# 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
|
|
|
|
class COL():
|
|
FG_WHITE = "\033[38;5;253m"
|
|
FG_BLACK = "\033[38;5;232m"
|
|
BG_WHITE = "\033[48;5;230m"
|
|
BG_DARK = "\033[48;5;71m"
|
|
BG_MOVE = "\033[48;5;249m"
|
|
RESET = "\033[0m"
|
|
|
|
PIECE_TO_STR = ["wK", "wQ", "wB", "wN", "wR", "wP",
|
|
"bK", "bQ", "bB", "bN", "bR", "bP"]
|
|
PIECE_TO_SYM = ["♔", "♕", "♗", "♘", "♖", "♙",
|
|
"♚", "♛", "♝", "♞", "♜", "♟"]
|
|
|
|
# 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)
|
|
|
|
# Convert Piece to number
|
|
PCE_TO_NUM = {"K":0, "Q":1, "B":2, "N":3, "R":4, "P":5}
|
|
|
|
# Convert Field to index
|
|
STR_TO_SQ = {}
|
|
files = "ABCDEFGH" # Oder "abcdefgh" je nach UCI Standard
|
|
ranks = "12345678"
|
|
|
|
for r_idx, r in enumerate(ranks):
|
|
for f_idx, f in enumerate(files):
|
|
# Berechne Index (Little Endian: A1=0, B1=1 ... H8=63)
|
|
sq = r_idx * 8 + f_idx
|
|
key = f + r # z.B. "A1"
|
|
STR_TO_SQ[key] = sq
|
|
|
|
# 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
|
|
|
|
clear = lambda: os.system('clear')
|
|
|
|
# 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.playing = True
|
|
|
|
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
|
|
|
|
# region --- Rook Moves ---
|
|
|
|
roo_bb = bbs [roo + turn_offset]
|
|
while roo_bb:
|
|
roo_sq = (roo_bb & -roo_bb).bit_length() -1
|
|
roo_bb &= (roo_bb -1)
|
|
|
|
# region North
|
|
ray = rays_n[roo_sq]
|
|
blockers = ray & occupancy
|
|
if blockers:
|
|
blocker_sq = blockers.bit_length() - 1
|
|
ray_to_del = rays_n[blocker_sq]
|
|
roo_attacks = (ray ^ ray_to_del) & ~friendly_pcs
|
|
else:
|
|
roo_attacks = ray
|
|
|
|
while roo_attacks:
|
|
to_sq = (roo_attacks & -roo_attacks).bit_length() - 1
|
|
to_mask = 1 << to_sq
|
|
roo_attacks &= (roo_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 | (roo_sq << 6) | ((roo + turn_offset) << 12) | (captured << 16) | capture << 20)
|
|
else:
|
|
moves.append(to_sq | (roo_sq << 6) | ((roo + turn_offset) << 12) | (no_piece << 16) | quiet << 20)
|
|
# endregion
|
|
|
|
# region East
|
|
ray = rays_e[roo_sq]
|
|
blockers = ray & occupancy
|
|
if blockers:
|
|
blocker_sq = blockers.bit_length() - 1
|
|
ray_to_del = rays_e[blocker_sq]
|
|
roo_attacks = (ray ^ ray_to_del) & ~friendly_pcs
|
|
else:
|
|
roo_attacks = ray
|
|
|
|
while roo_attacks:
|
|
to_sq = (roo_attacks & -roo_attacks).bit_length() - 1
|
|
to_mask = 1 << to_sq
|
|
roo_attacks &= (roo_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 | (roo_sq << 6) | ((roo + turn_offset) << 12) | (captured << 16) | capture << 20)
|
|
else:
|
|
moves.append(to_sq | (roo_sq << 6) | ((roo + turn_offset) << 12) | (no_piece << 16) | quiet << 20)
|
|
# endregion
|
|
|
|
# region South
|
|
ray = rays_s[roo_sq]
|
|
blockers = ray & occupancy
|
|
if blockers:
|
|
blocker_sq = blockers.bit_length() - 1
|
|
ray_to_del = rays_s[blocker_sq]
|
|
roo_attacks = (ray ^ ray_to_del) & ~friendly_pcs
|
|
else:
|
|
roo_attacks = ray
|
|
|
|
while roo_attacks:
|
|
to_sq = (roo_attacks & -roo_attacks).bit_length() - 1
|
|
to_mask = 1 << to_sq
|
|
roo_attacks &= (roo_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 | (roo_sq << 6) | ((roo + turn_offset) << 12) | (captured << 16) | capture << 20)
|
|
else:
|
|
moves.append(to_sq | (roo_sq << 6) | ((roo + turn_offset) << 12) | (no_piece << 16) | quiet << 20)
|
|
# endregion
|
|
|
|
# region West
|
|
ray = rays_w[roo_sq]
|
|
blockers = ray & occupancy
|
|
if blockers:
|
|
blocker_sq = blockers.bit_length() - 1
|
|
ray_to_del = rays_w[blocker_sq]
|
|
roo_attacks = (ray ^ ray_to_del) & ~friendly_pcs
|
|
else:
|
|
roo_attacks = ray
|
|
|
|
while roo_attacks:
|
|
to_sq = (roo_attacks & -roo_attacks).bit_length() - 1
|
|
to_mask = 1 << to_sq
|
|
roo_attacks &= (roo_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 | (roo_sq << 6) | ((roo + turn_offset) << 12) | (captured << 16) | capture << 20)
|
|
else:
|
|
moves.append(to_sq | (roo_sq << 6) | ((roo + turn_offset) << 12) | (no_piece << 16) | quiet << 20)
|
|
# endregion
|
|
|
|
|
|
|
|
# endregion Rook Moves
|
|
|
|
|
|
|
|
return captures, 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.move_list.append(move)
|
|
|
|
self.side_to_move ^= 1 # Seite Wechseln
|
|
|
|
def encode_and_make_move(self, move_str: str):
|
|
piece = move_str[0]
|
|
start = move_str[1:3]
|
|
end = move_str[3:5]
|
|
|
|
piece_int = (PCE_TO_NUM[piece.upper()] + 6) if self.side_to_move else PCE_TO_NUM[piece.upper()]
|
|
start_int = STR_TO_SQ[start.upper()]
|
|
end_int = STR_TO_SQ[end.upper()]
|
|
|
|
move_input = end_int | start_int << 6 | piece_int << 12
|
|
|
|
captures, moves = self.get_all_moves()
|
|
|
|
for move in captures:
|
|
if (move & 0xFFFF) == (move_input & 0xFFFF):
|
|
self.make_move(move)
|
|
return True
|
|
|
|
for move in moves:
|
|
if (move & 0xFFFF) == (move_input & 0xFFFF):
|
|
self.make_move(move)
|
|
return True
|
|
return False
|
|
|
|
def print_board(self):
|
|
board = [" "] * 64
|
|
if self.move_list:
|
|
move = self.move_list[-1]
|
|
last_sq = (move >> 6) & 0x3F
|
|
board[last_sq] = f"{COL.BG_MOVE} "
|
|
for pce in range(12):
|
|
locations = self.bitboards[pce]
|
|
while locations:
|
|
index = (locations & -locations).bit_length() - 1
|
|
board[index] = PIECE_TO_SYM[pce]
|
|
locations &= (locations - 1)
|
|
|
|
for c in range(7, -1, -1):
|
|
print(f"{c+1} ", end="")
|
|
for r in range(8):
|
|
|
|
bg_col = COL.BG_WHITE if (r + c) % 2 != 0 else COL.BG_DARK
|
|
#fg_col = COL.FG_WHITE if (board[r + (c * 8)][0]) != "b" else COL.FG_BLACK
|
|
|
|
print(f"{bg_col}{COL.FG_BLACK}{board[r + (c * 8)]}", end=" ")
|
|
|
|
|
|
print(COL.RESET)
|
|
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()
|
|
turn = 0
|
|
text = f"Please input move: "
|
|
while board.playing:
|
|
clear()
|
|
print(f"{COL.RESET}\n ---- CHESS ---- Turn: {turn}")
|
|
board.print_board()
|
|
move = input(text)
|
|
if move == "exit":
|
|
break
|
|
if len(move) !=5 or not board.encode_and_make_move(move):
|
|
text = "Move not possible new move: "
|
|
continue
|
|
|
|
|
|
turn += 1
|
|
|
|
|
|
|
|
|
|
''' 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]
|
|
|
|
''' |