reworked moveflag system

This commit is contained in:
Moritz Eigenauer 2025-11-12 18:09:51 +01:00
parent 951a8bbec6
commit 1f61dc35f8
32 changed files with 3784 additions and 40 deletions

7
ChessEngine/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
/target
/.idea
/src/replace.py
/scripts
/Cargo.lock
/src/benchmark/stockfish
uci_log.txt

7
ChessEngine/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "chess_engine"
version = "0.2.0"
authors = ["Moritz Eigenauer <moritz.eigenauer@gmail.com>"]
edition = "2024"
[dependencies]

2
ChessEngine/README.md Normal file
View file

@ -0,0 +1,2 @@
# ChessEngine
My own UCI chess engine in Rust

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")

487
ChessEngine/src/board.rs Normal file
View file

@ -0,0 +1,487 @@
<<<<<<< HEAD
use crate::square::Square;
use std::mem;
use crate::r#move::*;
use std::ops::Not;
pub const CASTLING_WK: u8 = 1;
pub const CASTLING_WK_MASK: u64 = 96; // F1 G1
pub const CASTLING_WQ: u8 = 2;
pub const CASTLING_WQ_MASK: u64 = 14; // B1 C1 D1
pub const CASTLING_BK: u8 = 4;
pub const CASTLING_BK_MASK: u64 = 6917529027641081856; // F8 G8
pub const CASTLING_BQ: u8 = 8;
pub const CASTLING_BQ_MASK: u64 = 1008806316530991104; // B8 C8 D8
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
White = 0,
Black = 1,
}
impl Not for Color {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Color::White => Color::Black,
Color::Black => Color::White,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PieceType {
Pawn = 0,
Knight = 1,
Bishop = 2,
Rook = 3,
Queen = 4,
King = 5,
}
pub struct Board {
pub side_to_move: Color,
pub pieces: [[u64; 2]; 6],
=======
use crate::color::Color;
use crate::square::Square;
use std::mem;
pub const CASTLING_W_KING: u8 = 1;
pub const CASTLING_W_KING_MASK: u64 = 96; // F1 G1
pub const CASTLING_W_QUEEN: u8 = 2;
pub const CASTLING_W_QUEEN_MASK: u64 = 14; // B1 C1 D1
pub const CASTLING_B_KING: u8 = 4;
pub const CASTLING_B_KING_MASK: u64 = 6917529027641081856; // F8 G8
pub const CASTLING_B_QUEEN: u8 = 8;
pub const CASTLING_B_QUEEN_MASK: u64 = 1008806316530991104; // B8 C8 D8
pub struct Board {
pub color: Color,
pub pawns: [u64; 2],
pub knights: [u64; 2],
pub bishops: [u64; 2],
pub rooks: [u64; 2],
pub queens: [u64; 2],
pub kings: [u64; 2],
// Replaced 'withes' and 'blacks' with 'occupied' array
>>>>>>> origin/master
pub occupied: [u64; 2],
pub all_occupied: u64,
pub empty_squares: u64,
pub castling_rights: u8,
pub en_passant_target: Option<Square>,
pub halfmove_clock: u8,
pub fullmove_number: u16,
}
impl Board {
/// Creates a new Board instance from a FEN string.
/// Assumes the FEN string is valid.
pub fn from_fen(fen: &str) -> Self {
let mut parts = fen.split_whitespace();
<<<<<<< HEAD
// Initialisiere das 2D-Array
let mut pieces = [[0u64; 2]; 6];
=======
let mut pawns = [0u64; 2];
let mut knights = [0u64; 2];
let mut bishops = [0u64; 2];
let mut rooks = [0u64; 2];
let mut queens = [0u64; 2];
let mut kings = [0u64; 2];
// Changed from 'withes' and 'blacks'
>>>>>>> origin/master
let mut occupied = [0u64; 2];
// Part 1: Piece placement
let placement = parts.next().unwrap_or("");
let mut rank = 7;
let mut file = 0;
for c in placement.chars() {
if c.is_digit(10) {
file += c.to_digit(10).unwrap_or(0) as usize;
} else if c == '/' {
rank -= 1;
file = 0;
} else if c.is_alphabetic() {
let sq = (rank * 8 + file) as u8;
let mask = 1u64 << sq;
if c.is_uppercase() {
let color_idx = Color::White as usize;
<<<<<<< HEAD
occupied[color_idx] |= mask;
match c {
'P' => pieces[PieceType::Pawn as usize][color_idx] |= mask,
'N' => pieces[PieceType::Knight as usize][color_idx] |= mask,
'B' => pieces[PieceType::Bishop as usize][color_idx] |= mask,
'R' => pieces[PieceType::Rook as usize][color_idx] |= mask,
'Q' => pieces[PieceType::Queen as usize][color_idx] |= mask,
'K' => pieces[PieceType::King as usize][color_idx] |= mask,
=======
occupied[color_idx] |= mask; // Changed
match c {
'P' => pawns[color_idx] |= mask,
'N' => knights[color_idx] |= mask,
'B' => bishops[color_idx] |= mask,
'R' => rooks[color_idx] |= mask,
'Q' => queens[color_idx] |= mask,
'K' => kings[color_idx] |= mask,
>>>>>>> origin/master
_ => {}
}
} else {
let color_idx = Color::Black as usize;
<<<<<<< HEAD
occupied[color_idx] |= mask;
match c {
'p' => pieces[PieceType::Pawn as usize][color_idx] |= mask,
'n' => pieces[PieceType::Knight as usize][color_idx] |= mask,
'b' => pieces[PieceType::Bishop as usize][color_idx] |= mask,
'r' => pieces[PieceType::Rook as usize][color_idx] |= mask,
'q' => pieces[PieceType::Queen as usize][color_idx] |= mask,
'k' => pieces[PieceType::King as usize][color_idx] |= mask,
=======
occupied[color_idx] |= mask; // Changed
match c {
'p' => pawns[color_idx] |= mask,
'n' => knights[color_idx] |= mask,
'b' => bishops[color_idx] |= mask,
'r' => rooks[color_idx] |= mask,
'q' => queens[color_idx] |= mask,
'k' => kings[color_idx] |= mask,
>>>>>>> origin/master
_ => {}
}
}
file += 1;
}
}
// Part 2: Active color
<<<<<<< HEAD
let side_to_move = match parts.next().unwrap_or("w") {
=======
let color = match parts.next().unwrap_or("w") {
>>>>>>> origin/master
"b" => Color::Black,
_ => Color::White,
};
// Part 3: Castling rights
let mut castling_rights = 0u8;
if let Some(castle_str) = parts.next() {
<<<<<<< HEAD
if castle_str.contains('K') { castling_rights |= CASTLING_WK; }
if castle_str.contains('Q') { castling_rights |= CASTLING_WQ; }
if castle_str.contains('k') { castling_rights |= CASTLING_BK; }
if castle_str.contains('q') { castling_rights |= CASTLING_BQ; }
=======
if castle_str.contains('K') { castling_rights |= 1; }
if castle_str.contains('Q') { castling_rights |= 2; }
if castle_str.contains('k') { castling_rights |= 4; }
if castle_str.contains('q') { castling_rights |= 8; }
>>>>>>> origin/master
}
// Part 4: En passant target
let en_passant_target = match parts.next().unwrap_or("-") {
"-" => None,
sq_str => {
let chars: Vec<char> = sq_str.chars().collect();
let file = (chars[0] as u8 - b'a') as u8;
let rank = (chars[1] as u8 - b'1') as u8;
let sq_index = rank * 8 + file;
<<<<<<< HEAD
=======
// Convert the u8 index back to the Square enum
>>>>>>> origin/master
// This is unsafe, but assumes the FEN is valid
Some(unsafe { mem::transmute::<u8, Square>(sq_index) })
}
};
// Part 5: Halfmove clock
let halfmove_clock = parts.next().unwrap_or("0").parse::<u8>().unwrap_or(0);
// Part 6: Fullmove number
let fullmove_number = parts.next().unwrap_or("1").parse::<u16>().unwrap_or(1);
let all_occupied = occupied[Color::White as usize] | occupied[Color::Black as usize];
<<<<<<< HEAD
let empty_squares = !all_occupied;
Board {
side_to_move,
pieces, // Geändertes Feld
=======
let empty_squares = !all_occupied;
Board {
color,
pawns,
knights,
bishops,
rooks,
queens,
kings,
>>>>>>> origin/master
occupied,
all_occupied,
empty_squares,
castling_rights,
en_passant_target,
halfmove_clock,
fullmove_number,
}
}
/// Converts the current board state into a FEN string.
pub fn to_fen(&self) -> String {
let mut fen = String::with_capacity(90);
// Part 1: Piece placement
let mut empty_count = 0;
for rank in (0..=7).rev() {
for file in 0..=7 {
<<<<<<< HEAD
let sq = (rank * 8 + file) as u8;
let mask = 1u64 << sq;
=======
// sq is the u8 index (0-63)
let sq = (rank * 8 + file) as u8;
let mask = 1u64 << sq; // This works (u64 << u8)
>>>>>>> origin/master
if let Some(piece) = self.get_piece_at(mask) {
if empty_count > 0 {
fen.push((b'0' + empty_count) as char);
empty_count = 0;
}
fen.push(piece);
} else {
empty_count += 1;
}
}
if empty_count > 0 {
fen.push((b'0' + empty_count) as char);
empty_count = 0;
}
if rank > 0 {
fen.push('/');
}
}
// Part 2: Active color
fen.push(' ');
<<<<<<< HEAD
fen.push(if self.side_to_move == Color::White { 'w' } else { 'b' });
=======
fen.push(if self.color == Color::White { 'w' } else { 'b' });
>>>>>>> origin/master
// Part 3: Castling rights
fen.push(' ');
let mut castle_str = String::new();
<<<<<<< HEAD
if (self.castling_rights & CASTLING_WK) != 0 { castle_str.push('K'); }
if (self.castling_rights & CASTLING_WQ) != 0 { castle_str.push('Q'); }
if (self.castling_rights & CASTLING_BK) != 0 { castle_str.push('k'); }
if (self.castling_rights & CASTLING_BQ) != 0 { castle_str.push('q'); }
=======
if (self.castling_rights & 1) != 0 { castle_str.push('K'); }
if (self.castling_rights & 2) != 0 { castle_str.push('Q'); }
if (self.castling_rights & 4) != 0 { castle_str.push('k'); }
if (self.castling_rights & 8) != 0 { castle_str.push('q'); }
>>>>>>> origin/master
if castle_str.is_empty() {
fen.push('-');
} else {
fen.push_str(&castle_str);
}
// Part 4: En passant target
fen.push(' ');
if let Some(sq) = self.en_passant_target {
<<<<<<< HEAD
=======
// Cast the Square enum to its u8 representation
>>>>>>> origin/master
let sq_index = sq as u8;
let file = (sq_index % 8) as u8;
let rank = (sq_index / 8) as u8;
fen.push((b'a' + file) as char);
fen.push((b'1' + rank) as char);
} else {
fen.push('-');
}
// Part 5: Halfmove clock
fen.push(' ');
fen.push_str(&self.halfmove_clock.to_string());
// Part 6: Fullmove number
fen.push(' ');
fen.push_str(&self.fullmove_number.to_string());
fen
}
/// Helper function to find which piece (as a char) is on a given square mask.
fn get_piece_at(&self, sq_mask: u64) -> Option<char> {
let white = Color::White as usize;
let black = Color::Black as usize;
<<<<<<< HEAD
if (self.pieces[PieceType::Pawn as usize][white] & sq_mask) != 0 { return Some('P'); }
if (self.pieces[PieceType::Pawn as usize][black] & sq_mask) != 0 { return Some('p'); }
if (self.pieces[PieceType::Knight as usize][white] & sq_mask) != 0 { return Some('N'); }
if (self.pieces[PieceType::Knight as usize][black] & sq_mask) != 0 { return Some('n'); }
if (self.pieces[PieceType::Bishop as usize][white] & sq_mask) != 0 { return Some('B'); }
if (self.pieces[PieceType::Bishop as usize][black] & sq_mask) != 0 { return Some('b'); }
if (self.pieces[PieceType::Rook as usize][white] & sq_mask) != 0 { return Some('R'); }
if (self.pieces[PieceType::Rook as usize][black] & sq_mask) != 0 { return Some('r'); }
if (self.pieces[PieceType::Queen as usize][white] & sq_mask) != 0 { return Some('Q'); }
if (self.pieces[PieceType::Queen as usize][black] & sq_mask) != 0 { return Some('q'); }
if (self.pieces[PieceType::King as usize][white] & sq_mask) != 0 { return Some('K'); }
if (self.pieces[PieceType::King as usize][black] & sq_mask) != 0 { return Some('k'); }
=======
if (self.pawns[white] & sq_mask) != 0 { return Some('P'); }
if (self.pawns[black] & sq_mask) != 0 { return Some('p'); }
if (self.knights[white] & sq_mask) != 0 { return Some('N'); }
if (self.knights[black] & sq_mask) != 0 { return Some('n'); }
if (self.bishops[white] & sq_mask) != 0 { return Some('B'); }
if (self.bishops[black] & sq_mask) != 0 { return Some('b'); }
if (self.rooks[white] & sq_mask) != 0 { return Some('R'); }
if (self.rooks[black] & sq_mask) != 0 { return Some('r'); }
if (self.queens[white] & sq_mask) != 0 { return Some('Q'); }
if (self.queens[black] & sq_mask) != 0 { return Some('q'); }
if (self.kings[white] & sq_mask) != 0 { return Some('K'); }
if (self.kings[black] & sq_mask) != 0 { return Some('k'); }
>>>>>>> origin/master
None
}
/// Prints a single bitboard (u64) as an 8x8 grid for debugging.
fn print_bitboard(&self, name: &str, bitboard: u64) {
println!("--- {} ---", name);
println!(" a b c d e f g h");
for rank in (0..=7).rev() {
print!("{} ", rank + 1);
for file in 0..=7 {
let sq_index = rank * 8 + file;
let mask = 1u64 << sq_index;
if (bitboard & mask) != 0 {
print!("1 ");
} else {
print!(". ");
}
}
println!();
}
println!("RAW VALUE: {}", bitboard);
<<<<<<< HEAD
println!();
=======
println!(); // Add a newline for spacing
>>>>>>> origin/master
}
/// Prints all internal bitboards for debugging purposes.
pub fn pretty_print_internals(&self) {
println!("\n========= BOARD INTERNAL BITBOARDS =========");
let white = Color::White as usize;
let black = Color::Black as usize;
<<<<<<< HEAD
self.print_bitboard("White Pawns", self.pieces[PieceType::Pawn as usize][white]);
self.print_bitboard("Black Pawns", self.pieces[PieceType::Pawn as usize][black]);
self.print_bitboard("White Knights", self.pieces[PieceType::Knight as usize][white]);
self.print_bitboard("Black Knights", self.pieces[PieceType::Knight as usize][black]);
self.print_bitboard("White Bishops", self.pieces[PieceType::Bishop as usize][white]);
self.print_bitboard("Black Bishops", self.pieces[PieceType::Bishop as usize][black]);
self.print_bitboard("White Rooks", self.pieces[PieceType::Rook as usize][white]);
self.print_bitboard("Black Rooks", self.pieces[PieceType::Rook as usize][black]);
self.print_bitboard("White Queens", self.pieces[PieceType::Queen as usize][white]);
self.print_bitboard("Black Queens", self.pieces[PieceType::Queen as usize][black]);
self.print_bitboard("White King", self.pieces[PieceType::King as usize][white]);
self.print_bitboard("Black King", self.pieces[PieceType::King as usize][black]);
println!("--- Aggregate Bitboards ---");
=======
self.print_bitboard("White Pawns", self.pawns[white]);
self.print_bitboard("Black Pawns", self.pawns[black]);
self.print_bitboard("White Knights", self.knights[white]);
self.print_bitboard("Black Knights", self.knights[black]);
self.print_bitboard("White Bishops", self.bishops[white]);
self.print_bitboard("Black Bishops", self.bishops[black]);
self.print_bitboard("White Rooks", self.rooks[white]);
self.print_bitboard("Black Rooks", self.rooks[black]);
self.print_bitboard("White Queens", self.queens[white]);
self.print_bitboard("Black Queens", self.queens[black]);
self.print_bitboard("White King", self.kings[white]);
self.print_bitboard("Black King", self.kings[black]);
println!("--- Aggregate Bitboards ---");
// Updated to use the 'occupied' array
>>>>>>> origin/master
self.print_bitboard("All White Pieces", self.occupied[white]);
self.print_bitboard("All Black Pieces", self.occupied[black]);
self.print_bitboard("All Occupied", self.all_occupied);
self.print_bitboard("Empty Squares", self.empty_squares);
println!("============================================\n");
}
<<<<<<< HEAD
pub fn make_move(&mut self, mv: Move) {
let from = mv.value()& MOVE_FROM_MASK;
let to = mv.value() & MOVE_TO_MASK;
let flag = mv.value() & MOVE_FLAG_MASK;
// promo must come first because of double usage of the flag bits
if flag == MOVE_FLAG_NO_PROMO {
} else { //
}
}
}
=======
}
>>>>>>> origin/master

8
ChessEngine/src/lib.rs Normal file
View file

@ -0,0 +1,8 @@
pub mod board;
pub mod r#move;
<<<<<<< HEAD
=======
pub mod color;
>>>>>>> origin/master
pub mod square;
pub mod movegen;

23
ChessEngine/src/main.rs Normal file
View file

@ -0,0 +1,23 @@
use chess_engine::board::Board;
use chess_engine::movegen::generate_pseudo_legal_moves;
use chess_engine::r#move::*;
<<<<<<< HEAD
fn main() {
let board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 1 2");
let mut move_list = MoveList::new();
generate_pseudo_legal_moves(&board, &mut move_list);
println!("Counted {} pseudo legal moves.", move_list.len());
=======
use chess_engine::movegen::tables::{get_rook_attacks, get_bishop_attacks};
fn main() {
// Calculate the move tables
get_rook_attacks();
get_bishop_attacks();
>>>>>>> origin/master
}

223
ChessEngine/src/move.rs Normal file
View file

@ -0,0 +1,223 @@
use std::slice;
use std::fmt;
use crate::square::Square;
<<<<<<< HEAD
use crate::board::PieceType;
=======
>>>>>>> origin/master
// BIT 0 - 5: FROM SQUARE (0-63)
pub const MOVE_FROM_MASK: u16 = 0b0000_0000_0011_1111;
// BIT 6 - 11: TO SQUARE (0-63)
pub const MOVE_TO_MASK: u16 = 0b0000_1111_1100_0000;
// BIT 12 - 15: FLAGS (4 bits)
<<<<<<< HEAD
// 1. 0 no capture, 1 capture
pub const MOVE_FLAG_MASK: u16 = 0b1111_0000_0000_0000;
pub const MOVE_FLAG_QUIET: u16 = 0b0000_0000_0000_0000;
pub const MOVE_FLAG_CAPTURE: u16 = 0b0001_0000_0000_0000;
pub const MOVE_FLAG_EN_PASSANT: u16 = 0b0010_0000_0000_0000;
pub const MOVE_FLAG_WK_CASTLE: u16 = 0b0011_0000_0000_0000;
pub const MOVE_FLAG_WQ_CASTLE: u16 = 0b0100_0000_0000_0000;
pub const MOVE_FLAG_BK_CASTLE: u16 = 0b0101_0000_0000_0000;
pub const MOVE_FLAG_BQ_CASTLE: u16 = 0b0110_0000_0000_0000;
// 0111 is free
// Promotion flags (use the 1xxx bits)
// We combine capture flag with promotion type
pub const MOVE_FLAG_PROMO: u16 = 0b1000_0000_0000_0000;
pub const MOVE_FLAG_NO_PROMO: u16 = 0b0000_0000_0000_0000;
=======
pub const MOVE_FLAG_MASK: u16 = 0b1111_0000_0000_0000;
pub const MOVE_FLAG_QUIET: u16 = 0b0000_0000_0000_0000;
pub const MOVE_FLAG_DOUBLE_PAWN: u16 = 0b0001_0000_0000_0000;
pub const MOVE_FLAG_KING_CASTLE: u16 = 0b0010_0000_0000_0000;
pub const MOVE_FLAG_QUEEN_CASTLE: u16 = 0b0011_0000_0000_0000;
pub const MOVE_FLAG_CAPTURE: u16 = 0b0100_0000_0000_0000;
pub const MOVE_FLAG_EN_PASSANT: u16 = 0b0101_0000_0000_0000;
// 0110, 0111 are free
// Promotion flags (use the 1xxx bits)
// We combine capture flag with promotion type
>>>>>>> origin/master
pub const MOVE_FLAG_PROMO_N: u16 = 0b1000_0000_0000_0000;
pub const MOVE_FLAG_PROMO_B: u16 = 0b1001_0000_0000_0000;
pub const MOVE_FLAG_PROMO_R: u16 = 0b1010_0000_0000_0000;
pub const MOVE_FLAG_PROMO_Q: u16 = 0b1011_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_N: u16 = 0b1100_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_B: u16 = 0b1101_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_R: u16 = 0b1110_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_Q: u16 = 0b1111_0000_0000_0000;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Move(u16);
impl Move {
pub fn new(from: Square, to: Square, flags: u16) -> Move {
Move(flags |
((to as u16) << 6 ) |
from as u16)
}
<<<<<<< HEAD
pub fn value(&self) -> u16 {
self.0
}
=======
>>>>>>> origin/master
#[inline(always)]
pub fn get_flags(&self) -> u16 {
self.0 & MOVE_FLAG_MASK
}
<<<<<<< HEAD
=======
// Helper for search
#[inline(always)]
pub fn is_capture(&self) -> bool {
// Check if the 3rd bit of the flag (0100) is set.
// This covers simple captures (0100), EP (0101), and promo-captures (11xx).
(self.0 & 0b0100_0000_0000_0000) != 0
}
// Helper for search
#[inline(always)]
pub fn is_promotion(&self) -> bool {
// Check if the 4th bit of the flag (1xxx) is set.
(self.0 & 0b1000_0000_0000_0000) != 0
}
>>>>>>> origin/master
/// Converts a square index (0-63) to algebraic notation (e.g., 0 -> "a1", 63 -> "h8").
fn square_val_to_alg(val: u16) -> String {
let file = (b'a' + (val % 8) as u8) as char;
let rank = (b'1' + (val / 8) as u8) as char;
format!("{}{}", file, rank)
}
/// Converts the move to coordinate notation (e.g., "e2e4", "e7e8q", "e1g1").
pub fn to_algebraic(&self) -> String {
let flags = self.get_flags();
// Handle castling first. In this new format, the "to" square is
// the *king's* destination square (g1/c1 or g8/c8).
// Your old implementation reading the file is still fine.
<<<<<<< HEAD
if (flags == MOVE_FLAG_WK_CASTLE) || (flags == MOVE_FLAG_BK_CASTLE) {
return "O-O".to_string();
}
if (flags == MOVE_FLAG_WQ_CASTLE) || (flags == MOVE_FLAG_BQ_CASTLE) {
=======
if flags == MOVE_FLAG_KING_CASTLE {
return "O-O".to_string();
}
if flags == MOVE_FLAG_QUEEN_CASTLE {
>>>>>>> origin/master
return "O-O-O".to_string();
}
let from_val = self.0 & MOVE_FROM_MASK;
let to_val = (self.0 & MOVE_TO_MASK) >> 6;
let from_str = Self::square_val_to_alg(from_val);
let to_str = Self::square_val_to_alg(to_val);
// Check if it's any promotion type (1xxx)
if (flags & 0b1000_0000_0000_0000) != 0 {
let promo_char = match flags {
MOVE_FLAG_PROMO_N | MOVE_FLAG_PROMO_CAP_N => 'n',
MOVE_FLAG_PROMO_B | MOVE_FLAG_PROMO_CAP_B => 'b',
MOVE_FLAG_PROMO_R | MOVE_FLAG_PROMO_CAP_R => 'r',
MOVE_FLAG_PROMO_Q | MOVE_FLAG_PROMO_CAP_Q => 'q',
_ => '?', // Should not happen
};
format!("{}{}{}", from_str, to_str, promo_char)
} else {
// This covers Quiet, DoublePawn, Capture, EnPassant
format!("{}{}", from_str, to_str)
}
}
}
// ... Rest des MoveList-Codes bleibt exakt gleich ...
// (MoveList, new, push, len, is_empty, iter, impl fmt::Display)
pub struct MoveList {
moves: [Move; 256],
count: usize,
}
impl MoveList {
pub fn new() -> Self {
MoveList {
moves: [unsafe { std::mem::zeroed() }; 256],
count: 0,
}
}
#[inline(always)]
pub fn push(&mut self, mv: Move) {
debug_assert!(self.count < 256, "Move list overflow!");
self.moves[self.count] = mv;
self.count += 1;
}
#[inline(always)]
pub fn len(&self) -> usize {
self.count
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.count == 0
}
#[inline(always)]
pub fn iter(&self) -> slice::Iter<'_, Move> {
self.moves[..self.count].iter()
}
}
impl fmt::Display for MoveList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.iter().map(|mv| mv.to_algebraic()).collect::<Vec<String>>().join(" "))
}
}
<<<<<<< HEAD
pub struct UndoMove {
mv: Move,
captured_piece: Option<PieceType>,
old_en_passant_square: Option<Square>,
old_castling_rights: u8,
old_halfmove_clock: u8,
}
impl UndoMove {
pub fn new(mv: Move,
captured_piece: Option<PieceType>,
old_en_passant_square: Option<Square>,
old_castling_rights: u8,
old_halfmove_clock: u8) -> Self {
Self {
mv,
captured_piece,
old_en_passant_square,
old_castling_rights,
old_halfmove_clock
}
}
}
=======
>>>>>>> origin/master

View file

@ -0,0 +1,25 @@
pub mod non_sliders;
pub mod sliders;
pub mod pawns;
pub mod tables;
use crate::board::Board;
use crate::r#move::*;
use non_sliders::*;
<<<<<<< HEAD
use sliders::*;
use pawns::*;
pub fn generate_pseudo_legal_moves(board: &Board, list: &mut MoveList) {
generate_pawn_moves(board, list);
generate_knight_moves(board, list);
generate_bishop_moves(board, list);
generate_rook_moves(board, list);
generate_queen_moves(board, list);
generate_king_moves(board, list);
=======
pub fn generate_pseudo_legal_moves(board: &Board, list: &mut MoveList) {
generate_knight_moves(board, list);
>>>>>>> origin/master
}

View file

@ -0,0 +1,140 @@
use crate::board::*;
<<<<<<< HEAD
use crate::r#move::*;
use crate::square::*;
use super::tables::{KING_ATTACKS, KNIGHT_ATTACKS};
pub fn generate_knight_moves(board: &Board, list: &mut MoveList) {
let enemy_occupied = board.occupied[!board.side_to_move as usize];
let mut friendly_knights = board.pieces[PieceType::Knight as usize][board.side_to_move as usize];
while friendly_knights != 0 {
let square = SQUARES[friendly_knights.trailing_zeros() as usize];
let mut attacks = KNIGHT_ATTACKS[square as usize] & !board.occupied[board.side_to_move as usize];
=======
use crate::color::Color;
use crate::r#move::{
Move, MoveList, MOVE_FLAG_CAPTURE, MOVE_FLAG_KING_CASTLE, MOVE_FLAG_QUEEN_CASTLE,
MOVE_FLAG_QUIET,
};
use crate::square::{Square, SQUARES};
use super::tables::{KING_ATTACKS, KNIGHT_ATTACKS};
pub fn generate_knight_moves(board: &Board, list: &mut MoveList) {
let enemy_occupied = board.occupied[!board.color as usize];
let mut friendly_knights = board.knights[board.color as usize];
while friendly_knights != 0 {
let square = SQUARES[friendly_knights.trailing_zeros() as usize];
let mut attacks = KNIGHT_ATTACKS[square as usize] & !board.occupied[board.color as usize];
>>>>>>> origin/master
while attacks != 0 {
let attack = SQUARES[attacks.trailing_zeros() as usize];
let attack_bb = 1u64 << attack as u64;
let flags = if (enemy_occupied & attack_bb) != 0 {
MOVE_FLAG_CAPTURE
} else {
MOVE_FLAG_QUIET
};
let mv = Move::new(square, attack, flags);
list.push(mv);
attacks &= attacks - 1;
}
friendly_knights &= friendly_knights - 1;
}
}
pub fn generate_king_moves(board: &Board, list: &mut MoveList) {
<<<<<<< HEAD
let enemy_occupied = board.occupied[!board.side_to_move as usize];
let friendly_king = board.pieces[PieceType::King as usize][board.side_to_move as usize];
=======
let enemy_occupied = board.occupied[!board.color as usize];
let friendly_king = board.kings[board.color as usize];
>>>>>>> origin/master
if friendly_king == 0 {
return;
}
let square = SQUARES[friendly_king.trailing_zeros() as usize];
// 1. Generate standard king moves
<<<<<<< HEAD
let mut attacks = KING_ATTACKS[square as usize] & !board.occupied[board.side_to_move as usize];
=======
let mut attacks = KING_ATTACKS[square as usize] & !board.occupied[board.color as usize];
>>>>>>> origin/master
while attacks != 0 {
let attack = SQUARES[attacks.trailing_zeros() as usize];
let attack_bb = 1u64 << attack as u64;
let flags = if (enemy_occupied & attack_bb) != 0 {
MOVE_FLAG_CAPTURE
} else {
MOVE_FLAG_QUIET
};
let mv = Move::new(square, attack, flags);
list.push(mv);
attacks &= attacks - 1;
}
// 2. Generate castling king moves
<<<<<<< HEAD
if board.side_to_move == Color::White {
// Kingside (OO)
if (board.castling_rights & CASTLING_WK) != 0 {
if (board.all_occupied & CASTLING_WK_MASK) == 0 {
list.push(Move::new(Square::E1, Square::G1, MOVE_FLAG_WK_CASTLE));
}
}
// Queenside (OOO)
if (board.castling_rights & CASTLING_WQ) != 0 {
if (board.all_occupied & CASTLING_WQ_MASK) == 0 {
list.push(Move::new(Square::E1, Square::C1, MOVE_FLAG_WQ_CASTLE));
=======
if board.color == Color::White {
// Kingside (OO)
if (board.castling_rights & CASTLING_W_KING) != 0 {
if (board.all_occupied & CASTLING_W_KING_MASK) == 0 {
list.push(Move::new(Square::E1, Square::G1, MOVE_FLAG_KING_CASTLE));
}
}
// Queenside (OOO)
if (board.castling_rights & CASTLING_W_QUEEN) != 0 {
if (board.all_occupied & CASTLING_W_QUEEN_MASK) == 0 {
list.push(Move::new(Square::E1, Square::C1, MOVE_FLAG_QUEEN_CASTLE));
>>>>>>> origin/master
}
}
} else { // Black
// Kingside (OO)
<<<<<<< HEAD
if (board.castling_rights & CASTLING_BK) != 0 {
if (board.all_occupied & CASTLING_BK_MASK) == 0 {
list.push(Move::new(Square::E8, Square::G8, MOVE_FLAG_BK_CASTLE));
}
}
// Queenside (OOO)
if (board.castling_rights & CASTLING_BQ) != 0 {
if (board.all_occupied & CASTLING_BQ_MASK) == 0 {
list.push(Move::new(Square::E8, Square::C8, MOVE_FLAG_BQ_CASTLE));
=======
if (board.castling_rights & CASTLING_B_KING) != 0 {
if (board.all_occupied & CASTLING_B_KING_MASK) == 0 {
list.push(Move::new(Square::E8, Square::G8, MOVE_FLAG_KING_CASTLE));
}
}
// Queenside (OOO)
if (board.castling_rights & CASTLING_B_QUEEN) != 0 {
if (board.all_occupied & CASTLING_B_QUEEN_MASK) == 0 {
list.push(Move::new(Square::E8, Square::C8, MOVE_FLAG_QUEEN_CASTLE));
>>>>>>> origin/master
}
}
}
}

View file

@ -0,0 +1,275 @@
<<<<<<< HEAD
use crate::board::*;
use crate::r#move::*;
use crate::square::*;
=======
use crate::board::Board;
use crate::r#move::*;
use crate::square::*;
use crate::color::Color;
>>>>>>> origin/master
pub const RANK1_MASK: u64 = 255; // A1 - H1
pub const RANK2_MASK: u64 = 65280; // A2 - H2
pub const RANK3_MASK: u64 = 16711680; // A3 - H3
pub const RANK4_MASK: u64 = 4278190080; // A4 - H4
pub const RANK5_MASK: u64 = 1095216660480; // A5 - H5
pub const RANK6_MASK: u64 = 280375465082880; // A6 - H6
pub const RANK7_MASK: u64 = 71776119061217280; // A7 - H7
pub const RANK8_MASK: u64 = 18374686479671623680; // A8 - H8
pub const PAWN_A_SIDE_CAPTURE_MASK_WITHE: u64 = 280371153272574; // B1 - H6 (omitted promotions)
pub const PAWN_H_SIDE_CAPTURE_MASK_WITHE: u64 = 140185576636287; // A1- G6 (omitted promotions)
pub const PAWN_A_SIDE_CAPTURE_MASK_BLACK: u64 = 18374403900871409664; // B3 - H8 (omitted promotions)
pub const PAWN_H_SIDE_CAPTURE_MASK_BLACK: u64 = 9187201950435704832; // A3- G8 (omitted promotions)
pub const PAWN_A_SIDE_CAPTURE_PROMOTION_MASK_WITHE: u64 = 71494644084506624; // B7 - H7
pub const PAWN_H_SIDE_CAPTURE_PROMOTION_MASK_WITHE: u64 = 35747322042253312; // A7- G7
pub const PAWN_A_SIDE_CAPTURE_PROMOTION_MASK_BLACK: u64 = 65024; // B2 - H2
pub const PAWN_H_SIDE_CAPTURE_PROMOTION_MASK_BLACK: u64 = 32512; // A2 - G2
pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) {
// 1. Withe
<<<<<<< HEAD
if board.side_to_move == Color::White {
let friendly_pawns = board.pieces[PieceType::Pawn as usize][0];
=======
if board.color == Color::White {
let friendly_pawns = board.pawns[0];
>>>>>>> origin/master
let opponent_occupied = board.occupied[1];
// 1.1 Single Push
let mut single_push_targets = ((friendly_pawns & !(RANK8_MASK | RANK7_MASK)) << 8) & board.empty_squares;
while single_push_targets > 0 {
let to = SQUARES[single_push_targets.trailing_zeros() as usize];
let from = to - 8;
list.push(Move::new(from, to, MOVE_FLAG_QUIET));
single_push_targets &= single_push_targets - 1;
}
// 1.2 Double Push
let base_rank_pawns = friendly_pawns & RANK2_MASK;
let rank3_unblocked = (base_rank_pawns << 8) & board.empty_squares;
let mut double_push_targets = (rank3_unblocked << 8) & board.empty_squares;
while double_push_targets > 0 {
let to = SQUARES[double_push_targets.trailing_zeros() as usize];
let from = to - 16;
<<<<<<< HEAD
list.push(Move::new(from, to, MOVE_FLAG_QUIET));
=======
list.push(Move::new(from, to, MOVE_FLAG_DOUBLE_PAWN));
>>>>>>> origin/master
double_push_targets &= double_push_targets - 1;
}
// 1.3 Captures
// 1.3.1 A-Side Capture (omitted promotion captures)
let mut a_side_capture_targets = (friendly_pawns & PAWN_H_SIDE_CAPTURE_MASK_WITHE) << 7 & opponent_occupied;
while a_side_capture_targets > 0 {
let to = SQUARES[a_side_capture_targets.trailing_zeros() as usize];
let from = to - 7;
list.push(Move::new(from, to, MOVE_FLAG_CAPTURE));
a_side_capture_targets &= a_side_capture_targets - 1;
}
// 1.3.2 H-Side Capture (omitted promotion captures)
let mut h_side_capture_targets = (friendly_pawns & PAWN_A_SIDE_CAPTURE_MASK_WITHE) << 9 & opponent_occupied;
while h_side_capture_targets > 0 {
let to = SQUARES[h_side_capture_targets.trailing_zeros() as usize];
let from = to - 9;
list.push(Move::new(from, to, MOVE_FLAG_CAPTURE));
h_side_capture_targets &= h_side_capture_targets - 1;
}
// 1.4 Promotion
// 1.4.1 Pushing promotion
let mut promotion_targets = ((friendly_pawns & RANK7_MASK) << 8) & board.empty_squares;
while promotion_targets > 0 {
let to = SQUARES[promotion_targets.trailing_zeros() as usize];
let from = to - 8;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_N));
promotion_targets &= promotion_targets - 1;
}
// 1.4.2 Capturing Promotion
// 1.4.2.1 A-side capturing promotion
// CORRECTED: Use PAWN_A_SIDE_CAPTURE_PROMOTION_MASK_WITHE (excludes A-file)
let mut promotion_targets_a_side_capture = ((friendly_pawns & PAWN_A_SIDE_CAPTURE_PROMOTION_MASK_WITHE) << 7) & board.occupied[1];
while promotion_targets_a_side_capture > 0 {
let to = SQUARES[promotion_targets_a_side_capture.trailing_zeros() as usize];
let from = to - 7;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
promotion_targets_a_side_capture &= promotion_targets_a_side_capture - 1;
}
// 1.4.2.2 H-side capturing promotion
// CORRECTED: Use PAWN_H_SIDE_CAPTURE_PROMOTION_MASK_WITHE (excludes H-file)
let mut promotion_targets_h_side_capture = ((friendly_pawns & PAWN_H_SIDE_CAPTURE_PROMOTION_MASK_WITHE) << 9) & board.occupied[1];
while promotion_targets_h_side_capture > 0 {
let to = SQUARES[promotion_targets_h_side_capture.trailing_zeros() as usize];
let from = to - 9;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
promotion_targets_h_side_capture &= promotion_targets_h_side_capture - 1;
}
// 1.5 En Passant
if let Some(en_passant_target_square) = board.en_passant_target {
// Check if the target square is on the 6th rank (A6=40 to H6=47)
if (en_passant_target_square >= Square::A6) && (en_passant_target_square <= Square::H6) {
let en_passant_target_bb: u64 = 1_u64 << (en_passant_target_square as u64);
// 1. Check A-Side capture (<< 7, e.g., D5 -> C6)
let attacker_mask_a_side = (en_passant_target_bb >> 7) & PAWN_H_SIDE_CAPTURE_MASK_WITHE;
if (attacker_mask_a_side & friendly_pawns) > 0 {
let from = en_passant_target_square - 7;
list.push(Move::new(from, en_passant_target_square, MOVE_FLAG_EN_PASSANT));
}
// 2. Check H-Side capture (<< 9, e.g., B5 -> C6)
let attacker_mask_h_side = (en_passant_target_bb >> 9) & PAWN_A_SIDE_CAPTURE_MASK_WITHE;
if (attacker_mask_h_side & friendly_pawns) > 0 {
let from = en_passant_target_square - 9;
list.push(Move::new(from, en_passant_target_square, MOVE_FLAG_EN_PASSANT));
}
}
}
// 2. Black
} else {
<<<<<<< HEAD
let friendly_pawns = board.pieces[PieceType::Pawn as usize][1];
=======
let friendly_pawns = board.pawns[1];
>>>>>>> origin/master
let opponent_occupied = board.occupied[0];
// 2.1 Single Push
let mut single_push_targets = ((friendly_pawns & !(RANK1_MASK | RANK2_MASK)) >> 8) & board.empty_squares;
while single_push_targets > 0 {
let to = SQUARES[single_push_targets.trailing_zeros() as usize];
let from = to + 8;
list.push(Move::new(from, to, MOVE_FLAG_QUIET));
single_push_targets &= single_push_targets - 1;
}
// 2.2 Double Push
let base_rank_pawns = friendly_pawns & RANK7_MASK;
let rank6_unblocked = (base_rank_pawns >> 8) & board.empty_squares;
let mut double_push_targets = (rank6_unblocked >> 8) & board.empty_squares;
while double_push_targets > 0 {
let to = SQUARES[double_push_targets.trailing_zeros() as usize];
let from = to + 16;
<<<<<<< HEAD
list.push(Move::new(from, to, MOVE_FLAG_QUIET));
=======
list.push(Move::new(from, to, MOVE_FLAG_DOUBLE_PAWN));
>>>>>>> origin/master
double_push_targets &= double_push_targets - 1;
}
// 2.3 Captures
// 2.3.1 A-Side Capture (>> 9)
let mut a_side_capture_targets = (friendly_pawns & PAWN_A_SIDE_CAPTURE_MASK_BLACK) >> 9 & opponent_occupied;
while a_side_capture_targets > 0 {
let to = SQUARES[a_side_capture_targets.trailing_zeros() as usize];
let from = to + 9;
list.push(Move::new(from, to, MOVE_FLAG_CAPTURE));
a_side_capture_targets &= a_side_capture_targets - 1;
}
// 2.3.2 H-Side Capture (>> 7)
let mut h_side_capture_targets = (friendly_pawns & PAWN_H_SIDE_CAPTURE_MASK_BLACK) >> 7 & opponent_occupied;
while h_side_capture_targets > 0 {
let to = SQUARES[h_side_capture_targets.trailing_zeros() as usize];
let from = to + 7;
list.push(Move::new(from, to, MOVE_FLAG_CAPTURE));
h_side_capture_targets &= h_side_capture_targets - 1;
}
// 2.4 Promotion
// 2.4.1 Pushing promotion
let mut promotion_targets = ((friendly_pawns & RANK2_MASK) >> 8) & board.empty_squares;
while promotion_targets > 0 {
let to = SQUARES[promotion_targets.trailing_zeros() as usize];
let from = to + 8;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_N));
promotion_targets &= promotion_targets - 1;
}
// 2.4.2 Capturing Promotion
// 2.4.2.1 A-side capturing promotion (>> 9)
let mut promotion_targets_a_side_capture = ((friendly_pawns & PAWN_A_SIDE_CAPTURE_PROMOTION_MASK_BLACK) >> 9) & opponent_occupied;
while promotion_targets_a_side_capture > 0 {
let to = SQUARES[promotion_targets_a_side_capture.trailing_zeros() as usize];
let from = to + 9;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
promotion_targets_a_side_capture &= promotion_targets_a_side_capture - 1;
}
// 2.4.2.2 H-side capturing promotion (>> 7)
let mut promotion_targets_h_side_capture = ((friendly_pawns & PAWN_H_SIDE_CAPTURE_PROMOTION_MASK_BLACK) >> 7) & opponent_occupied;
while promotion_targets_h_side_capture > 0 {
let to = SQUARES[promotion_targets_h_side_capture.trailing_zeros() as usize];
let from = to + 7;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
promotion_targets_h_side_capture &= promotion_targets_h_side_capture - 1;
}
// 2.5 En Passant
if let Some(en_passant_target_square) = board.en_passant_target {
// Check if the target square is on the 3rd rank (A3=16 to H3=23)
if (en_passant_target_square >= Square::A3) && (en_passant_target_square <= Square::H3) {
let en_passant_target_bb: u64 = 1_u64 << (en_passant_target_square as u64);
// 1. Check A-Side capture (>> 9, e.g., B4 -> A3)
let attacker_mask_a_side = (en_passant_target_bb << 9) & PAWN_A_SIDE_CAPTURE_MASK_BLACK;
if (attacker_mask_a_side & friendly_pawns) > 0 {
let from = en_passant_target_square + 9;
list.push(Move::new(from, en_passant_target_square, MOVE_FLAG_EN_PASSANT));
}
// 2. Check H-Side capture (>> 7, e.g., G4 -> H3)
let attacker_mask_h_side = (en_passant_target_bb << 7) & PAWN_H_SIDE_CAPTURE_MASK_BLACK;
if (attacker_mask_h_side & friendly_pawns) > 0 {
let from = en_passant_target_square + 7;
list.push(Move::new(from, en_passant_target_square, MOVE_FLAG_EN_PASSANT));
}
}
}
}
}

View file

@ -0,0 +1,149 @@
use crate::board::*;
<<<<<<< HEAD
use crate::r#move::{Move, MoveList, MOVE_FLAG_CAPTURE, MOVE_FLAG_QUIET};
use crate::square::SQUARES;
use super::tables::*;
pub fn generate_rook_moves(board: &Board, list: &mut MoveList) {
let mut friendly_rooks = board.pieces[PieceType::Rook as usize][board.side_to_move as usize];
=======
use crate::color::Color;
use crate::r#move::{Move, MoveList, MOVE_FLAG_CAPTURE, MOVE_FLAG_QUIET};
use crate::square::{Square, SQUARES};
use super::tables::{PREMASKS_ROOK, PREMASKS_BISHOP, MAGICS_ROOK, MAGICS_BISHOP, RELEVANT_BITS_ROOK, RELEVANT_BITS_BISHOP, get_rook_attacks, get_bishop_attacks};
pub fn generate_rook_moves(board: &Board, list: &mut MoveList) {
let mut friendly_rooks = board.rooks[board.color as usize];
>>>>>>> origin/master
while friendly_rooks > 0 {
let square_index = friendly_rooks.trailing_zeros() as usize;
let premask = PREMASKS_ROOK[square_index];
let magic = MAGICS_ROOK[square_index];
let relevant_bits = RELEVANT_BITS_ROOK[square_index];
let shift = 64 - relevant_bits;
let blockers = board.all_occupied & premask;
let attack_table = get_rook_attacks();
let magic_index = (blockers.wrapping_mul(magic)) >> shift;
let movable_squares = attack_table[square_index][magic_index as usize];
// 1. Normal moves
let mut quiet_moves = movable_squares & !board.all_occupied;
while quiet_moves > 0 {
let from = SQUARES[square_index];
let to = SQUARES[quiet_moves.trailing_zeros() as usize];
list.push(Move::new(from, to, MOVE_FLAG_QUIET));
quiet_moves &= quiet_moves - 1;
}
// 2. Captures
<<<<<<< HEAD
let mut capture_moves = movable_squares & board.occupied[!board.side_to_move as usize];
=======
let mut capture_moves = movable_squares & board.occupied[!board.color as usize];
>>>>>>> origin/master
while capture_moves > 0 {
let from = SQUARES[square_index];
let to = SQUARES[capture_moves.trailing_zeros() as usize];
list.push(Move::new(from, to, MOVE_FLAG_CAPTURE));
capture_moves &= capture_moves - 1;
}
friendly_rooks &= friendly_rooks - 1;
}
}
<<<<<<< HEAD
pub fn generate_bishop_moves(board: &Board, list: &mut MoveList) {
let mut friendly_bishops = board.pieces[PieceType::Bishop as usize][board.side_to_move as usize];
while friendly_bishops > 0 {
let square_index = friendly_bishops.trailing_zeros() as usize;
let premask = PREMASKS_BISHOP[square_index];
let magic = MAGICS_BISHOP[square_index];
let relevant_bits = RELEVANT_BITS_BISHOP[square_index];
let shift = 64 - relevant_bits;
let blockers = board.all_occupied & premask;
let attack_table = get_bishop_attacks();
let magic_index = (blockers.wrapping_mul(magic)) >> shift;
let movable_squares = attack_table[square_index][magic_index as usize];
// 1. Normal moves
let mut quiet_moves = movable_squares & !board.all_occupied;
while quiet_moves > 0 {
let from = SQUARES[square_index];
let to = SQUARES[quiet_moves.trailing_zeros() as usize];
list.push(Move::new(from, to, MOVE_FLAG_QUIET));
quiet_moves &= quiet_moves - 1;
}
// 2. Captures
let mut capture_moves = movable_squares & board.occupied[!board.side_to_move as usize];
while capture_moves > 0 {
let from = SQUARES[square_index];
let to = SQUARES[capture_moves.trailing_zeros() as usize];
list.push(Move::new(from, to, MOVE_FLAG_CAPTURE));
capture_moves &= capture_moves - 1;
}
friendly_bishops &= friendly_bishops - 1;
}
}
pub fn generate_queen_moves(board: &Board, list: &mut MoveList) {
let mut friendly_queens = board.pieces[PieceType::Queen as usize][board.side_to_move as usize];
while friendly_queens > 0 {
let square_index = friendly_queens.trailing_zeros() as usize;
// --- 1. Get Rook Attacks ---
let rook_premask = PREMASKS_ROOK[square_index];
let rook_magic = MAGICS_ROOK[square_index];
let rook_relevant_bits = RELEVANT_BITS_ROOK[square_index];
let rook_shift = 64 - rook_relevant_bits;
let rook_blockers = board.all_occupied & rook_premask;
let rook_attack_table = get_rook_attacks();
let rook_magic_index = (rook_blockers.wrapping_mul(rook_magic)) >> rook_shift;
let rook_moves = rook_attack_table[square_index][rook_magic_index as usize];
// --- 2. Get Bishop Attacks ---
let bishop_premask = PREMASKS_BISHOP[square_index];
let bishop_magic = MAGICS_BISHOP[square_index];
let bishop_relevant_bits = RELEVANT_BITS_BISHOP[square_index];
let bishop_shift = 64 - bishop_relevant_bits;
let bishop_blockers = board.all_occupied & bishop_premask;
let bishop_attack_table = get_bishop_attacks();
let bishop_magic_index = (bishop_blockers.wrapping_mul(bishop_magic)) >> bishop_shift;
let bishop_moves = bishop_attack_table[square_index][bishop_magic_index as usize];
// --- 3. Combine Attacks ---
let movable_squares = rook_moves | bishop_moves;
// --- 4. Generate Moves (Identical to Rook/Bishop) ---
// 4a. Normal moves
let mut quiet_moves = movable_squares & !board.all_occupied;
while quiet_moves > 0 {
let from = SQUARES[square_index];
let to = SQUARES[quiet_moves.trailing_zeros() as usize];
list.push(Move::new(from, to, MOVE_FLAG_QUIET));
quiet_moves &= quiet_moves - 1;
}
// 4b. Captures
let mut capture_moves = movable_squares & board.occupied[!board.side_to_move as usize];
while capture_moves > 0 {
let from = SQUARES[square_index];
let to = SQUARES[capture_moves.trailing_zeros() as usize];
list.push(Move::new(from, to, MOVE_FLAG_CAPTURE));
capture_moves &= capture_moves - 1;
}
friendly_queens &= friendly_queens - 1;
}
}
=======
>>>>>>> origin/master

View file

@ -0,0 +1,647 @@
use std::sync::OnceLock;
pub const KNIGHT_ATTACKS: [u64; 64] = [
0b00000000_00000000_00000000_00000000_00000000_00000010_00000100_00000000, // Square 0 (A1)
0b00000000_00000000_00000000_00000000_00000000_00000101_00001000_00000000, // Square 1 (B1)
0b00000000_00000000_00000000_00000000_00000000_00001010_00010001_00000000, // Square 2 (C1)
0b00000000_00000000_00000000_00000000_00000000_00010100_00100010_00000000, // Square 3 (D1)
0b00000000_00000000_00000000_00000000_00000000_00101000_01000100_00000000, // Square 4 (E1)
0b00000000_00000000_00000000_00000000_00000000_01010000_10001000_00000000, // Square 5 (F1)
0b00000000_00000000_00000000_00000000_00000000_10100000_00010000_00000000, // Square 6 (G1)
0b00000000_00000000_00000000_00000000_00000000_01000000_00100000_00000000, // Square 7 (H1)
0b00000000_00000000_00000000_00000000_00000010_00000100_00000000_00000100, // Square 8 (A2)
0b00000000_00000000_00000000_00000000_00000101_00001000_00000000_00001000, // Square 9 (B2)
0b00000000_00000000_00000000_00000000_00001010_00010001_00000000_00010001, // Square 10 (C2)
0b00000000_00000000_00000000_00000000_00010100_00100010_00000000_00100010, // Square 11 (D2)
0b00000000_00000000_00000000_00000000_00101000_01000100_00000000_01000100, // Square 12 (E2)
0b00000000_00000000_00000000_00000000_01010000_10001000_00000000_10001000, // Square 13 (F2)
0b00000000_00000000_00000000_00000000_10100000_00010000_00000000_00010000, // Square 14 (G2)
0b00000000_00000000_00000000_00000000_01000000_00100000_00000000_00100000, // Square 15 (H2)
0b00000000_00000000_00000000_00000010_00000100_00000000_00000100_00000010, // Square 16 (A3)
0b00000000_00000000_00000000_00000101_00001000_00000000_00001000_00000101, // Square 17 (B3)
0b00000000_00000000_00000000_00001010_00010001_00000000_00010001_00001010, // Square 18 (C3)
0b00000000_00000000_00000000_00010100_00100010_00000000_00100010_00010100, // Square 19 (D3)
0b00000000_00000000_00000000_00101000_01000100_00000000_01000100_00101000, // Square 20 (E3)
0b00000000_00000000_00000000_01010000_10001000_00000000_10001000_01010000, // Square 21 (F3)
0b00000000_00000000_00000000_10100000_00010000_00000000_00010000_10100000, // Square 22 (G3)
0b00000000_00000000_00000000_01000000_00100000_00000000_00100000_01000000, // Square 23 (H3)
0b00000000_00000000_00000010_00000100_00000000_00000100_00000010_00000000, // Square 24 (A4)
0b00000000_00000000_00000101_00001000_00000000_00001000_00000101_00000000, // Square 25 (B4)
0b00000000_00000000_00001010_00010001_00000000_00010001_00001010_00000000, // Square 26 (C4)
0b00000000_00000000_00010100_00100010_00000000_00100010_00010100_00000000, // Square 27 (D4)
0b00000000_00000000_00101000_01000100_00000000_01000100_00101000_00000000, // Square 28 (E4)
0b00000000_00000000_01010000_10001000_00000000_10001000_01010000_00000000, // Square 29 (F4)
0b00000000_00000000_10100000_00010000_00000000_00010000_10100000_00000000, // Square 30 (G4)
0b00000000_00000000_01000000_00100000_00000000_00100000_01000000_00000000, // Square 31 (H4)
0b00000000_00000010_00000100_00000000_00000100_00000010_00000000_00000000, // Square 32 (A5)
0b00000000_00000101_00001000_00000000_00001000_00000101_00000000_00000000, // Square 33 (B5)
0b00000000_00001010_00010001_00000000_00010001_00001010_00000000_00000000, // Square 34 (C5)
0b00000000_00010100_00100010_00000000_00100010_00010100_00000000_00000000, // Square 35 (D5)
0b00000000_00101000_01000100_00000000_01000100_00101000_00000000_00000000, // Square 36 (E5)
0b00000000_01010000_10001000_00000000_10001000_01010000_00000000_00000000, // Square 37 (F5)
0b00000000_10100000_00010000_00000000_00010000_10100000_00000000_00000000, // Square 38 (G5)
0b00000000_01000000_00100000_00000000_00100000_01000000_00000000_00000000, // Square 39 (H5)
0b00000010_00000100_00000000_00000100_00000010_00000000_00000000_00000000, // Square 40 (A6)
0b00000101_00001000_00000000_00001000_00000101_00000000_00000000_00000000, // Square 41 (B6)
0b00001010_00010001_00000000_00010001_00001010_00000000_00000000_00000000, // Square 42 (C6)
0b00010100_00100010_00000000_00100010_00010100_00000000_00000000_00000000, // Square 43 (D6)
0b00101000_01000100_00000000_01000100_00101000_00000000_00000000_00000000, // Square 44 (E6)
0b01010000_10001000_00000000_10001000_01010000_00000000_00000000_00000000, // Square 45 (F6)
0b10100000_00010000_00000000_00010000_10100000_00000000_00000000_00000000, // Square 46 (G6)
0b01000000_00100000_00000000_00100000_01000000_00000000_00000000_00000000, // Square 47 (H6)
0b00000100_00000000_00000100_00000010_00000000_00000000_00000000_00000000, // Square 48 (A7)
0b00001000_00000000_00001000_00000101_00000000_00000000_00000000_00000000, // Square 49 (B7)
0b00010001_00000000_00010001_00001010_00000000_00000000_00000000_00000000, // Square 50 (C7)
0b00100010_00000000_00100010_00010100_00000000_00000000_00000000_00000000, // Square 51 (D7)
0b01000100_00000000_01000100_00101000_00000000_00000000_00000000_00000000, // Square 52 (E7)
0b10001000_00000000_10001000_01010000_00000000_00000000_00000000_00000000, // Square 53 (F7)
0b00010000_00000000_00010000_10100000_00000000_00000000_00000000_00000000, // Square 54 (G7)
0b00100000_00000000_00100000_01000000_00000000_00000000_00000000_00000000, // Square 55 (H7)
0b00000000_00000100_00000010_00000000_00000000_00000000_00000000_00000000, // Square 56 (A8)
0b00000000_00001000_00000101_00000000_00000000_00000000_00000000_00000000, // Square 57 (B8)
0b00000000_00010001_00001010_00000000_00000000_00000000_00000000_00000000, // Square 58 (C8)
0b00000000_00100010_00010100_00000000_00000000_00000000_00000000_00000000, // Square 59 (D8)
0b00000000_01000100_00101000_00000000_00000000_00000000_00000000_00000000, // Square 60 (E8)
0b00000000_10001000_01010000_00000000_00000000_00000000_00000000_00000000, // Square 61 (F8)
0b00000000_00010000_10100000_00000000_00000000_00000000_00000000_00000000, // Square 62 (G8)
0b00000000_00100000_01000000_00000000_00000000_00000000_00000000_00000000, // Square 63 (H8)
];
pub const PREMASKS_ROOK: [u64; 64] = [
0b00000000_00000001_00000001_00000001_00000001_00000001_00000001_01111110, // Square 0 (A1)
0b00000000_00000010_00000010_00000010_00000010_00000010_00000010_01111100, // Square 1 (B1)
0b00000000_00000100_00000100_00000100_00000100_00000100_00000100_01111010, // Square 2 (C1)
0b00000000_00001000_00001000_00001000_00001000_00001000_00001000_01110110, // Square 3 (D1)
0b00000000_00010000_00010000_00010000_00010000_00010000_00010000_01101110, // Square 4 (E1)
0b00000000_00100000_00100000_00100000_00100000_00100000_00100000_01011110, // Square 5 (F1)
0b00000000_01000000_01000000_01000000_01000000_01000000_01000000_00111110, // Square 6 (G1)
0b00000000_10000000_10000000_10000000_10000000_10000000_10000000_01111110, // Square 7 (H1)
0b00000000_00000001_00000001_00000001_00000001_00000001_01111110_00000000, // Square 8 (A2)
0b00000000_00000010_00000010_00000010_00000010_00000010_01111100_00000000, // Square 9 (B2)
0b00000000_00000100_00000100_00000100_00000100_00000100_01111010_00000000, // Square 10 (C2)
0b00000000_00001000_00001000_00001000_00001000_00001000_01110110_00000000, // Square 11 (D2)
0b00000000_00010000_00010000_00010000_00010000_00010000_01101110_00000000, // Square 12 (E2)
0b00000000_00100000_00100000_00100000_00100000_00100000_01011110_00000000, // Square 13 (F2)
0b00000000_01000000_01000000_01000000_01000000_01000000_00111110_00000000, // Square 14 (G2)
0b00000000_10000000_10000000_10000000_10000000_10000000_01111110_00000000, // Square 15 (H2)
0b00000000_00000001_00000001_00000001_00000001_01111110_00000001_00000000, // Square 16 (A3)
0b00000000_00000010_00000010_00000010_00000010_01111100_00000010_00000000, // Square 17 (B3)
0b00000000_00000100_00000100_00000100_00000100_01111010_00000100_00000000, // Square 18 (C3)
0b00000000_00001000_00001000_00001000_00001000_01110110_00001000_00000000, // Square 19 (D3)
0b00000000_00010000_00010000_00010000_00010000_01101110_00010000_00000000, // Square 20 (E3)
0b00000000_00100000_00100000_00100000_00100000_01011110_00100000_00000000, // Square 21 (F3)
0b00000000_01000000_01000000_01000000_01000000_00111110_01000000_00000000, // Square 22 (G3)
0b00000000_10000000_10000000_10000000_10000000_01111110_10000000_00000000, // Square 23 (H3)
0b00000000_00000001_00000001_00000001_01111110_00000001_00000001_00000000, // Square 24 (A4)
0b00000000_00000010_00000010_00000010_01111100_00000010_00000010_00000000, // Square 25 (B4)
0b00000000_00000100_00000100_00000100_01111010_00000100_00000100_00000000, // Square 26 (C4)
0b00000000_00001000_00001000_00001000_01110110_00001000_00001000_00000000, // Square 27 (D4)
0b00000000_00010000_00010000_00010000_01101110_00010000_00010000_00000000, // Square 28 (E4)
0b00000000_00100000_00100000_00100000_01011110_00100000_00100000_00000000, // Square 29 (F4)
0b00000000_01000000_01000000_01000000_00111110_01000000_01000000_00000000, // Square 30 (G4)
0b00000000_10000000_10000000_10000000_01111110_10000000_10000000_00000000, // Square 31 (H4)
0b00000000_00000001_00000001_01111110_00000001_00000001_00000001_00000000, // Square 32 (A5)
0b00000000_00000010_00000010_01111100_00000010_00000010_00000010_00000000, // Square 33 (B5)
0b00000000_00000100_00000100_01111010_00000100_00000100_00000100_00000000, // Square 34 (C5)
0b00000000_00001000_00001000_01110110_00001000_00001000_00001000_00000000, // Square 35 (D5)
0b00000000_00010000_00010000_01101110_00010000_00010000_00010000_00000000, // Square 36 (E5)
0b00000000_00100000_00100000_01011110_00100000_00100000_00100000_00000000, // Square 37 (F5)
0b00000000_01000000_01000000_00111110_01000000_01000000_01000000_00000000, // Square 38 (G5)
0b00000000_10000000_10000000_01111110_10000000_10000000_10000000_00000000, // Square 39 (H5)
0b00000000_00000001_01111110_00000001_00000001_00000001_00000001_00000000, // Square 40 (A6)
0b00000000_00000010_01111100_00000010_00000010_00000010_00000010_00000000, // Square 41 (B6)
0b00000000_00000100_01111010_00000100_00000100_00000100_00000100_00000000, // Square 42 (C6)
0b00000000_00001000_01110110_00001000_00001000_00001000_00001000_00000000, // Square 43 (D6)
0b00000000_00010000_01101110_00010000_00010000_00010000_00010000_00000000, // Square 44 (E6)
0b00000000_00100000_01011110_00100000_00100000_00100000_00100000_00000000, // Square 45 (F6)
0b00000000_01000000_00111110_01000000_01000000_01000000_01000000_00000000, // Square 46 (G6)
0b00000000_10000000_01111110_10000000_10000000_10000000_10000000_00000000, // Square 47 (H6)
0b00000000_01111110_00000001_00000001_00000001_00000001_00000001_00000000, // Square 48 (A7)
0b00000000_01111100_00000010_00000010_00000010_00000010_00000010_00000000, // Square 49 (B7)
0b00000000_01111010_00000100_00000100_00000100_00000100_00000100_00000000, // Square 50 (C7)
0b00000000_01110110_00001000_00001000_00001000_00001000_00001000_00000000, // Square 51 (D7)
0b00000000_01101110_00010000_00010000_00010000_00010000_00010000_00000000, // Square 52 (E7)
0b00000000_01011110_00100000_00100000_00100000_00100000_00100000_00000000, // Square 53 (F7)
0b00000000_00111110_01000000_01000000_01000000_01000000_01000000_00000000, // Square 54 (G7)
0b00000000_01111110_10000000_10000000_10000000_10000000_10000000_00000000, // Square 55 (H7)
0b01111110_00000001_00000001_00000001_00000001_00000001_00000001_00000000, // Square 56 (A8)
0b01111100_00000010_00000010_00000010_00000010_00000010_00000010_00000000, // Square 57 (B8)
0b01111010_00000100_00000100_00000100_00000100_00000100_00000100_00000000, // Square 58 (C8)
0b01110110_00001000_00001000_00001000_00001000_00001000_00001000_00000000, // Square 59 (D8)
0b01101110_00010000_00010000_00010000_00010000_00010000_00010000_00000000, // Square 60 (E8)
0b01011110_00100000_00100000_00100000_00100000_00100000_00100000_00000000, // Square 61 (F8)
0b00111110_01000000_01000000_01000000_01000000_01000000_01000000_00000000, // Square 62 (G8)
0b01111110_10000000_10000000_10000000_10000000_10000000_10000000_00000000, // Square 63 (H8)
];
pub const PREMASKS_BISHOP: [u64; 64] = [
0b00000000_01000000_00100000_00010000_00001000_00000100_00000010_00000000, // Square 0 (A1)
0b00000000_00000000_01000000_00100000_00010000_00001000_00000100_00000000, // Square 1 (B1)
0b00000000_00000000_00000000_01000000_00100000_00010000_00001010_00000000, // Square 2 (C1)
0b00000000_00000000_00000000_00000000_01000000_00100010_00010100_00000000, // Square 3 (D1)
0b00000000_00000000_00000000_00000000_00000010_01000100_00101000_00000000, // Square 4 (E1)
0b00000000_00000000_00000000_00000010_00000100_00001000_01010000_00000000, // Square 5 (F1)
0b00000000_00000000_00000010_00000100_00001000_00010000_00100000_00000000, // Square 6 (G1)
0b00000000_00000010_00000100_00001000_00010000_00100000_01000000_00000000, // Square 7 (H1)
0b00000000_00100000_00010000_00001000_00000100_00000010_00000000_00000000, // Square 8 (A2)
0b00000000_01000000_00100000_00010000_00001000_00000100_00000000_00000000, // Square 9 (B2)
0b00000000_00000000_01000000_00100000_00010000_00001010_00000000_00000000, // Square 10 (C2)
0b00000000_00000000_00000000_01000000_00100010_00010100_00000000_00000000, // Square 11 (D2)
0b00000000_00000000_00000000_00000010_01000100_00101000_00000000_00000000, // Square 12 (E2)
0b00000000_00000000_00000010_00000100_00001000_01010000_00000000_00000000, // Square 13 (F2)
0b00000000_00000010_00000100_00001000_00010000_00100000_00000000_00000000, // Square 14 (G2)
0b00000000_00000100_00001000_00010000_00100000_01000000_00000000_00000000, // Square 15 (H2)
0b00000000_00010000_00001000_00000100_00000010_00000000_00000010_00000000, // Square 16 (A3)
0b00000000_00100000_00010000_00001000_00000100_00000000_00000100_00000000, // Square 17 (B3)
0b00000000_01000000_00100000_00010000_00001010_00000000_00001010_00000000, // Square 18 (C3)
0b00000000_00000000_01000000_00100010_00010100_00000000_00010100_00000000, // Square 19 (D3)
0b00000000_00000000_00000010_01000100_00101000_00000000_00101000_00000000, // Square 20 (E3)
0b00000000_00000010_00000100_00001000_01010000_00000000_01010000_00000000, // Square 21 (F3)
0b00000000_00000100_00001000_00010000_00100000_00000000_00100000_00000000, // Square 22 (G3)
0b00000000_00001000_00010000_00100000_01000000_00000000_01000000_00000000, // Square 23 (H3)
0b00000000_00001000_00000100_00000010_00000000_00000010_00000100_00000000, // Square 24 (A4)
0b00000000_00010000_00001000_00000100_00000000_00000100_00001000_00000000, // Square 25 (B4)
0b00000000_00100000_00010000_00001010_00000000_00001010_00010000_00000000, // Square 26 (C4)
0b00000000_01000000_00100010_00010100_00000000_00010100_00100010_00000000, // Square 27 (D4)
0b00000000_00000010_01000100_00101000_00000000_00101000_01000100_00000000, // Square 28 (E4)
0b00000000_00000100_00001000_01010000_00000000_01010000_00001000_00000000, // Square 29 (F4)
0b00000000_00001000_00010000_00100000_00000000_00100000_00010000_00000000, // Square 30 (G4)
0b00000000_00010000_00100000_01000000_00000000_01000000_00100000_00000000, // Square 31 (H4)
0b00000000_00000100_00000010_00000000_00000010_00000100_00001000_00000000, // Square 32 (A5)
0b00000000_00001000_00000100_00000000_00000100_00001000_00010000_00000000, // Square 33 (B5)
0b00000000_00010000_00001010_00000000_00001010_00010000_00100000_00000000, // Square 34 (C5)
0b00000000_00100010_00010100_00000000_00010100_00100010_01000000_00000000, // Square 35 (D5)
0b00000000_01000100_00101000_00000000_00101000_01000100_00000010_00000000, // Square 36 (E5)
0b00000000_00001000_01010000_00000000_01010000_00001000_00000100_00000000, // Square 37 (F5)
0b00000000_00010000_00100000_00000000_00100000_00010000_00001000_00000000, // Square 38 (G5)
0b00000000_00100000_01000000_00000000_01000000_00100000_00010000_00000000, // Square 39 (H5)
0b00000000_00000010_00000000_00000010_00000100_00001000_00010000_00000000, // Square 40 (A6)
0b00000000_00000100_00000000_00000100_00001000_00010000_00100000_00000000, // Square 41 (B6)
0b00000000_00001010_00000000_00001010_00010000_00100000_01000000_00000000, // Square 42 (C6)
0b00000000_00010100_00000000_00010100_00100010_01000000_00000000_00000000, // Square 43 (D6)
0b00000000_00101000_00000000_00101000_01000100_00000010_00000000_00000000, // Square 44 (E6)
0b00000000_01010000_00000000_01010000_00001000_00000100_00000010_00000000, // Square 45 (F6)
0b00000000_00100000_00000000_00100000_00010000_00001000_00000100_00000000, // Square 46 (G6)
0b00000000_01000000_00000000_01000000_00100000_00010000_00001000_00000000, // Square 47 (H6)
0b00000000_00000000_00000010_00000100_00001000_00010000_00100000_00000000, // Square 48 (A7)
0b00000000_00000000_00000100_00001000_00010000_00100000_01000000_00000000, // Square 49 (B7)
0b00000000_00000000_00001010_00010000_00100000_01000000_00000000_00000000, // Square 50 (C7)
0b00000000_00000000_00010100_00100010_01000000_00000000_00000000_00000000, // Square 51 (D7)
0b00000000_00000000_00101000_01000100_00000010_00000000_00000000_00000000, // Square 52 (E7)
0b00000000_00000000_01010000_00001000_00000100_00000010_00000000_00000000, // Square 53 (F7)
0b00000000_00000000_00100000_00010000_00001000_00000100_00000010_00000000, // Square 54 (G7)
0b00000000_00000000_01000000_00100000_00010000_00001000_00000100_00000000, // Square 55 (H7)
0b00000000_00000010_00000100_00001000_00010000_00100000_01000000_00000000, // Square 56 (A8)
0b00000000_00000100_00001000_00010000_00100000_01000000_00000000_00000000, // Square 57 (B8)
0b00000000_00001010_00010000_00100000_01000000_00000000_00000000_00000000, // Square 58 (C8)
0b00000000_00010100_00100010_01000000_00000000_00000000_00000000_00000000, // Square 59 (D8)
0b00000000_00101000_01000100_00000010_00000000_00000000_00000000_00000000, // Square 60 (E8)
0b00000000_01010000_00001000_00000100_00000010_00000000_00000000_00000000, // Square 61 (F8)
0b00000000_00100000_00010000_00001000_00000100_00000010_00000000_00000000, // Square 62 (G8)
0b00000000_01000000_00100000_00010000_00001000_00000100_00000010_00000000, // Square 63 (H8)
];
pub const KING_ATTACKS: [u64; 64] = [
0b00000000_00000000_00000000_00000000_00000000_00000000_00000011_00000010, // Square 0 (A1)
0b00000000_00000000_00000000_00000000_00000000_00000000_00000111_00000101, // Square 1 (B1)
0b00000000_00000000_00000000_00000000_00000000_00000000_00001110_00001010, // Square 2 (C1)
0b00000000_00000000_00000000_00000000_00000000_00000000_00011100_00010100, // Square 3 (D1)
0b00000000_00000000_00000000_00000000_00000000_00000000_00111000_00101000, // Square 4 (E1)
0b00000000_00000000_00000000_00000000_00000000_00000000_01110000_01010000, // Square 5 (F1)
0b00000000_00000000_00000000_00000000_00000000_00000000_11100000_10100000, // Square 6 (G1)
0b00000000_00000000_00000000_00000000_00000000_00000000_11000000_01000000, // Square 7 (H1)
0b00000000_00000000_00000000_00000000_00000000_00000011_00000010_00000011, // Square 8 (A2)
0b00000000_00000000_00000000_00000000_00000000_00000111_00000101_00000111, // Square 9 (B2)
0b00000000_00000000_00000000_00000000_00000000_00001110_00001010_00001110, // Square 10 (C2)
0b00000000_00000000_00000000_00000000_00000000_00011100_00010100_00011100, // Square 11 (D2)
0b00000000_00000000_00000000_00000000_00000000_00111000_00101000_00111000, // Square 12 (E2)
0b00000000_00000000_00000000_00000000_00000000_01110000_01010000_01110000, // Square 13 (F2)
0b00000000_00000000_00000000_00000000_00000000_11100000_10100000_11100000, // Square 14 (G2)
0b00000000_00000000_00000000_00000000_00000000_11000000_01000000_11000000, // Square 15 (H2)
0b00000000_00000000_00000000_00000000_00000011_00000010_00000011_00000000, // Square 16 (A3)
0b00000000_00000000_00000000_00000000_00000111_00000101_00000111_00000000, // Square 17 (B3)
0b00000000_00000000_00000000_00000000_00001110_00001010_00001110_00000000, // Square 18 (C3)
0b00000000_00000000_00000000_00000000_00011100_00010100_00011100_00000000, // Square 19 (D3)
0b00000000_00000000_00000000_00000000_00111000_00101000_00111000_00000000, // Square 20 (E3)
0b00000000_00000000_00000000_00000000_01110000_01010000_01110000_00000000, // Square 21 (F3)
0b00000000_00000000_00000000_00000000_11100000_10100000_11100000_00000000, // Square 22 (G3)
0b00000000_00000000_00000000_00000000_11000000_01000000_11000000_00000000, // Square 23 (H3)
0b00000000_00000000_00000000_00000011_00000010_00000011_00000000_00000000, // Square 24 (A4)
0b00000000_00000000_00000000_00000111_00000101_00000111_00000000_00000000, // Square 25 (B4)
0b00000000_00000000_00000000_00001110_00001010_00001110_00000000_00000000, // Square 26 (C4)
0b00000000_00000000_00000000_00011100_00010100_00011100_00000000_00000000, // Square 27 (D4)
0b00000000_00000000_00000000_00111000_00101000_00111000_00000000_00000000, // Square 28 (E4)
0b00000000_00000000_00000000_01110000_01010000_01110000_00000000_00000000, // Square 29 (F4)
0b00000000_00000000_00000000_11100000_10100000_11100000_00000000_00000000, // Square 30 (G4)
0b00000000_00000000_00000000_11000000_01000000_11000000_00000000_00000000, // Square 31 (H4)
0b00000000_00000000_00000011_00000010_00000011_00000000_00000000_00000000, // Square 32 (A5)
0b00000000_00000000_00000111_00000101_00000111_00000000_00000000_00000000, // Square 33 (B5)
0b00000000_00000000_00001110_00001010_00001110_00000000_00000000_00000000, // Square 34 (C5)
0b00000000_00000000_00011100_00010100_00011100_00000000_00000000_00000000, // Square 35 (D5)
0b00000000_00000000_00111000_00101000_00111000_00000000_00000000_00000000, // Square 36 (E5)
0b00000000_00000000_01110000_01010000_01110000_00000000_00000000_00000000, // Square 37 (F5)
0b00000000_00000000_11100000_10100000_11100000_00000000_00000000_00000000, // Square 38 (G5)
0b00000000_00000000_11000000_01000000_11000000_00000000_00000000_00000000, // Square 39 (H5)
0b00000000_00000011_00000010_00000011_00000000_00000000_00000000_00000000, // Square 40 (A6)
0b00000000_00000111_00000101_00000111_00000000_00000000_00000000_00000000, // Square 41 (B6)
0b00000000_00001110_00001010_00001110_00000000_00000000_00000000_00000000, // Square 42 (C6)
0b00000000_00011100_00010100_00011100_00000000_00000000_00000000_00000000, // Square 43 (D6)
0b00000000_00111000_00101000_00111000_00000000_00000000_00000000_00000000, // Square 44 (E6)
0b00000000_01110000_01010000_01110000_00000000_00000000_00000000_00000000, // Square 45 (F6)
0b00000000_11100000_10100000_11100000_00000000_00000000_00000000_00000000, // Square 46 (G6)
0b00000000_11000000_01000000_11000000_00000000_00000000_00000000_00000000, // Square 47 (H6)
0b00000011_00000010_00000011_00000000_00000000_00000000_00000000_00000000, // Square 48 (A7)
0b00000111_00000101_00000111_00000000_00000000_00000000_00000000_00000000, // Square 49 (B7)
0b00001110_00001010_00001110_00000000_00000000_00000000_00000000_00000000, // Square 50 (C7)
0b00011100_00010100_00011100_00000000_00000000_00000000_00000000_00000000, // Square 51 (D7)
0b00111000_00101000_00111000_00000000_00000000_00000000_00000000_00000000, // Square 52 (E7)
0b01110000_01010000_01110000_00000000_00000000_00000000_00000000_00000000, // Square 53 (F7)
0b11100000_10100000_11100000_00000000_00000000_00000000_00000000_00000000, // Square 54 (G7)
0b11000000_01000000_11000000_00000000_00000000_00000000_00000000_00000000, // Square 55 (H7)
0b00000010_00000011_00000000_00000000_00000000_00000000_00000000_00000000, // Square 56 (A8)
0b00000101_00000111_00000000_00000000_00000000_00000000_00000000_00000000, // Square 57 (B8)
0b00001010_00001110_00000000_00000000_00000000_00000000_00000000_00000000, // Square 58 (C8)
0b00010100_00011100_00000000_00000000_00000000_00000000_00000000_00000000, // Square 59 (D8)
0b00101000_00111000_00000000_00000000_00000000_00000000_00000000_00000000, // Square 60 (E8)
0b01010000_01110000_00000000_00000000_00000000_00000000_00000000_00000000, // Square 61 (F8)
0b10100000_11100000_00000000_00000000_00000000_00000000_00000000_00000000, // Square 62 (G8)
0b01000000_11000000_00000000_00000000_00000000_00000000_00000000_00000000, // Square 63 (H8)
];
pub const MAGICS_ROOK: [u64; 64] = [
0b10001010_10000000_00010000_01000000_00000000_10000000_00000000_00100000, // Square 0 (A1)
0b00000001_01000000_00000000_00100000_00000000_00010000_00000000_01000000, // Square 1 (B1)
0b00000010_10000000_00011000_10000000_10100000_00000001_01110000_00000001, // Square 2 (C1)
0b00000001_00000000_00001000_00010000_00000001_00000000_00000100_00100000, // Square 3 (D1)
0b00000010_00000000_00000010_00000000_00010000_00001000_00000100_00100000, // Square 4 (E1)
0b00000011_00000000_00011100_00000000_00000010_00000001_00000000_00001000, // Square 5 (F1)
0b10000100_10000000_00000000_10000000_00000010_00000000_00000001_00000000, // Square 6 (G1)
0b00100000_10000000_00001000_10000000_00000100_01000000_00101001_00000000, // Square 7 (H1)
0b00000000_00000000_10000000_00000000_10011000_00100000_01000000_00000000, // Square 8 (A2)
0b00100000_00100100_01000000_00010000_00000000_00100000_00000000_01000000, // Square 9 (B2)
0b00000001_00000000_10000000_00100000_00000000_10000000_00010000_00000000, // Square 10 (C2)
0b00000001_00100000_10000000_00001000_00000000_10000000_00010000_00000000, // Square 11 (D2)
0b00000010_00001000_10000000_10000000_10001000_00000000_00000100_00000000, // Square 12 (E2)
0b00000000_00000010_10000000_00100010_00000000_10000000_00000100_00000000, // Square 13 (F2)
0b00100010_00000000_10000000_00000001_00000000_00000010_00000000_10000000, // Square 14 (G2)
0b00001000_00000001_00000000_00000000_01100000_10000010_00010001_00000000, // Square 15 (H2)
0b00000000_10000000_00000100_01000000_00000110_01000010_00100000_00000000, // Square 16 (A3)
0b00000001_00000000_10000000_10000000_00100000_00000000_01000000_00000000, // Square 17 (B3)
0b00010010_00010000_10001010_00000000_00010000_00100000_01000010_00000000, // Square 18 (C3)
0b00000001_01000000_10000100_10000000_00010000_00000000_00001000_00000010, // Square 19 (D3)
0b00000100_10000001_10000010_10000000_00010100_00000000_00101000_00000000, // Square 20 (E3)
0b10000000_10010100_00000000_01000000_00000010_00000000_01000001_00000000, // Square 21 (F3)
0b01000000_00010000_00000100_00000000_00010000_00000001_00001000_00000010, // Square 22 (G3)
0b00000000_00000000_00000010_00000000_00001000_10000000_01100001_00000100, // Square 23 (H3)
0b00000001_00000000_01000000_00000000_10000000_00100000_10000000_00000000, // Square 24 (A4)
0b00100000_01000000_00000000_00100001_00100000_00001000_00010000_00000000, // Square 25 (B4)
0b00000000_00100001_00100000_00000110_10000000_00010000_00000000_10000001, // Square 26 (C4)
0b00000000_00100000_00010000_00000000_10000000_00001000_00000000_10000000, // Square 27 (D4)
0b00000000_00000010_00000000_00001010_00000000_00100000_00000100_00010000, // Square 28 (E4)
0b00000000_00000000_00000010_00000000_10000000_10000000_00000100_00000000, // Square 29 (F4)
0b00000000_10000000_00001000_10000100_00000000_00010000_00000001_00000010, // Square 30 (G4)
0b00000000_10000000_00000000_01000110_00000000_00000100_00101000_10000001, // Square 31 (H4)
0b01000000_01000000_00000000_10000000_01000000_10000000_00000000_00100000, // Square 32 (A5)
0b00000100_01000000_00000000_00110000_00000000_00100000_00001000_00000001, // Square 33 (B5)
0b00000000_00000100_00100000_00000000_00010001_00000000_01000101_00000000, // Square 34 (C5)
0b00000001_10001000_00000010_00000000_00010000_00010000_00000001_00000000, // Square 35 (D5)
0b00000000_00010100_10000000_00000100_00000001_10000000_00101000_00000000, // Square 36 (E5)
0b00100000_10000000_00000100_00000000_10000000_10000000_00000010_00000000, // Square 37 (F5)
0b00000001_00100100_00001000_00000010_00000100_00000000_00010000_00000001, // Square 38 (G5)
0b00000010_00000000_00000100_01100101_00000010_00000000_00000100_10000100, // Square 39 (H5)
0b00000100_10000000_01000000_00000000_10000000_00001000_10000000_00100000, // Square 40 (A6)
0b00010000_00000000_01000010_00100000_00010000_00000011_01000000_00000000, // Square 41 (B6)
0b00000000_00110000_00100000_00000001_00000000_00010001_00000000_01000000, // Square 42 (C6)
0b00000000_00000000_00010000_00000000_00100001_00000001_00000000_00001001, // Square 43 (D6)
0b00100000_00000010_00001000_00000001_00000000_00010001_00000000_00000100, // Square 44 (E6)
0b00000010_00000010_00000000_10000000_00000100_00000000_10000000_00000010, // Square 45 (F6)
0b00000000_00100000_00000010_00000000_00000100_00000001_00000001_00000000, // Square 46 (G6)
0b00100000_01001000_01000100_00000000_01000000_10000010_00000000_00000001, // Square 47 (H6)
0b00000001_00000001_00000000_00100010_00000000_01000000_10000010_00000000, // Square 48 (A7)
0b00000000_01000000_10000000_00100000_00000000_01000000_00010000_10000000, // Square 49 (B7)
0b01000000_00001000_00010100_00100000_00000100_01000001_00000001_00000000, // Square 50 (C7)
0b00000010_00000110_00001000_00100000_11000000_00010010_00000010_00000000, // Square 51 (D7)
0b00000000_00000001_00000000_00010000_00000100_00001000_00000001_00000000, // Square 52 (E7)
0b00000010_00001100_00000010_00000000_10000000_00000100_00000000_10000000, // Square 53 (F7)
0b00101001_00110101_01100001_00001000_00110000_00000010_00100100_00000000, // Square 54 (G7)
0b00000000_01000100_01000100_00000000_01000001_00000000_10010010_00000000, // Square 55 (H7)
0b00000010_10000000_00000000_00010000_01000000_10000000_00100001_00000001, // Square 56 (A8)
0b00100001_00000000_00011001_00000000_01000000_00000000_00100000_10000101, // Square 57 (B8)
0b10000000_11000000_00001000_01000001_00000000_00010000_00100000_00000001, // Square 58 (C8)
0b01000000_00100100_00001000_00010000_00000001_00000000_00000100_00100001, // Square 59 (D8)
0b00000000_00000010_00000000_00110000_10100000_00100100_01001000_01110010, // Square 60 (E8)
0b00000000_00010010_00000000_00010000_00001000_01000001_01000100_00000010, // Square 61 (F8)
0b00000010_00000000_01100001_00000100_10010000_00001010_00001000_00000100, // Square 62 (G8)
0b00000000_00000001_00000000_01000000_10000001_00000000_00100100_00000010, // Square 63 (H8)
];
pub const MAGICS_BISHOP: [u64; 64] = [
0b00000000_01000000_00000100_00001000_01000100_01000000_01000000_10000100, // Square 0 (A1)
0b00000000_00100000_00000100_00100000_10001010_00000000_01000010_00001000, // Square 1 (B1)
0b00000000_00010000_00011001_00000000_01000001_00001000_00000010_00000010, // Square 2 (C1)
0b00000001_00001000_00000110_00001000_01000101_00000100_00100000_00010000, // Square 3 (D1)
0b00000101_10000001_00010000_01000001_10000000_10000000_00000010_00010000, // Square 4 (E1)
0b00100001_00010010_00001000_00000100_01000110_00100000_00000000_00010000, // Square 5 (F1)
0b00010000_10000000_10000010_00001000_00100000_00000110_00000010_00010000, // Square 6 (G1)
0b00000011_11000000_10000000_10000100_00010000_00100010_00000010_00000000, // Square 7 (H1)
0b00000000_00000100_00000101_00000100_00000100_01000100_00000100_00000100, // Square 8 (A2)
0b00000000_00000000_00000010_00010000_00000001_01000010_00000000_10001000, // Square 9 (B2)
0b00100100_11010000_00001000_00001000_00000001_00001000_00100001_00000010, // Square 10 (C2)
0b00000000_00000001_00000010_00001010_00001010_00000010_00000100_00000000, // Square 11 (D2)
0b00000000_00000000_00000100_00000011_00001000_00100000_00000100_00000010, // Square 12 (E2)
0b00000000_00000100_00000001_00010000_00000010_00010000_00001000_00000000, // Square 13 (F2)
0b00000100_00000001_01001000_01000001_00000100_00010000_01000000_00000101, // Square 14 (G2)
0b00001000_00000001_00000001_00000100_00000010_00000010_00000010_00000000, // Square 15 (H2)
0b00000000_01000000_00000010_00010000_11000011_10001000_00000001_00000000, // Square 16 (A3)
0b00000100_00000100_00000010_00100000_00100100_00010000_10000010_00000000, // Square 17 (B3)
0b00001000_00010000_00000001_10000010_00000000_00100000_01000001_00000010, // Square 18 (C3)
0b00000000_00000100_00000000_00101000_00000001_10100000_00100000_00000011, // Square 19 (D3)
0b00000000_10000101_00000100_00001000_00100000_00001000_00000100_00000000, // Square 20 (E3)
0b10000001_00000001_00000010_11001000_00001000_10001000_00000100_00000000, // Square 21 (F3)
0b00000000_00001110_10010000_00000100_00010000_10001000_01001000_00000000, // Square 22 (G3)
0b10000000_00000010_00000010_00000100_10000000_10000100_00000001_00000010, // Square 23 (H3)
0b00000010_00100000_00100000_00001000_01100101_00001001_00000010_00000001, // Square 24 (A4)
0b00100000_00010000_00010000_00001010_00000010_00000010_00010010_00000010, // Square 25 (B4)
0b00000001_01010010_00000100_10000100_00001000_00000010_00100100_00000001, // Square 26 (C4)
0b00000000_00100000_00001000_00000000_00000010_00001000_00010001_00010000, // Square 27 (D4)
0b01000000_00000001_00000000_00010000_00100001_00000000_01000000_00000000, // Square 28 (E4)
0b10000000_00000000_01000000_01000000_00001010_00000001_00010000_00000010, // Square 29 (F4)
0b00000000_11100100_00000000_01000000_10000001_00000001_00010000_00000010, // Square 30 (G4)
0b00000000_00011100_00000000_01000000_00000001_00000001_00100000_10000000, // Square 31 (H4)
0b10000000_00000100_00100000_00001001_01100010_10100000_00000010_00100000, // Square 32 (A5)
0b10000100_00100010_00010000_00000010_00001000_01010000_00000010_00000010, // Square 33 (B5)
0b00100000_00000000_01000000_00100010_00000000_00110000_00001100_00001000, // Square 34 (C5)
0b10000110_01000110_00000010_00000000_10000000_00001000_00000000_10000000, // Square 35 (D5)
0b10000000_00000010_00001010_00000010_00000000_00010000_00001000_00001000, // Square 36 (E5)
0b00100000_00010000_00000000_01001000_10000000_00010001_00010000_00000000, // Square 37 (F5)
0b01100010_00110000_00000000_10100000_10000000_00000001_00010100_00000000, // Square 38 (G5)
0b01000010_00000000_10001100_00000011_01000000_00100000_10010010_00000010, // Square 39 (H5)
0b00000010_00001001_00011000_10000010_01000000_00000000_00010000_00000000, // Square 40 (A6)
0b01000000_00000100_00001000_10101000_10000100_00000000_00011000_00000000, // Square 41 (B6)
0b00000000_00010001_00000100_00000000_10100110_00001000_00000100_00000000, // Square 42 (C6)
0b00011000_01000000_00000110_00001010_01000100_00000010_00001000_00000000, // Square 43 (D6)
0b00000000_10010000_00001000_00000001_00000100_00000000_00000000_01000001, // Square 44 (E6)
0b00000010_00000001_00000001_00010000_00000000_10000000_10000001_00000001, // Square 45 (F6)
0b00011010_00100010_00001000_00001000_00000101_00000100_11110000_10000000, // Square 46 (G6)
0b10000000_00010010_00000010_00000110_00000000_00100001_00010010_00010010, // Square 47 (H6)
0b00000101_00000000_10000110_00010000_00010001_00100100_00000000_00000000, // Square 48 (A7)
0b00000001_10000000_10000000_01100001_00001000_00100000_00001000_00000000, // Square 49 (B7)
0b01000000_00000000_00000010_00001110_00000001_00000100_00000000_01000100, // Square 50 (C7)
0b00110000_00000000_00000000_00100110_00010000_01000100_00000000_00001010, // Square 51 (D7)
0b00001000_00000010_00100100_00010001_00000010_00000010_00000000_00000010, // Square 52 (E7)
0b00000000_00100000_10010000_01100000_01100001_00100001_00000000_00000001, // Square 53 (F7)
0b01011010_10000100_10000100_00010000_00000100_00000001_00000011_00010000, // Square 54 (G7)
0b00000000_00000100_00000001_00001000_00000001_00000001_00011100_00000100, // Square 55 (H7)
0b00000000_00001010_00000001_00000001_00001001_01010000_00100010_00000000, // Square 56 (A8)
0b00000000_00000000_00000000_01001010_00000010_00000001_00100000_00000000, // Square 57 (B8)
0b01010000_00000010_00000001_00000001_00000000_10011000_10110000_00101000, // Square 58 (C8)
0b10000000_01000000_00000000_00101000_00010001_00000100_00001001_00000000, // Square 59 (D8)
0b00000000_00101000_00000000_00000000_00010000_00000010_00000010_00000100, // Square 60 (E8)
0b00000110_00000000_00000000_00100000_00100000_00101101_00000010_01000000, // Square 61 (F8)
0b10001001_00011000_10000100_01001000_01000010_00001000_00100010_00000000, // Square 62 (G8)
0b01000000_00010000_00000001_00010000_00101001_00000010_00000000_00100000, // Square 63 (H8)
];
pub const RELEVANT_BITS_ROOK: [u8; 64] = [
12, 11, 11, 11, 11, 11, 11, 12,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
12, 11, 11, 11, 11, 11, 11, 12,
];
pub const RELEVANT_BITS_BISHOP: [u8; 64] = [
6, 5, 5, 5, 5, 5, 5, 6,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 5, 5, 5, 5, 5, 5, 6,
];
static ROOK_ATTACKS: OnceLock<Box<[[u64; 4096]; 64]>> = OnceLock::new();
static BISHOP_ATTACKS: OnceLock<Box<[[u64; 512]; 64]>> = OnceLock::new();
// HILFSFUNKTION: Berechnet Turmzüge "langsam"
// Diese Funktion wird nur beim "Backen" der Tabellen verwendet.
fn calculate_rook_attacks_slowly(square: usize, blockers: u64) -> u64 {
let mut attacks = 0_u64;
let rank = square / 8;
let file = square % 8;
// 1. Nach Norden (rank +)
for r in (rank + 1)..=7 {
let target_sq = r * 8 + file;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break; // Wir treffen einen Blocker, stopp
}
}
// 2. Nach Süden (rank -)
for r in (0..rank).rev() {
let target_sq = r * 8 + file;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break;
}
}
// 3. Nach Osten (file +)
for f in (file + 1)..=7 {
let target_sq = rank * 8 + f;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break;
}
}
// 4. Nach Westen (file -)
for f in (0..file).rev() {
let target_sq = rank * 8 + f;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break;
}
}
attacks
}
fn generate_final_rook_attacks() -> Box<[[u64; 4096]; 64]> {
// Heap-Allokation (Dein Code war hier korrekt)
let mut v: Vec<[u64; 4096]> = Vec::with_capacity(64);
for _ in 0..64 {
v.push([0_u64; 4096]);
}
let mut final_rook_attacks: Box<[[u64; 4096]; 64]> = v.try_into().unwrap_or_else(|_| {
panic!("Vec to Box conversion failed.");
});
// Haupt-Back-Schleife
for square_index in 0_usize..=63_usize {
let premask = PREMASKS_ROOK[square_index]; // [deine Quelle]
let magic = MAGICS_ROOK[square_index]; // [deine Quelle]
// ACHTUNG: Hier war ein Fehler in deinem Code, du hattest BISHOP statt ROOK
let relevant_bits = RELEVANT_BITS_ROOK[square_index]; // [deine Quelle]
let shift = 64 - relevant_bits;
// Schleife durch alle 2^n Blocker-Kombinationen
let mut blocker_combination = 0_u64;
loop {
// ---- HIER IST DIE KORREKTE LOGIK ----
// 1. Berechne die "echten" Züge für diese Blocker-Kombination (der langsame Weg)
let attack_squares = calculate_rook_attacks_slowly(square_index, blocker_combination);
// 2. Berechne den "magischen" Speicherort
let magic_index = (blocker_combination.wrapping_mul(magic)) >> shift;
let magic_index_as_usize = magic_index as usize;
// 3. Speichere die echten Züge an diesem magischen Ort
final_rook_attacks[square_index][magic_index_as_usize] = attack_squares;
// ---- ENDE DER LOGIK ----
// Gehe zur nächsten Blocker-Kombination (Dein Code war hier korrekt)
if blocker_combination == premask {
break;
}
blocker_combination = blocker_combination.wrapping_sub(premask) & premask;
}
}
final_rook_attacks
}
pub fn get_rook_attacks() -> &'static Box<[[u64; 4096]; 64]> {
// get_or_init stellt sicher, dass generate_final_rook_attacks()
// nur beim allerersten Aufruf ausgeführt wird.
// Alle anderen Aufrufe geben sofort die fertige Tabelle zurück.
ROOK_ATTACKS.get_or_init(generate_final_rook_attacks)
}
// HILFSFUNKTION: Berechnet Läuferzüge "langsam"
// Diese Funktion wird nur beim "Backen" der Tabellen verwendet.
fn calculate_bishop_attacks_slowly(square: usize, blockers: u64) -> u64 {
let mut attacks = 0_u64;
let rank = square / 8;
let file = square % 8;
// Temporäre Rank/File-Iteratoren
let (mut r, mut f);
// 1. Nach Nord-Osten (rank+, file+)
r = rank + 1;
f = file + 1;
while r <= 7 && f <= 7 {
let target_sq = r * 8 + f;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break; // Wir treffen einen Blocker, stopp
}
r += 1;
f += 1;
}
// 2. Nach Süd-Osten (rank-, file+)
r = rank; // Start bei rank, da 0..rank fehlschlägt wenn rank = 0
f = file + 1;
while r > 0 && f <= 7 { // r > 0 (da r = rank-1 in der 1. Iteration)
r -= 1;
let target_sq = r * 8 + f;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break;
}
f += 1;
}
// 3. Nach Süd-Westen (rank-, file-)
r = rank;
f = file;
while r > 0 && f > 0 { // r > 0 und f > 0
r -= 1;
f -= 1;
let target_sq = r * 8 + f;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break;
}
}
// 4. Nach Nord-Westen (rank+, file-)
r = rank + 1;
f = file;
while r <= 7 && f > 0 { // f > 0
f -= 1;
let target_sq = r * 8 + f;
attacks |= 1_u64 << target_sq;
if (blockers >> target_sq) & 1 == 1 {
break;
}
r += 1;
}
attacks
}
fn generate_final_bishop_attacks() -> Box<[[u64; 512]; 64]> {
// Heap-Allokation
// (Array ist kleiner: 512 statt 4096. Stack Overflow wäre hier unwahrscheinlich,
// aber wir bleiben konsistent mit der Turm-Logik.)
let mut v: Vec<[u64; 512]> = Vec::with_capacity(64);
for _ in 0..64 {
v.push([0_u64; 512]);
}
let mut final_bishop_attacks: Box<[[u64; 512]; 64]> = v.try_into().unwrap_or_else(|_| {
panic!("Vec to Box conversion failed for bishops.");
});
// Haupt-Back-Schleife
for square_index in 0_usize..=63_usize {
// Verwende die BISHOP-Konstanten aus deiner tables.rs
let premask = PREMASKS_BISHOP[square_index];
let magic = MAGICS_BISHOP[square_index];
let relevant_bits = RELEVANT_BITS_BISHOP[square_index];
let shift = 64 - relevant_bits;
// Schleife durch alle 2^n Blocker-Kombinationen
let mut blocker_combination = 0_u64;
loop {
// 1. Berechne die "echten" Züge für diese Blocker-Kombination (der langsame Weg)
let attack_squares = calculate_bishop_attacks_slowly(square_index, blocker_combination);
// 2. Berechne den "magischen" Speicherort
let magic_index = (blocker_combination.wrapping_mul(magic)) >> shift;
let magic_index_as_usize = magic_index as usize;
// 3. Speichere die echten Züge an diesem magischen Ort
// (Stelle sicher, dass magic_index_as_usize < 512 ist,
// was durch korrekte Magics garantiert wird)
final_bishop_attacks[square_index][magic_index_as_usize] = attack_squares;
// Gehe zur nächsten Blocker-Kombination
if blocker_combination == premask {
break;
}
blocker_combination = blocker_combination.wrapping_sub(premask) & premask;
}
}
final_bishop_attacks
}
pub fn get_bishop_attacks() -> &'static Box<[[u64; 512]; 64]> {
// get_or_init stellt sicher, dass generate_final_bishop_attacks()
// nur beim allerersten Aufruf ausgeführt wird.
// Alle anderen Aufrufe geben sofort die fertige Tabelle zurück.
BISHOP_ATTACKS.get_or_init(generate_final_bishop_attacks)
}

