Reinitialize repository and add working move generation for all pieces

This commit is contained in:
Moritz Eigenauer 2025-11-12 17:01:12 +01:00
commit 951a8bbec6
28 changed files with 3373 additions and 0 deletions

View file

@ -0,0 +1,60 @@
#!/usr/bin/env python3
def generate_king_attacks():
"""
Generates a list of 64 u64 bitboards, where each bitboard represents
the squares a king can attack from a given source square.
Uses Little-Endian File-Rank (LEFR) mapping: A1=0, H1=7, A2=8, ..., H8=63.
"""
all_attacks = []
# All 8 possible king move offsets (delta_file, delta_rank)
king_moves = [
(0, 1), (0, -1), (1, 0), (-1, 0), # Orthogonal
(1, 1), (1, -1), (-1, 1), (-1, -1) # Diagonal
]
for sq in range(64):
attacks_bb = 0
# Calculate rank and file for the source square
rank = sq // 8
file = sq % 8
for df, dr in king_moves:
target_rank = rank + dr
target_file = file + df
# Check if the target square is on the board
if 0 <= target_rank <= 7 and 0 <= target_file <= 7:
# Convert target rank and file back to a square index
target_sq = target_rank * 8 + target_file
# Set the corresponding bit in the bitboard
attacks_bb |= (1 << target_sq)
all_attacks.append(attacks_bb)
return all_attacks
def print_rust_array(attacks, const_name):
"""
Prints the list of attack bitboards as a Rust array.
"""
print(f"pub const {const_name}: [u64; 64] = [")
for i, attacks_bb in enumerate(attacks):
# Format as 64-bit zero-padded binary string
raw_binary_string = f"{attacks_bb:064b}"
# Insert underscores every 8 bits for readability
chunks = [raw_binary_string[i:i+8] for i in range(0, 64, 8)]
binary_string = f"0b{'_'.join(chunks)}"
# Correctly index the string 'ABCDEFGH'
print(f" {binary_string}, // Square {i} ({'ABCDEFGH'[i%8]}{i//8 + 1})")
print("];")
if __name__ == "__main__":
attacks = generate_king_attacks()
print_rust_array(attacks, "KING_ATTACKS")

View file

@ -0,0 +1,60 @@
#!/usr/bin/env python3
def generate_knight_attacks():
"""
Generates a list of 64 u64 bitboards, where each bitboard represents
the squares a knight can attack from a given source square.
Uses Little-Endian File-Rank (LEFR) mapping: A1=0, H1=7, A2=8, ..., H8=63.
"""
all_attacks = []
# All 8 possible knight move offsets (delta_file, delta_rank)
knight_moves = [
(1, 2), (1, -2), (-1, 2), (-1, -2),
(2, 1), (2, -1), (-2, 1), (-2, -1)
]
for sq in range(64):
attacks_bb = 0
# Calculate rank and file for the source square
rank = sq // 8
file = sq % 8
for df, dr in knight_moves:
target_rank = rank + dr
target_file = file + df
# Check if the target square is on the board
if 0 <= target_rank <= 7 and 0 <= target_file <= 7:
# Convert target rank and file back to a square index
target_sq = target_rank * 8 + target_file
# Set the corresponding bit in the bitboard
attacks_bb |= (1 << target_sq)
all_attacks.append(attacks_bb)
return all_attacks
def print_rust_array(attacks):
"""
Prints the list of attack bitboards as a Rust array.
"""
print("pub const KNIGHT_ATTACKS: [u64; 64] = [")
for i, attacks_bb in enumerate(attacks):
# Format as 64-bit zero-padded binary string
raw_binary_string = f"{attacks_bb:064b}"
# Insert underscores every 8 bits for readability
chunks = [raw_binary_string[i:i+8] for i in range(0, 64, 8)]
binary_string = f"0b{'_'.join(chunks)}"
# Correctly index the string 'ABCDEFGH'
print(f" {binary_string}, // Square {i} ({'ABCDEFGH'[i%8]}{i//8 + 1})")
print("];")
if __name__ == "__main__":
attacks = generate_knight_attacks()
print_rust_array(attacks)

View file

@ -0,0 +1,87 @@
#!/usr/bin/env python3
def generate_pawn_attacks():
"""
Generates a list of 64 u64 bitboards for each color (White, Black),
where each bitboard represents the squares a pawn can attack from
a given source square.
Uses Little-Endian File-Rank (LEFR) mapping: A1=0, H1=7, A2=8, ..., H8=63.
Returns:
[[u64; 64]; 2] (represented as a list of two lists)
Index 0: White attacks
Index 1: Black attacks
"""
# all_attacks[0] = White, all_attacks[1] = Black
all_attacks = [[], []]
for sq in range(64):
rank = sq // 8
file = sq % 8
white_attacks_bb = 0
black_attacks_bb = 0
# --- White Attacks (Index 0) ---
# Pawns attack "up" the board (increasing square index)
# We only generate attacks if the pawn is not on the 8th rank
if rank < 7:
# Attack Up-Left (e.g., B2 -> A3)
# Not possible if pawn is on the A-file
if file > 0:
white_attacks_bb |= (1 << (sq + 7))
# Attack Up-Right (e.g., B2 -> C3)
# Not possible if pawn is on the H-file
if file < 7:
white_attacks_bb |= (1 << (sq + 9))
# --- Black Attacks (Index 1) ---
# Pawns attack "down" the board (decreasing square index)
# We only generate attacks if the pawn is not on the 1st rank
if rank > 0:
# Attack Down-Left (e.g., G7 -> F6)
# Not possible if pawn is on the A-file
if file > 0:
black_attacks_bb |= (1 << (sq - 9))
# Attack Down-Right (e.g., G7 -> H6)
# Not possible if pawn is on the H-file
if file < 7:
black_attacks_bb |= (1 << (sq - 7))
all_attacks[0].append(white_attacks_bb)
all_attacks[1].append(black_attacks_bb)
return all_attacks
def print_rust_array(attacks_by_color, const_name):
"""
Prints the list of attack bitboards as a nested Rust array.
"""
print(f"pub const {const_name}: [[u64; 64]; 2] = [")
# --- Print White Attacks ---
print(" [ // Color 0: White")
for i, attacks_bb in enumerate(attacks_by_color[0]):
raw_binary_string = f"{attacks_bb:064b}"
chunks = [raw_binary_string[i:i+8] for i in range(0, 64, 8)]
binary_string = f"0b{'_'.join(chunks)}"
print(f" {binary_string}, // Square {i} ({'ABCDEFGH'[i%8]}{i//8 + 1})")
print(" ],")
# --- Print Black Attacks ---
print(" [ // Color 1: Black")
for i, attacks_bb in enumerate(attacks_by_color[1]):
raw_binary_string = f"{attacks_bb:064b}"
chunks = [raw_binary_string[i:i+8] for i in range(0, 64, 8)]
binary_string = f"0b{'_'.join(chunks)}"
print(f" {binary_string}, // Square {i} ({'ABCDEFGH'[i%8]}{i//8 + 1})")
print(" ]")
print("];")
if __name__ == "__main__":
attacks = generate_pawn_attacks()
print_rust_array(attacks, "PAWN_ATTACKS")

View file