141
ChessEngine/src/square.rs Normal file
View file

@ -0,0 +1,141 @@
use std::ops::{Sub, Add};
use std::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Square {
A1 = 0,
B1 = 1,
C1 = 2,
D1 = 3,
E1 = 4,
F1 = 5,
G1 = 6,
H1 = 7,
A2 = 8,
B2 = 9,
C2 = 10,
D2 = 11,
E2 = 12,
F2 = 13,
G2 = 14,
H2 = 15,
A3 = 16,
B3 = 17,
C3 = 18,
D3 = 19,
E3 = 20,
F3 = 21,
G3 = 22,
H3 = 23,
A4 = 24,
B4 = 25,
C4 = 26,
D4 = 27,
E4 = 28,
F4 = 29,
G4 = 30,
H4 = 31,
A5 = 32,
B5 = 33,
C5 = 34,
D5 = 35,
E5 = 36,
F5 = 37,
G5 = 38,
H5 = 39,
A6 = 40,
B6 = 41,
C6 = 42,
D6 = 43,
E6 = 44,
F6 = 45,
G6 = 46,
H6 = 47,
A7 = 48,
B7 = 49,
C7 = 50,
D7 = 51,
E7 = 52,
F7 = 53,
G7 = 54,
H7 = 55,
A8 = 56,
B8 = 57,
C8 = 58,
D8 = 59,
E8 = 60,
F8 = 61,
G8 = 62,
H8 = 63,
}
pub const SQUARES: [Square; 64] = [
Square::A1, Square::B1, Square::C1, Square::D1, Square::E1, Square::F1, Square::G1, Square::H1,
Square::A2, Square::B2, Square::C2, Square::D2, Square::E2, Square::F2, Square::G2, Square::H2,
Square::A3, Square::B3, Square::C3, Square::D3, Square::E3, Square::F3, Square::G3, Square::H3,
Square::A4, Square::B4, Square::C4, Square::D4, Square::E4, Square::F4, Square::G4, Square::H4,
Square::A5, Square::B5, Square::C5, Square::D5, Square::E5, Square::F5, Square::G5, Square::H5,
Square::A6, Square::B6, Square::C6, Square::D6, Square::E6, Square::F6, Square::G6, Square::H6,
Square::A7, Square::B7, Square::C7, Square::D7, Square::E7, Square::F7, Square::G7, Square::H7,
Square::A8, Square::B8, Square::C8, Square::D8, Square::E8, Square::F8, Square::G8, Square::H8,
];
impl TryFrom<u8> for Square {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
if value <= 63 {
Ok(unsafe { std::mem::transmute(value) })
} else {
Err("Square index out of bounds")
}
}
}
impl Add<u8> for Square {
type Output = Self;
fn add(self, rhs: u8) -> Self::Output {
let new_val = (self as u8) + rhs;
new_val.try_into().expect("Square addition resulted in an invalid square")
}
}
impl Sub<u8> for Square {
type Output = Self;
fn sub(self, rhs: u8) -> Self::Output {
let new_val = (self as u8).checked_sub(rhs)
.expect("Square subtraction resulted in an underflow");
new_val.try_into().expect("Square subtraction resulted in an invalid square")
}
}
impl PartialOrd for Square {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
fn lt(&self, other: &Self) -> bool {
(*self as u8) < (*other as u8)
}
fn le(&self, other: &Self) -> bool {
(*self as u8) <= (*other as u8)
}
fn gt(&self, other: &Self) -> bool {
(*self as u8) > (*other as u8)
}
fn ge(&self, other: &Self) -> bool {
(*self as u8) >= (*other as u8)
}
}
impl Ord for Square {
fn cmp(&self, other: &Self) -> Ordering {
(*self as u8).cmp(&(*other as u8))
}
}