@ -0,0 +1,110 @@
#!/usr/bin/env python3
def generate_premasks():
"""
Generiert relevante Belegungsmasken (relevant occupancy masks) für
Türme (Rooks) und Läufer (Bishops) auf allen 64 Feldern.
Diese Masken enthalten alle Felder zwischen der Figur und dem Rand,
ABER AUSSCHLIESSLICH der Randfelder selbst.
"""
all_rook_masks = []
all_bishop_masks = []
# Richtungen für beide Figurentypen
rook_directions = [
(1, 0), # Hoch
(-1, 0), # Runter
(0, 1), # Rechts
(0, -1) # Links
]
bishop_directions = [
(1, 1), # Hoch-Rechts
(1, -1), # Hoch-Links
(-1, 1), # Runter-Rechts
(-1, -1) # Runter-Links
]
for sq in range(64):
rook_mask_bb = 0
bishop_mask_bb = 0
rank = sq // 8
file = sq % 8
# --- 1. Turm (Rook) Masken-Generierung ---
# (Dies ist die korrigierte Logik)
for dr, df in rook_directions:
target_rank = rank + dr
target_file = file + df
# Schleife, solange wir auf dem Brett sind (0-7)
while 0 <= target_rank <= 7 and 0 <= target_file <= 7:
is_relevant = False
# Prüfen, ob das Feld *vor* dem Rand liegt.
if df != 0: # Horizontale Bewegung
if 1 <= target_file <= 6: # Files 'b' bis 'g'
is_relevant = True
elif dr != 0: # Vertikale Bewegung
if 1 <= target_rank <= 6: # Ranks 2 bis 7
is_relevant = True
if is_relevant:
target_sq = target_rank * 8 + target_file
rook_mask_bb |= (1 << target_sq)
# Zum nächsten Feld in dieser Richtung
target_rank += dr
target_file += df
all_rook_masks.append(rook_mask_bb)
# --- 2. Läufer (Bishop) Masken-Generierung ---
# (Diese Logik war in deinem "Rook"-Skript und ist hier korrekt)
for dr, df in bishop_directions:
target_rank = rank + dr
target_file = file + df
# Schleife, solange wir *von allen* Rändern entfernt sind (1-6)
while 1 <= target_rank <= 6 and 1 <= target_file <= 6:
target_sq = target_rank * 8 + target_file
bishop_mask_bb |= (1 << target_sq)
# Zum nächsten Feld in dieser Richtung
target_rank += dr
target_file += df
all_bishop_masks.append(bishop_mask_bb)
# Gibt beide Listen als Tupel zurück
return all_rook_masks, all_bishop_masks
def print_rust_array(attacks, const_name):
"""
Gibt die Liste der Bitboards als Rust-Array aus.
"""
print(f"pub const {const_name}: [u64; 64] = [")
for i, attacks_bb in enumerate(attacks):
# Formatieren als 64-Bit binärer String mit Nullen
raw_binary_string = f"{attacks_bb:064b}"
# Unterstriche alle 8 Bits zur Lesbarkeit einfügen
chunks = [raw_binary_string[j:j+8] for j in range(0, 64, 8)]
binary_string = f"0b{'_'.join(chunks)}"
# Index 'ABCDEFGH' korrekt zuordnen
print(f" {binary_string}, // Square {i} ({'ABCDEFGH'[i%8]}{i//8 + 1})")
print("];")
if __name__ == "__main__":
# Entpackt das Tupel mit beiden Masken-Listen
rook_masks, bishop_masks = generate_premasks()
print("--- ROOK MASKS ---")
print_rust_array(rook_masks, "ROOK_RELEVANT_OCCUPANCY")
print("\n--- BISHOP MASKS ---")
print_rust_array(bishop_masks, "BISHOP_RELEVANT_OCCUPANCY")

View file