View file

@ -0,0 +1,85 @@
use chess_engine::board::Board;
use chess_engine::movegen::sliders::generate_bishop_moves;
use chess_engine::r#move::MoveList;
/// Compares two move list strings ignoring the order of moves.
fn assert_moves_equal(actual_str: &str, expected_str: &str) {
let mut actual_moves: Vec<&str> = actual_str.split_whitespace().collect();
let mut expected_moves: Vec<&str> = expected_str.split_whitespace().collect();
actual_moves.sort();
expected_moves.sort();
assert_eq!(actual_moves, expected_moves);
}
#[test]
fn test_bishop_moves_single() {
let fen_standard = "8/8/8/1p1p4/2B5/8/8/8 w - - 0 1";
let board = Board::from_fen(fen_standard);
let mut list = MoveList::new();
generate_bishop_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "c4b5 c4d5 c4b3 c4d3 c4a2 c4e2 c4f1");
}
#[test]
fn test_bishop_moves_empty_board_center() {
// Läufer in der Mitte (e4) auf einem leeren Brett
let fen = "8/8/8/8/4B3/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_bishop_moves(&board, &mut list);
let expected = "e4d3 e4c2 e4b1 e4f5 e4g6 e4h7 e4d5 e4c6 e4b7 e4a8 e4f3 e4g2 e4h1";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_bishop_moves_from_corner() {
// Läufer in der Ecke (a1) auf einem leeren Brett
let fen = "8/8/8/8/8/8/8/B7 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_bishop_moves(&board, &mut list);
let expected = "a1b2 a1c3 a1d4 a1e5 a1f6 a1g7 a1h8";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_bishop_moves_friendly_blockers() {
// Läufer auf c1, blockiert von EIGENEN Bauern auf b2 und d2.
// Darf b2/d2 NICHT schlagen und nicht darüber springen.
let fen = "8/8/8/8/8/8/1P1P4/2B5 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_bishop_moves(&board, &mut list);
// Es sollten KEINE Züge generiert werden.
assert_moves_equal(&list.to_string(), "");
}
#[test]
fn test_bishop_moves_mixed_blockers_and_captures() {
let fen = "8/8/1p3P2/8/3B4/8/1p3P2/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_bishop_moves(&board, &mut list);
let expected = "d4c3 d4e3 d4b2 d4c5 d4b6 d4e5";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_bishop_moves_black_turn() {
// Schwarzer Läufer auf c5, weiße Bauern auf b4 und d4.
let fen = "8/8/8/2b5/1P1P4/8/8/8 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_bishop_moves(&board, &mut list);
// Schlagzüge: b4, d4
// Ruhige Züge: b6, a7, d6, e7, f8
let expected = "c5b4 c5d4 c5b6 c5a7 c5d6 c5e7 c5f8";
assert_moves_equal(&list.to_string(), expected);
}

View file

@ -0,0 +1,95 @@
<<<<<<< HEAD
use chess_engine::board::*;
=======
use chess_engine::board::Board;
>>>>>>> origin/master
#[test]
fn test_fen_roundtrip_standard() {
let fen_standard = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
assert_eq!(Board::from_fen(fen_standard).to_fen(), fen_standard);
}
#[test]
fn test_fen_roundtrip_kiwipete() {
let fen_kiwipete = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
assert_eq!(Board::from_fen(fen_kiwipete).to_fen(), fen_kiwipete);
}
#[test]
fn test_fen_roundtrip_en_passant() {
let fen_en_passant = "rnbqkbnr/pppppp1p/8/8/p7/4P3/PPPP1PPP/RNBQKBNR w KQkq e3 0 1";
assert_eq!(Board::from_fen(fen_en_passant).to_fen(), fen_en_passant);
}
#[test]
fn test_fen_roundtrip_castle() {
let fen_castle = "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2QK2R b - - 0 1";
assert_eq!(Board::from_fen(fen_castle).to_fen(), fen_castle);
}
#[test]
fn test_fen_roundtrip_just_kings() {
let fen_just_kings = "8/k7/8/8/8/8/7K/8 w - - 0 1";
assert_eq!(Board::from_fen(fen_just_kings).to_fen(), fen_just_kings);
}
#[test]
fn test_fen_roundtrip_high_move_values() {
let fen_high_move_values = "8/P1k5/K7/8/8/8/8/8 w - - 0 78";
assert_eq!(Board::from_fen(fen_high_move_values).to_fen(), fen_high_move_values);
}
#[test]
fn test_fen_roundtrip_empty_count1() {
let fen_empty_count1 = "1n6/8/8/8/8/8/8/8 w - - 0 1";
assert_eq!(Board::from_fen(fen_empty_count1).to_fen(), fen_empty_count1);
}
#[test]
fn test_fen_roundtrip_empty_count2() {
let fen_empty_count2 = "6n1/8/8/8/8/8/8/8 w - - 0 1";
assert_eq!(Board::from_fen(fen_empty_count2).to_fen(), fen_empty_count2);
}
#[test]
fn test_board_fen_state() {
let fen_standard = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
let board = Board::from_fen(fen_standard);
<<<<<<< HEAD
assert_eq!(board.pieces[PieceType::Pawn as usize][Color::White as usize], 65280);
assert_eq!(board.pieces[PieceType::Pawn as usize][Color::Black as usize], 71776119061217280);
assert_eq!(board.pieces[PieceType::Knight as usize][Color::White as usize], 66);
assert_eq!(board.pieces[PieceType::Knight as usize][Color::Black as usize], 4755801206503243776);
assert_eq!(board.pieces[PieceType::Bishop as usize][Color::White as usize], 36);
assert_eq!(board.pieces[PieceType::Bishop as usize][Color::Black as usize], 2594073385365405696);
assert_eq!(board.pieces[PieceType::Rook as usize][Color::White as usize], 129);
assert_eq!(board.pieces[PieceType::Rook as usize][Color::Black as usize], 9295429630892703744);
assert_eq!(board.pieces[PieceType::Queen as usize][Color::White as usize], 8);
assert_eq!(board.pieces[PieceType::Queen as usize][Color::Black as usize], 576460752303423488);
assert_eq!(board.pieces[PieceType::King as usize][Color::White as usize], 16);
assert_eq!(board.pieces[PieceType::King as usize][Color::Black as usize], 1152921504606846976);
=======
assert_eq!(board.pawns[0], 65280);
assert_eq!(board.pawns[1], 71776119061217280);
assert_eq!(board.knights[0], 66);
assert_eq!(board.knights[1], 4755801206503243776);
assert_eq!(board.bishops[0], 36);
assert_eq!(board.bishops[1], 2594073385365405696);
assert_eq!(board.rooks[0], 129);
assert_eq!(board.rooks[1], 9295429630892703744);
assert_eq!(board.queens[0], 8);
assert_eq!(board.queens[1], 576460752303423488);
assert_eq!(board.kings[0], 16);
assert_eq!(board.kings[1], 1152921504606846976);
>>>>>>> origin/master
assert_eq!(board.occupied[0], 65535);
assert_eq!(board.occupied[1], 18446462598732840960);
assert_eq!(board.all_occupied, 18446462598732906495);
assert_eq!(board.castling_rights, 15);
assert_eq!(board.en_passant_target, None);
assert_eq!(board.halfmove_clock, 0);
assert_eq!(board.fullmove_number, 1);
}

View file

@ -0,0 +1,145 @@
use chess_engine::board::Board;
use chess_engine::movegen::non_sliders::generate_king_moves;
use chess_engine::r#move::MoveList;
/// Compares two move list strings ignoring the order of moves.
fn assert_moves_equal(actual_str: &str, expected_str: &str) {
let mut actual_moves: Vec<&str> = actual_str.split_whitespace().collect();
let mut expected_moves: Vec<&str> = expected_str.split_whitespace().collect();
actual_moves.sort();
expected_moves.sort();
assert_eq!(actual_moves, expected_moves);
}
#[test]
fn test_king_moves_start_pos_blocked() {
let fen_standard = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
let board = Board::from_fen(fen_standard);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// King is completely blocked in the start position
assert_moves_equal(&list.to_string(), "");
}
#[test]
fn test_king_moves_center() {
let fen = "8/8/8/8/4K3/8/8/8 w - - 0 1"; // King on e4
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// 8 moves from e4
assert_moves_equal(&list.to_string(), "e4d3 e4d4 e4d5 e4e3 e4e5 e4f3 e4f4 e4f5");
}
#[test]
fn test_king_moves_corner() {
let fen = "K7/8/8/8/8/8/8/8 w - - 0 1"; // King on a8
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// 3 moves from a8
assert_moves_equal(&list.to_string(), "a8a7 a8b7 a8b8");
}
#[test]
fn test_king_moves_blocked_friendly() {
// King on d4, surrounded by friendly pawns
let fen = "8/8/8/3P1P2/3K4/3P1P2/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// d3, d5, f3, f5 are blocked.
// c3, c4, c5, e3, e4, e5 are free.
assert_moves_equal(&list.to_string(), "d4c3 d4c4 d4c5 d4e3 d4e4 d4e5");
}
#[test]
fn test_king_moves_capture_and_blocked() {
// King on d4
// Friendly: c3, e5
// Enemy: c5, e3
let fen = "8/8/8/2p1P3/3K4/2P1p3/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// Blocked: c3, e5
// Captures: c5, e3
// Empty: c4, d3, d5, e4, f3, f4, f5
// Note: f3, f4, f5 are valid moves
assert_moves_equal(&list.to_string(), "d4c4 d4d3 d4d5 d4e4 d4c5 d4e3");
}
#[test]
fn test_king_moves_black() {
let fen = "8/8/8/8/4k3/8/8/8 b - - 0 1"; // Black king on e4
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// 8 moves from e4
assert_moves_equal(&list.to_string(), "e4d3 e4d4 e4d5 e4e3 e4e5 e4f3 e4f4 e4f5");
}
#[test]
fn test_king_moves_castling_white_all() {
// King on e1, rooks on a1, h1. All rights. No pieces between.
let fen = "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// 5 standard moves + 2 castling moves (e1g1, e1c1)
assert_moves_equal(&list.to_string(), "e1d1 e1d2 e1e2 e1f1 e1f2 O-O O-O-O");
}
#[test]
fn test_king_moves_castling_black_all() {
// King on e8, rooks on a8, h8. All rights. No pieces between.
let fen = "r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// 5 standard moves + 2 castling moves (e8g8, e8c8)
assert_moves_equal(&list.to_string(), "O-O O-O-O e8d8 e8e7 e8f8 e8d7 e8f7");
}
#[test]
fn test_king_moves_castling_blocked_pieces_white() {
// White: Queenside blocked by knight
let fen = "r3k2r/8/8/8/8/8/8/RN2K2R w KQkq - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// Should only get Kingside castling (e1g1) and standard moves
assert_moves_equal(&list.to_string(), "e1d1 e1d2 e1e2 e1f1 e1f2 O-O");
}
#[test]
fn test_king_moves_castling_blocked_pieces_black() {
// Black: Kingside blocked by bishop
let fen = "r3kb1r/8/8/8/8/8/8/R3K2R b KQkq - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "e8d8 e8d7 e8e7 e8f7 O-O-O");
}
#[test]
fn test_king_moves_castling_no_rights() {
// Same as `test_king_moves_castling_white_all` but no castling rights
let fen = "r3k2r/8/8/8/8/8/8/R3K2R w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
// 5 standard moves, 0 castling
assert_moves_equal(&list.to_string(), "e1d1 e1d2 e1e2 e1f1 e1f2");
}
#[test]
fn test_king_moves_empty_board() {
let fen = "8/8/8/8/8/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_king_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "");
}

View file

@ -0,0 +1,96 @@
use chess_engine::board::Board;
use chess_engine::movegen::non_sliders::generate_knight_moves;
use chess_engine::r#move::MoveList;
/// Compares two move list strings ignoring the order of moves.
fn assert_moves_equal(actual_str: &str, expected_str: &str) {
let mut actual_moves: Vec<&str> = actual_str.split_whitespace().collect();
let mut expected_moves: Vec<&str> = expected_str.split_whitespace().collect();
actual_moves.sort();
expected_moves.sort();
assert_eq!(actual_moves, expected_moves);
}
#[test]
fn test_knight_move_generation() {
let fen_standard = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
let board = Board::from_fen(fen_standard);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "b1a3 b1c3 g1f3 g1h3");
}
#[test]
fn test_knight_move_generation_black() {
let fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "b8a6 b8c6 g8f6 g8h6");
}
#[test]
fn test_knight_moves_center() {
let fen = "8/8/8/3N4/8/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
// 8 moves from d5
assert_moves_equal(&list.to_string(), "d5b4 d5b6 d5c3 d5c7 d5e3 d5e7 d5f4 d5f6");
}
#[test]
fn test_knight_moves_capture() {
// Black knight on e5, white pawn on d3
let fen = "8/8/8/4n3/8/3P4/8/8 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
// 8 moves, including capture on d3
assert_moves_equal(&list.to_string(), "e5c4 e5c6 e5d3 e5d7 e5f3 e5f7 e5g4 e5g6");
}
#[test]
fn test_knight_moves_blocked_friendly() {
// Knight on d4, some moves are blocked, some are not.
let fen = "8/8/3P1P2/3P1P2/3N4/3P1P2/3P1P2/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
// f3, f5, d3, d5, d6, f6 are blocked.
// c2, e2, b3, b5, c6, e6 are free.
assert_moves_equal(&list.to_string(), "d4c2 d4e2 d4b3 d4b5 d4c6 d4e6");
}
#[test]
fn test_knight_moves_corner() {
let fen = "N7/8/8/8/8/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
// 2 moves from a8
assert_moves_equal(&list.to_string(), "a8b6 a8c7");
}
#[test]
fn test_knight_moves_capture_and_blocked() {
// White knights on b1 and g1.
// b1: a3 (friendly), c3 (friendly), d2 (enemy)
// g1: f3 (empty), h3 (empty), e2 (empty)
let fen = "8/8/8/8/8/P1P5/3p4/1N4NR w K - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "b1d2 g1e2 g1f3 g1h3");
}
#[test]
fn test_knight_moves_empty_board() {
let fen = "8/8/8/8/8/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_knight_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "");
}

View file

@ -0,0 +1,112 @@
use chess_engine::r#move::*;
use chess_engine::square::Square;
#[test]
fn test_quiet_move_white_pawn() {
// Test 1: Standard Quiet Move (White Pawn)
// (from: E2, to: E4)
// NOTE: This was MOVE_TYPE_FLAG_QUIET, but in the new system it's a specific flag.
// The algebraic notation is the same, so we test with the new specific flag.
<<<<<<< HEAD
let m_quiet = Move::new(Square::E2, Square::E4, MOVE_FLAG_QUIET);
=======
let m_quiet = Move::new(Square::E2, Square::E4, MOVE_FLAG_DOUBLE_PAWN);
>>>>>>> origin/master
assert_eq!(m_quiet.to_algebraic(), "e2e4");
}
#[test]
fn test_quiet_move_black_knight() {
// Test 2: Standard Quiet Move (Black Knight)
// (from: B8, to: C6)
let m_knight = Move::new(Square::B8, Square::C6, MOVE_FLAG_QUIET);
assert_eq!(m_knight.to_algebraic(), "b8c6");
}
#[test]
fn test_en_passant_move() {
// Test 3: En Passant Move (Notation is same as quiet move)
// (from: E5, to: F6)
let m_ep = Move::new(Square::E5, Square::F6, MOVE_FLAG_EN_PASSANT);
assert_eq!(m_ep.to_algebraic(), "e5f6");
}
#[test]
fn test_promotion_to_queen() {
// Test 4: Promotion to Queen (Push)
// (from: E7, to: E8)
let m_promo_q = Move::new(Square::E7, Square::E8, MOVE_FLAG_PROMO_Q);
assert_eq!(m_promo_q.to_algebraic(), "e7e8q");
}
#[test]
fn test_promotion_to_rook() {
// Test 5: Promotion to Rook (Push)
// (from: A7, to: A8)
let m_promo_r = Move::new(Square::A7, Square::A8, MOVE_FLAG_PROMO_R);
assert_eq!(m_promo_r.to_algebraic(), "a7a8r");
}
#[test]
fn test_promotion_to_bishop() {
// Test 6: Promotion to Bishop (Capture)
// (from: G2, to: H1)
let m_promo_b = Move::new(Square::G2, Square::H1, MOVE_FLAG_PROMO_CAP_B);
assert_eq!(m_promo_b.to_algebraic(), "g2h1b");
}
#[test]
fn test_promotion_to_knight() {
// Test 7: Promotion to Knight (Capture)
// (from: G7, to: F8)
let m_promo_n = Move::new(Square::G7, Square::F8, MOVE_FLAG_PROMO_CAP_N);
assert_eq!(m_promo_n.to_algebraic(), "g7f8n");
}
#[test]
fn test_white_kingside_castling() {
// Test 8: White Kingside Castling
// (from: E1, to: G1)
<<<<<<< HEAD
let m_castle_wk = Move::new(Square::E1, Square::G1, MOVE_FLAG_WK_CASTLE);
=======
let m_castle_wk = Move::new(Square::E1, Square::G1, MOVE_FLAG_KING_CASTLE);
>>>>>>> origin/master
assert_eq!(m_castle_wk.to_algebraic(), "O-O");
}
#[test]
fn test_white_queenside_castling() {
// Test 9: White Queenside Castling
// (from: E1, to: C1)
<<<<<<< HEAD
let m_castle_wq = Move::new(Square::E1, Square::C1, MOVE_FLAG_WQ_CASTLE);
=======
let m_castle_wq = Move::new(Square::E1, Square::C1, MOVE_FLAG_QUEEN_CASTLE);
>>>>>>> origin/master
assert_eq!(m_castle_wq.to_algebraic(), "O-O-O");
}
#[test]
fn test_black_kingside_castling() {
// Test 10: Black Kingside Castling
// (from: E8, to: G8)
<<<<<<< HEAD
let m_castle_bk = Move::new(Square::E8, Square::G8, MOVE_FLAG_BK_CASTLE);
=======
let m_castle_bk = Move::new(Square::E8, Square::G8, MOVE_FLAG_KING_CASTLE);
>>>>>>> origin/master
assert_eq!(m_castle_bk.to_algebraic(), "O-O");
}
#[test]
fn test_black_queenside_castling() {
// Test 11: Black Queenside Castling
// (from: E8, to: C8)
<<<<<<< HEAD
let m_castle_bq = Move::new(Square::E8, Square::C8, MOVE_FLAG_BQ_CASTLE);
=======
let m_castle_bq = Move::new(Square::E8, Square::C8, MOVE_FLAG_QUEEN_CASTLE);
>>>>>>> origin/master
assert_eq!(m_castle_bq.to_algebraic(), "O-O-O");
}

View file

@ -0,0 +1,142 @@
use chess_engine::board::Board;
use chess_engine::movegen::pawns::generate_pawn_moves;
use chess_engine::r#move::MoveList;
/// Compares two move list strings ignoring the order of moves.
fn assert_moves_equal(actual_str: &str, expected_str: &str) {
let mut actual_moves: Vec<&str> = actual_str.split_whitespace().collect();
let mut expected_moves: Vec<&str> = expected_str.split_whitespace().collect();
actual_moves.sort();
expected_moves.sort();
assert_eq!(actual_moves, expected_moves);
}
#[test]
fn test_pawn_moves_start_pos() {
let fen_standard = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
let board = Board::from_fen(fen_standard);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "a2a3 a2a4 b2b3 b2b4 c2c3 c2c4 d2d3 d2d4 e2e3 e2e4 f2f3 f2f4 g2g3 g2g4 h2h3 h2h4");
}
#[test]
fn test_pawn_moves_black_start_pos() {
let fen_standard = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1";
let board = Board::from_fen(fen_standard);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "a7a6 a7a5 b7b6 b7b5 c7c6 c7c5 d7d6 d7d5 e7e6 e7e5 f7f6 f7f5 g7g6 g7g5 h7h6 h7h5");
}
#[test]
fn test_pawn_moves_black_blocked_and_captures() {
// Black pawns on a7, c7, e7, g7. White pawns on b6, d6, f6.
let fen = "8/p1p1p1p1/1P1P1P2/8/8/8/8/8 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "a7a6 a7a5 a7b6 c7c6 c7c5 c7b6 c7d6 e7e6 e7e5 e7d6 e7f6 g7g6 g7g5 g7f6");
}
#[test]
fn test_pawn_moves_black_side_captures() {
// Test captures on A and H files (no wrap-around)
let fen = "8/8/8/8/8/1p4p1/P6P/8 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "b3b2 b3a2 g3g2 g3h2");
}
#[test]
fn test_pawn_moves_white_side_captures() {
// Test captures on A and H files (no wrap-around)
let fen = "8/p6p/1P4P1/8/8/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "b6b7 b6a7 g6g7 g6h7");
}
#[test]
fn test_pawn_moves_black_en_passant() {
// White just moved e2e4, en passant target is e3. Black pawn on d4.
let fen = "rnbqkbnr/ppp1pppp/8/8/3pP3/8/PPP2PPP/RNBQKBNR b KQkq e3 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "a7a6 a7a5 b7b6 b7b5 c7c6 c7c5 d4d3 d4e3 e7e6 e7e5 f7f6 f7f5 g7g6 g7g5 h7h6 h7h5");
}
#[test]
fn test_pawn_moves_white_en_passant() {
// Black just moved d7d5, en passant target is d6. White pawn on e5.
let fen = "rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "a2a3 a2a4 b2b3 b2b4 c2c3 c2c4 d2d3 d2d4 e5e6 e5d6 f2f3 f2f4 g2g3 g2g4 h2h3 h2h4");
}
#[test]
fn test_pawn_moves_black_promotion() {
let fen = "8/8/8/8/8/8/p1p1p1p1/8 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(),
"a2a1q a2a1r a2a1b a2a1n c2c1q c2c1r c2c1b c2c1n e2e1q e2e1r e2e1b e2e1n g2g1q g2g1r g2g1b g2g1n");
}
#[test]
fn test_pawn_moves_white_promotion() {
let fen = "8/P1P1P1P1/8/8/8/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(),
"a7a8q a7a8r a7a8b a7a8n c7c8q c7c8r c7c8b c7c8n e7e8q e7e8r e7e8b e7e8n g7g8q g7g8r g7g8b g7g8n");
}
#[test]
fn test_pawn_moves_black_promotion_capture() {
// Black pawn on b2. White rooks on a1 and c1.
let fen = "8/8/8/8/8/8/1p6/R1R5 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(),
"b2b1q b2b1r b2b1b b2b1n b2a1q b2a1r b2a1b b2a1n b2c1q b2c1r b2c1b b2c1n");
}
#[test]
fn test_pawn_moves_white_promotion_capture() {
// White pawn on a7. Black rooks on b8 and d8.
let fen = "1r1r4/P7/8/8/8/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(),
"a7a8q a7a8r a7a8b a7a8n a7b8q a7b8r a7b8b a7b8n");
}
#[test]
fn test_pawn_moves_black_midgame_random() {
let fen = "r1bqkb1r/1p2pppp/p1n2n2/3p4/2BNP3/2N5/PPP2PPP/R1BQK2R b KQkq - 1 6";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "a6a5 b7b6 b7b5 d5c4 d5e4 e7e6 e7e5 g7g6 g7g5 h7h6 h7h5");
}
#[test]
fn test_pawn_moves_white_midgame_random() {
let fen = "r1bqk2r/2ppbppp/p1n2n2/1p2p3/B1P1P3/5N2/PP1P1PPP/RNBQR1K1 w kq - 0 7";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_pawn_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "a2a3 b2b3 b2b4 c4c5 c4b5 d2d3 d2d4 g2g3 g2g4 h2h3 h2h4");
}