@ -0,0 +1,197 @@
import tkinter as tk
class ChessBitboardApp:
def __init__(self, root):
self.root = root
self.root.title("Chess Bitboard Generator")
# The bitboard, stored as a 64-bit integer
self.bitboard = 0
# A dictionary to keep track of squares and their marked state
# Key: (row, col), Value: {
# 'widget': tk.Frame, 'label_alg': tk.Label, 'label_idx': tk.Label,
# 'marked': bool, 'original_color': str
# }
self.squares = {}
# --- Create the GUI ---
# Frame for the chessboard
board_frame = tk.Frame(root)
board_frame.pack()
# Create the 8x8 grid of squares
# We loop from row 7 (rank 8) down to 0 (rank 1) for visual layout
for r in range(7, -1, -1): # 7, 6, 5, ... 0
for c in range(8): # 0, 1, 2, ... 7
# Determine the square's original color
is_light_square = (r + c) % 2 == 1
original_color = "#F0D9B5" if is_light_square else "#B58863"
# Create the square as a Frame
square = tk.Frame(
board_frame,
width=50,
height=50,
bg=original_color,
relief="sunken",
borderwidth=1
)
# Make frame *not* resize to labels
square.pack_propagate(False) # Use pack_propagate since we use place/pack inside
square.grid(row=7 - r, column=c)
# --- Add labels to the square (Feature 2) ---
algebraic_not = f"{'abcdefgh'[c]}{r + 1}"
bit_index = r * 8 + c
label_alg = tk.Label(square, text=algebraic_not, bg=original_color, font=("Arial", 8, "bold"))
label_alg.place(x=2, y=1) # Use place to position label
label_idx = tk.Label(square, text=f"{bit_index}", bg=original_color, font=("Arial", 8))
label_idx.place(relx=1.0, rely=1.0, anchor='se', x=-2, y=-1) # Use place for bottom-right
# Bind the click event to all parts of the square
click_lambda = lambda event, row=r, col=c: self.on_square_click(event, row, col)
square.bind("<Button-1>", click_lambda)
label_alg.bind("<Button-1>", click_lambda)
label_idx.bind("<Button-1>", click_lambda)
# Store the square's info
self.squares[(r, c)] = {
'widget': square,
'label_alg': label_alg,
'label_idx': label_idx,
'marked': False,
'original_color': original_color
}
# Frame for the bitboard display
info_frame = tk.Frame(root, pady=10)
info_frame.pack()
# --- Make display labels copyable (Feature 1) ---
self.binary_var = tk.StringVar()
self.int_var = tk.StringVar()
tk.Label(info_frame, text="Binary:").pack()
self.binary_label = tk.Entry(
info_frame,
textvariable=self.binary_var,
state="readonly",
font=("Courier", 10),
width=77 # 64 chars + 15 underscores + 'b'
)
self.binary_label.pack()
tk.Label(info_frame, text="Integer:").pack()
self.int_label = tk.Entry(
info_frame,
textvariable=self.int_var,
state="readonly",
font=("Courier", 12, "bold"),
width=22
)
self.int_label.pack()
# --- Add Entry for pasting bitboard (Feature 3) ---
input_frame = tk.Frame(root, pady=5)
input_frame.pack()
tk.Label(input_frame, text="Paste Bitboard (int) and Press Enter:").pack(side=tk.LEFT)
self.paste_entry = tk.Entry(input_frame, font=("Courier", 12), width=22)
self.paste_entry.pack(side=tk.LEFT, padx=5)
self.paste_entry.bind("<Return>", self.on_paste_bitboard)
# Initialize display
self.update_display()
def on_square_click(self, event, row, col):
"""Handles the click event for a square."""
square_info = self.squares[(row, col)]
# Toggle the marked state
square_info['marked'] = not square_info['marked']
self.update_square_visuals(row, col)
# Recalculate the bitboard and update the display
self.recalculate_bitboard()
self.update_display()
def update_square_visuals(self, row, col):
"""Updates a single square's color based on its 'marked' state."""
square_info = self.squares[(row, col)]
is_marked = square_info['marked']
new_color = "#50C878" if is_marked else square_info['original_color']
label_fg_color = "white" if is_marked else "black" # Make text white on green
square_info['widget'].config(bg=new_color)
square_info['label_alg'].config(bg=new_color, fg=label_fg_color)
square_info['label_idx'].config(bg=new_color, fg=label_fg_color)
def recalculate_bitboard(self):
"""Recalculates the 64-bit integer from the marked squares."""
self.bitboard = 0
for r in range(8):
for c in range(8):
if self.squares[(r, c)]['marked']:
bit_index = r * 8 + c
self.bitboard |= (1 << bit_index)
def update_display(self):
"""Updates the binary and integer labels."""
# Update the integer label
self.int_var.set(f"{self.bitboard}")
# Update the binary label
binary_string = f"{self.bitboard:064b}"
formatted_binary = "b" + "_".join(binary_string[i:i+8] for i in range(0, 64, 4))
self.binary_var.set(f"{formatted_binary}")
def on_paste_bitboard(self, event):
"""Handles the 'Enter' key press in the paste entry box."""
try:
# Get text from entry and convert to integer
new_bitboard = int(self.paste_entry.get())
if 0 <= new_bitboard <= (1 << 64) - 1:
self.bitboard = new_bitboard
# Update the board visuals from the new bitboard
self.update_board_from_bitboard()
# Update the display labels
self.update_display()
else:
# Handle out-of-range numbers
self.paste_entry.delete(0, tk.END)
self.paste_entry.insert(0, "Out of 64-bit range")
except ValueError:
# Handle non-integer input
self.paste_entry.delete(0, tk.END)
self.paste_entry.insert(0, "Invalid Integer")
def update_board_from_bitboard(self):
"""Updates the visual state of all squares based on self.bitboard."""
for r in range(8):
for c in range(8):
bit_index = r * 8 + c
square_info = self.squares[(r, c)]
# Check if the bit at bit_index is set
if (self.bitboard >> bit_index) & 1:
square_info['marked'] = True
else:
square_info['marked'] = False
# Update the square's color
self.update_square_visuals(r, c)
if __name__ == "__main__":
root = tk.Tk()
app = ChessBitboardApp(root)
root.mainloop()

View file

@ -0,0 +1,205 @@
a = """
[
0x8a80104000800020,
0x140002000100040,
0x2801880a0017001,
0x100081001000420,
0x200020010080420,
0x3001c0002010008,
0x8480008002000100,
0x2080088004402900,
0x800098204000,
0x2024401000200040,
0x100802000801000,
0x120800800801000,
0x208808088000400,
0x2802200800400,
0x2200800100020080,
0x801000060821100,
0x80044006422000,
0x100808020004000,
0x12108a0010204200,
0x140848010000802,
0x481828014002800,
0x8094004002004100,
0x4010040010010802,
0x20008806104,
0x100400080208000,
0x2040002120081000,
0x21200680100081,
0x20100080080080,
0x2000a00200410,
0x20080800400,
0x80088400100102,
0x80004600042881,
0x4040008040800020,
0x440003000200801,
0x4200011004500,
0x188020010100100,
0x14800401802800,
0x2080040080800200,
0x124080204001001,
0x200046502000484,
0x480400080088020,
0x1000422010034000,
0x30200100110040,
0x100021010009,
0x2002080100110004,
0x202008004008002,
0x20020004010100,
0x2048440040820001,
0x101002200408200,
0x40802000401080,
0x4008142004410100,
0x2060820c0120200,
0x1001004080100,
0x20c020080040080,
0x2935610830022400,
0x44440041009200,
0x280001040802101,
0x2100190040002085,
0x80c0084100102001,
0x4024081001000421,
0x20030a0244872,
0x12001008414402,
0x2006104900a0804,
0x1004081002402,
]
""".replace("[", "").replace("]", "").replace(",", "").strip().split("\n")
a = [int(x, 16) for x in a]
b = """
[
0x8a80104000800020,
0x140002000100040,
0x2801880a0017001,
0x100081001000420,
0x200020010080420,
0x3001c0002010008,
0x8480008002000100,
0x2080088004402900,
0x800098204000,
0x2024401000200040,
0x100802000801000,
0x120800800801000,
0x208808088000400,
0x2802200800400,
0x2200800100020080,
0x801000060821100,
0x80044006422000,
0x100808020004000,
0x12108a0010204200,
0x140848010000802,
0x481828014002800,
0x8094004002004100,
0x4010040010010802,
0x20008806104,
0x100400080208000,
0x2040002120081000,
0x21200680100081,
0x20100080080080,
0x2000a00200410,
0x20080800400,
0x80088400100102,
0x80004600042881,
0x4040008040800020,
0x440003000200801,
0x4200011004500,
0x188020010100100,
0x14800401802800,
0x2080040080800200,
0x124080204001001,
0x200046502000484,
0x480400080088020,
0x1000422010034000,
0x30200100110040,
0x100021010009,
0x2002080100110004,
0x202008004008002,
0x20020004010100,
0x2048440040820001,
0x101002200408200,
0x40802000401080,
0x4008142004410100,
0x2060820c0120200,
0x1001004080100,
0x20c020080040080,
0x2935610830022400,
0x44440041009200,
0x280001040802101,
0x2100190040002085,
0x80c0084100102001,
0x4024081001000421,
0x20030a0244872,
0x12001008414402,
0x2006104900a0804,
0x1004081002402,
]
""".replace("[", "").replace("]", "").replace(",", "").strip().split("\n")
b = [int(x, 16) for x in b]
def format_rust_array(data_list, array_name="GeneratedArray"):
"""
Converts a list of integers/hex into a formatted Rust array
with binary representation and chess square comments.
"""
print(f"pub const {array_name}: [u64; {len(data_list)}] = [")
files = "ABCDEFGH"
for i, val in enumerate(data_list):
# 1. Convert to 64-bit binary string (MSB on left)
bin_str = f"{val:064b}"
# 2. Insert underscores every 8 bits for readability
# Range 0 to 64 with step 8
chunks = [bin_str[j:j+8] for j in range(0, 64, 8)]
formatted_bin = "_".join(chunks)
# 3. Calculate Square and Algebraic Notation for the comment
# Assuming standard Little-Endian Rank-File mapping (A1=0, B1=1 ... H8=63)
file_idx = i % 8
rank_idx = i // 8
if rank_idx < 8:
algebraic = f"{files[file_idx]}{rank_idx + 1}"
else:
algebraic = "N/A" # Handle lists larger than 64 items gracefully
# 4. Print the formatted line
print(f" 0b{formatted_bin}, // Square {i} ({algebraic})")
print("];")
# --- OPTION 1: Convert your specific hex list ---
my_hex_list = [
0x8a80104000800020,
0x140002000100040
]
# --- OPTION 2: Generate the actual King Attacks (to match your example) ---
def generate_king_attacks():
king_moves = []
for square in range(64):
attacks = 0
file = square % 8
rank = square // 8
# Iterate over all 8 neighbors
for d_file in [-1, 0, 1]:
for d_rank in [-1, 0, 1]:
if d_file == 0 and d_rank == 0:
continue
target_file = file + d_file
target_rank = rank + d_rank
if 0 <= target_file < 8 and 0 <= target_rank < 8:
target_square = target_rank * 8 + target_file
attacks |= (1 << target_square)
king_moves.append(attacks)
return king_moves
if __name__ == "__main__":
format_rust_array(a, "MAGICS_ROOK")
format_rust_array(b, "MAGICS_BISHOP")