View file

@ -0,0 +1,12 @@
use chess_engine::board::Board;
use chess_engine::movegen::generate_pseudo_legal_moves;
use chess_engine::r#move::MoveList;
// "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 7 3195901860 "false"
// "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 "false"
// "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" 7 178633661 "false"
// "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" 6 706045033 "false"
// "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" 5 89941194 "false"
// "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10" 5 164075551 "false"
// "r7/4p3/5p1q/3P4/4pQ2/4pP2/6pp/R3K1kr w Q - 1 3" 5 11609488 "false"

View file

@ -0,0 +1,93 @@
use chess_engine::board::Board;
use chess_engine::movegen::sliders::generate_queen_moves;
use chess_engine::r#move::MoveList;
/// Compares two move list strings ignoring the order of moves.
fn assert_moves_equal(actual_str: &str, expected_str: &str) {
let mut actual_moves: Vec<&str> = actual_str.split_whitespace().collect();
let mut expected_moves: Vec<&str> = expected_str.split_whitespace().collect();
actual_moves.sort();
expected_moves.sort();
assert_eq!(actual_moves, expected_moves);
}
#[test]
fn test_queen_moves_single_rook() {
let fen_standard = "8/1Q2p3/8/8/8/8/6p1/8 w - - 0 1";
let board = Board::from_fen(fen_standard);
let mut list = MoveList::new();
generate_queen_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "b7a8 b7a7 b7a6 b7b8 b7b6 b7b5 b7b4 b7b3 b7b2 b7b1 b7c8 b7c7 b7c6 b7d7 b7e7 b7d5 b7e4 b7f3 b7g2");
}
#[test]
fn test_queen_center_open() {
// Queen in the center, open board, should generate 27 moves
let fen = "4k3/8/8/8/3Q4/8/8/4K3 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_queen_moves(&board, &mut list);
let expected = "d4a1 d4b2 d4c3 d4e5 d4f6 d4g7 d4h8 \
d4a4 d4b4 d4c4 d4e4 d4f4 d4g4 d4h4 \
d4d1 d4d2 d4d3 d4d5 d4d6 d4d7 d4d8 \
d4a7 d4b6 d4c5 d4e3 d4f2 d4g1";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_queen_corner_blocked_friendly() {
// Queen in corner, completely blocked by friendly pieces
let fen = "rnbqkbnr/pppppppp/8/8/8/8/PP1PP1PP/QNBQKBNR w Kkq - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_queen_moves(&board, &mut list);
assert_moves_equal(&list.to_string(), "d1a4 d1b3 d1c2");
}
#[test]
fn test_queen_multiple_captures_black() {
// Black queen on h8, with multiple white pieces to capture
let fen = "q3k3/P1P1P1P1/8/8/8/P1P1P1P1/8/4K3 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_queen_moves(&board, &mut list);
let expected = "a8b8 a8c8 a8d8 a8a7 a8b7 a8c6 a8d5 a8e4 a8f3 a8g2 a8h1";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_multiple_queens() {
let fen = "4k3/8/8/8/8/8/8/Q3K2Q w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_queen_moves(&board, &mut list);
let expected = "a1a2 a1a3 a1a4 a1a5 a1a6 a1a7 a1a8 a1b1 a1c1 a1d1 a1b2 a1c3 a1d4 a1e5 a1f6 a1g7 a1h8 \
h1h2 h1h3 h1h4 h1h5 h1h6 h1h7 h1h8 h1g1 h1f1 h1g2 h1f3 h1e4 h1d5 h1c6 h1b7 h1a8";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_queen_rook_only() {
// Queen on d4, bishop moves blocked by friendly pawns
let fen = "4k3/8/8/2P1P3/3Q4/2P1P3/8/4K3 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_queen_moves(&board, &mut list);
let expected = "d4a4 d4b4 d4c4 d4e4 d4f4 d4g4 d4h4 \
d4d1 d4d2 d4d3 d4d5 d4d6 d4d7 d4d8";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_queen_bishop_only() {
// Queen on d4, rook moves blocked by friendly pawns
let fen = "4k3/8/8/3P4/2PQP3/3P4/8/4K3 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_queen_moves(&board, &mut list);
let expected = "d4a1 d4b2 d4c3 d4e5 d4f6 d4g7 d4h8 \
d4a7 d4b6 d4c5 d4e3 d4f2 d4g1";
assert_moves_equal(&list.to_string(), expected);
}

View file

@ -0,0 +1,111 @@
use chess_engine::board::Board;
use chess_engine::movegen::sliders::generate_rook_moves;
use chess_engine::r#move::MoveList;
/// Compares two move list strings ignoring the order of moves.
fn assert_moves_equal(actual_str: &str, expected_str: &str) {
let mut actual_moves: Vec<&str> = actual_str.split_whitespace().collect();
let mut expected_moves: Vec<&str> = expected_str.split_whitespace().collect();
actual_moves.sort();
expected_moves.sort();
assert_eq!(actual_moves, expected_moves);
}
#[test]
<<<<<<< HEAD
fn test_rook_moves_single_rook() {
=======
fn test_rook_moves_1() {
>>>>>>> origin/master
let fen_standard = "8/8/8/2b5/2Rb4/2b5/8/8 w - - 0 1";
let board = Board::from_fen(fen_standard);
let mut list = MoveList::new();
generate_rook_moves(&board, &mut list);
<<<<<<< HEAD
// This FEN has a White Rook at c4, and Black Bishops at c5, d4, and c3.
// It should be able to capture all three and move left to a4/b4.
assert_moves_equal(&list.to_string(), "c4a4 c4b4 c4c3 c4c5 c4d4");
}
#[test]
fn test_rook_moves_empty_board() {
let fen = "8/8/8/8/3R4/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_rook_moves(&board, &mut list);
let expected = "d4d1 d4d2 d4d3 d4d5 d4d6 d4d7 d4d8 d4a4 d4b4 d4c4 d4e4 d4f4 d4g4 d4h4";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_rook_moves_corner_blocked_black() {
let fen = "r6k/1p6/8/8/8/8/8/K7 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_rook_moves(&board, &mut list);
// Black rook at a8. Friendly king at h8, friendly pawn at b7.
// Rook can move down the a-file and right along the 8th rank, stopping before h8.
let expected = "a8a7 a8a6 a8a5 a8a4 a8a3 a8a2 a8a1 a8b8 a8c8 a8d8 a8e8 a8f8 a8g8";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_rook_moves_double_rooks_friendly_block() {
let fen = "8/8/8/8/R3R3/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_rook_moves(&board, &mut list);
// Rooks at a4 and e4. They block each other horizontally.
// Rook a4 moves a1-a8 and b4, c4, d4.
// Rook e4 moves e1-e8, f4, g4, h4 AND d4, c4, b4.
let expected = "a4a1 a4a2 a4a3 a4a5 a4a6 a4a7 a4a8 a4b4 a4c4 a4d4 \
e4e1 e4e2 e4e3 e4e5 e4e6 e4e7 e4e8 e4f4 e4g4 e4h4 \
e4d4 e4c4 e4b4";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_rook_moves_capture_stops_movegen() {
let fen = "r7/P7/8/8/8/8/8/8 b - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_rook_moves(&board, &mut list);
// Black rook at a8, White pawn at a7.
// The rook can capture at a7, but cannot move past it.
// It can still move horizontally.
let expected = "a8a7 a8b8 a8c8 a8d8 a8e8 a8f8 a8g8 a8h8";
assert_moves_equal(&list.to_string(), expected);
}
#[test]
fn test_rook_moves_completely_blocked_friendly() {
let fen = "8/8/8/1P6/PRP5/1P6/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_rook_moves(&board, &mut list);
// White rook at b4.
// Blocked by P(b5), P(b3), P(a4), P(c4).
// Should have 0 moves.
assert_moves_equal(&list.to_string(), "");
}
#[test]
fn test_rook_moves_ignores_absolute_pin() {
let fen = "r3k3/8/8/8/R3K3/8/8/8 w - - 0 1";
let board = Board::from_fen(fen);
let mut list = MoveList::new();
generate_rook_moves(&board, &mut list);
// White rook at a4 is absolutely pinned to King at e4 by Black rook at a8.
// A pseudo-legal generator should *ignore* the pin.
// It should generate vertical moves (including capture at a8)
// and horizontal moves (stopping before the friendly King at e4).
let expected = "a4a1 a4a2 a4a3 a4a5 a4a6 a4a7 a4a8 a4b4 a4c4 a4d4";
assert_moves_equal(&list.to_string(), expected);
}
=======
// King is completely blocked in the start position
assert_moves_equal(&list.to_string(), "");
}
>>>>>>> origin/master

View file

@ -319,9 +319,9 @@ impl Board {
let flag = mv.value() & MOVE_FLAG_MASK;
// promo must come first because of double usage of the flag bits
if flag == MOVE_FLAG_NO_PROMO {
if flag & MOVE_FLAG_CAPTURE > 0 {
} else { //
} else { // No capture
}
}

View file

@ -10,33 +10,33 @@ pub const MOVE_FROM_MASK: u16 = 0b0000_0000_0011_1111;
pub const MOVE_TO_MASK: u16 = 0b0000_1111_1100_0000;
// BIT 12 - 15: FLAGS (4 bits)
// 1. 0 no capture, 1 capture
pub const MOVE_FLAG_MASK: u16 = 0b1111_0000_0000_0000;
pub const MOVE_FLAG_QUIET: u16 = 0b0000_0000_0000_0000;
pub const MOVE_FLAG_CAPTURE: u16 = 0b0001_0000_0000_0000;
pub const MOVE_FLAG_EN_PASSANT: u16 = 0b0010_0000_0000_0000;
pub const MOVE_FLAG_EN_PASSANT: u16 = 0b0011_0000_0000_0000;
pub const MOVE_FLAG_WK_CASTLE: u16 = 0b0011_0000_0000_0000;
pub const MOVE_FLAG_WQ_CASTLE: u16 = 0b0100_0000_0000_0000;
pub const MOVE_FLAG_BK_CASTLE: u16 = 0b0101_0000_0000_0000;
pub const MOVE_FLAG_BQ_CASTLE: u16 = 0b0110_0000_0000_0000;
// 0111 is free
// Castle flags
pub const MOVE_MASK_CASTLE: u16 = 0b1100_0000_0000_0000;
pub const MOVE_FLAG_CASTLE_TRUE: u16 = 0b0100_0000_0000_0000;
pub const MOVE_FLAG_WK_CASTLE: u16 = 0b0100_0000_0000_0000;
pub const MOVE_FLAG_WQ_CASTLE: u16 = 0b0101_0000_0000_0000;
pub const MOVE_FLAG_BK_CASTLE: u16 = 0b0110_0000_0000_0000;
pub const MOVE_FLAG_BQ_CASTLE: u16 = 0b0111_0000_0000_0000;
// Promotion flags (use the 1xxx bits)
// We combine capture flag with promotion type
pub const MOVE_FLAG_PROMO: u16 = 0b1000_0000_0000_0000;
pub const MOVE_FLAG_NO_PROMO: u16 = 0b0000_0000_0000_0000;
pub const MOVE_FLAG_PROMO_N: u16 = 0b1000_0000_0000_0000;
pub const MOVE_FLAG_PROMO_B: u16 = 0b1001_0000_0000_0000;
pub const MOVE_FLAG_PROMO_R: u16 = 0b1010_0000_0000_0000;
pub const MOVE_FLAG_PROMO_Q: u16 = 0b1011_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_N: u16 = 0b1100_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_B: u16 = 0b1101_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_R: u16 = 0b1110_0000_0000_0000;
pub const MOVE_FLAG_PROMO_CAP_Q: u16 = 0b1111_0000_0000_0000;
pub const MOVE_FLAG_PROMO_B: u16 = 0b1010_0000_0000_0000;
pub const MOVE_FLAG_PROMO_R: u16 = 0b1100_0000_0000_0000;
pub const MOVE_FLAG_PROMO_Q: u16 = 0b1110_0000_0000_0000;
pub const MOVE_FLAG_PROMO_N_CAP: u16 = 0b1001_0000_0000_0000;
pub const MOVE_FLAG_PROMO_B_CAP: u16 = 0b1011_0000_0000_0000;
pub const MOVE_FLAG_PROMO_R_CAP: u16 = 0b1101_0000_0000_0000;
pub const MOVE_FLAG_PROMO_Q_CAP: u16 = 0b1111_0000_0000_0000;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Move(u16);
@ -88,10 +88,10 @@ impl Move {
// Check if it's any promotion type (1xxx)
if (flags & 0b1000_0000_0000_0000) != 0 {
let promo_char = match flags {
MOVE_FLAG_PROMO_N | MOVE_FLAG_PROMO_CAP_N => 'n',
MOVE_FLAG_PROMO_B | MOVE_FLAG_PROMO_CAP_B => 'b',
MOVE_FLAG_PROMO_R | MOVE_FLAG_PROMO_CAP_R => 'r',
MOVE_FLAG_PROMO_Q | MOVE_FLAG_PROMO_CAP_Q => 'q',
MOVE_FLAG_PROMO_N | MOVE_FLAG_PROMO_N_CAP => 'n',
MOVE_FLAG_PROMO_B | MOVE_FLAG_PROMO_B_CAP => 'b',
MOVE_FLAG_PROMO_R | MOVE_FLAG_PROMO_R_CAP => 'r',
MOVE_FLAG_PROMO_Q | MOVE_FLAG_PROMO_Q_CAP => 'q',
_ => '?', // Should not happen
};
format!("{}{}{}", from_str, to_str, promo_char)

View file

@ -98,10 +98,10 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) {
while promotion_targets_a_side_capture > 0 {
let to = SQUARES[promotion_targets_a_side_capture.trailing_zeros() as usize];
let from = to - 7;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_N_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_B_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_R_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_Q_CAP));
promotion_targets_a_side_capture &= promotion_targets_a_side_capture - 1;
}
@ -111,10 +111,10 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) {
while promotion_targets_h_side_capture > 0 {
let to = SQUARES[promotion_targets_h_side_capture.trailing_zeros() as usize];
let from = to - 9;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_N_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_B_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_R_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_Q_CAP));
promotion_targets_h_side_capture &= promotion_targets_h_side_capture - 1;
}
@ -207,10 +207,10 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) {
while promotion_targets_a_side_capture > 0 {
let to = SQUARES[promotion_targets_a_side_capture.trailing_zeros() as usize];
let from = to + 9;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_N_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_B_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_R_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_Q_CAP));
promotion_targets_a_side_capture &= promotion_targets_a_side_capture - 1;
}
@ -219,10 +219,10 @@ pub fn generate_pawn_moves(board: &Board, list: &mut MoveList) {
while promotion_targets_h_side_capture > 0 {
let to = SQUARES[promotion_targets_h_side_capture.trailing_zeros() as usize];
let from = to + 7;
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_Q));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_R));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_B));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_CAP_N));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_N_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_B_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_R_CAP));
list.push(Move::new(from, to, MOVE_FLAG_PROMO_Q_CAP));
promotion_targets_h_side_capture &= promotion_targets_h_side_capture - 1;
}

View file

@ -47,7 +47,7 @@ fn test_promotion_to_rook() {
fn test_promotion_to_bishop() {
// Test 6: Promotion to Bishop (Capture)
// (from: G2, to: H1)
let m_promo_b = Move::new(Square::G2, Square::H1, MOVE_FLAG_PROMO_CAP_B);
let m_promo_b = Move::new(Square::G2, Square::H1, MOVE_FLAG_PROMO_B_CAP);
assert_eq!(m_promo_b.to_algebraic(), "g2h1b");
}
@ -55,7 +55,7 @@ fn test_promotion_to_bishop() {
fn test_promotion_to_knight() {
// Test 7: Promotion to Knight (Capture)
// (from: G7, to: F8)
let m_promo_n = Move::new(Square::G7, Square::F8, MOVE_FLAG_PROMO_CAP_N);
let m_promo_n = Move::new(Square::G7, Square::F8, MOVE_FLAG_PROMO_N_CAP);
assert_eq!(m_promo_n.to_algebraic(), "g7f8n");
}