Skip to content

Commit

Permalink
Pawn, knight and king attack tables (#5)
Browse files Browse the repository at this point in the history
* Add pawn, knight and king attack tables
* Bump version to 0.9
  • Loading branch information
oriolarcas authored Dec 28, 2023
1 parent e3485dd commit c7c9d1f
Show file tree
Hide file tree
Showing 12 changed files with 687 additions and 57 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "chusst",
"version": "0.8.0",
"version": "0.9.0",
"private": true,
"dependencies": {
"@tauri-apps/api": "^1.4.0",
Expand Down
4 changes: 2 additions & 2 deletions between.py → scripts/in_between.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
print("const IN_BETWEEN_TABLE: [[u64; 64]; 64] = [")

def index_to_rank_and_file(index):
rank = index // 8
file = index % 8
return rank, file

print("const IN_BETWEEN_TABLE: [[u64; 64]; 64] = [")

for source_index in range(64):
print(" [")
for target_index in range(64):
Expand Down
55 changes: 55 additions & 0 deletions scripts/king.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
def index_to_rank_and_file(index):
rank = index // 8
file = index % 8
return rank, file

def rank_and_file_to_index(rank, file):
return rank * 8 + file

def bitboard_from_bit(index):
return 1 << index

def is_valid_position(rank, file):
return rank >= 0 and rank < 8 and file >= 0 and file < 8

def format_bitboard(bitboard):
print(" abcdefgh")
for rank_number, rank_byte in reversed(list(enumerate(bitboard.to_bytes(8, 'little')))):
print(f"{rank_number + 1} {rank_byte:08b}")

print("pub const KING_ATTACK_TABLE: [u64; 64] = [")

for source_index in range(64):
source_rank, source_file = index_to_rank_and_file(source_index)

moves = [
(-1, -1),
(-1, 0),
(-1, 1),
(0, -1),
(0, 1),
(1, -1),
(1, 0),
(1, 1),
]

bitboard = 0

for move_ranks, move_files in moves:
target_rank = source_rank + move_ranks
target_file = source_file + move_files

if not is_valid_position(target_rank, target_file):
continue

target_index = rank_and_file_to_index(target_rank, target_file)
bitboard |= bitboard_from_bit(target_index)

if bitboard == 0:
print(" 0,")
else:
print(f" 0x{bitboard:016x},")
# print(f"{format_bitboard(bitboard | bitboard_from_bit(source_index))}")


print("];")
55 changes: 55 additions & 0 deletions scripts/knights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
def index_to_rank_and_file(index):
rank = index // 8
file = index % 8
return rank, file

def rank_and_file_to_index(rank, file):
return rank * 8 + file

def bitboard_from_bit(index):
return 1 << index

def is_valid_position(rank, file):
return rank >= 0 and rank < 8 and file >= 0 and file < 8

def format_bitboard(bitboard):
print(" abcdefgh")
for rank_number, rank_byte in reversed(list(enumerate(bitboard.to_bytes(8, 'little')))):
print(f"{rank_number + 1} {rank_byte:08b}")

print("pub const KNIGHT_ATTACK_TABLE: [u64; 64] = [")

for source_index in range(64):
source_rank, source_file = index_to_rank_and_file(source_index)

moves = [
(-1, -2),
(-1, 2),
(-2, -1),
(-2, 1),
(2, -1),
(2, 1),
(1, -2),
(1, 2),
]

bitboard = 0

for move_ranks, move_files in moves:
target_rank = source_rank + move_ranks
target_file = source_file + move_files

if not is_valid_position(target_rank, target_file):
continue

target_index = rank_and_file_to_index(target_rank, target_file)
bitboard |= bitboard_from_bit(target_index)

if bitboard == 0:
print(" 0,")
else:
print(f" 0x{bitboard:016x},")
# print(f"{format_bitboard(bitboard | bitboard_from_bit(source_index))}")


print("];")
112 changes: 112 additions & 0 deletions scripts/pawns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
def index_to_rank_and_file(index):
rank = index // 8
file = index % 8
return rank, file

def rank_and_file_to_index(rank, file):
return rank * 8 + file

def bitboard_from_bit(index):
return 1 << index

# A pawn can never be below its starting rank (1 for white or 8 for black)
# Also when it arrives to the promotion rank, it promotes and cannot be a pawn anymore
# So no possible moves for ranks 1 and 8

print("pub const WHITE_PAWN_ATTACK_TABLE: [u64; 64] = [")

for source_index in range(64):
source_rank, source_file = index_to_rank_and_file(source_index)

# Possible captures, not including en passant

if source_rank == 0 or source_rank == 7:
print(" 0,")
continue

bitboard = 0
if source_file > 0:
target_index = rank_and_file_to_index(source_rank + 1, source_file - 1)
bitboard |= bitboard_from_bit(target_index)

if source_file < 7:
target_index = rank_and_file_to_index(source_rank + 1, source_file + 1)
bitboard |= bitboard_from_bit(target_index)

print(f" 0x{bitboard:016x},")

print("];")

print()

print("pub const BLACK_PAWN_ATTACK_TABLE: [u64; 64] = [")

for source_index in range(64):
source_rank, source_file = index_to_rank_and_file(source_index)

# Possible captures, not including en passant

if source_rank == 0 or source_rank == 7:
print(" 0,")
continue

bitboard = 0
if source_file > 0:
target_index = rank_and_file_to_index(source_rank - 1, source_file - 1)
bitboard |= bitboard_from_bit(target_index)

if source_file < 7:
target_index = rank_and_file_to_index(source_rank - 1, source_file + 1)
bitboard |= bitboard_from_bit(target_index)

print(f" 0x{bitboard:016x},")

print("];")

print("pub const WHITE_ATTACKED_BY_PAWN_TABLE: [u64; 64] = [")

for source_index in range(64):
source_rank, source_file = index_to_rank_and_file(source_index)

# Possible captures, not including en passant

if source_rank == 7:
print(" 0,")
continue

bitboard = 0
if source_file > 0:
target_index = rank_and_file_to_index(source_rank + 1, source_file - 1)
bitboard |= bitboard_from_bit(target_index)

if source_file < 7:
target_index = rank_and_file_to_index(source_rank + 1, source_file + 1)
bitboard |= bitboard_from_bit(target_index)

print(f" 0x{bitboard:016x},")

print("];")

print("pub const BLACK_ATTACKED_BY_PAWN_TABLE: [u64; 64] = [")

for source_index in range(64):
source_rank, source_file = index_to_rank_and_file(source_index)

# Possible captures, not including en passant

if source_rank == 0:
print(" 0,")
continue

bitboard = 0
if source_file > 0:
target_index = rank_and_file_to_index(source_rank - 1, source_file - 1)
bitboard |= bitboard_from_bit(target_index)

if source_file < 7:
target_index = rank_and_file_to_index(source_rank - 1, source_file + 1)
bitboard |= bitboard_from_bit(target_index)

print(f" 0x{bitboard:016x},")

print("];")
4 changes: 2 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "chusst"
version = "0.8.0"
version = "0.9.0"
description = "A simple chess engine in Rust"
authors = ["Oriol Arcas"]
license = ""
Expand All @@ -25,7 +25,7 @@ bencher = "0.1.5"
[features]
# default = ["compact-board"]
# default = ["bitboards"]
# default = ["compact-board", "bitboards"]
default = ["compact-board", "bitboards"]

# Use bitboards to evaluate valid moves
bitboards = []
Expand Down
44 changes: 27 additions & 17 deletions src-tauri/src/eval/bitboards.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod attack;
mod in_between;

use crate::board::{Board, ModifiableBoard, Piece, PieceType, Player, Position, Square};
Expand Down Expand Up @@ -88,27 +89,10 @@ impl PlayerBitboards {
}
}

pub fn in_between(source: &Position, target: &Position) -> Bitboard {
let source_index = position_to_bitboard_index(source);
let target_index = position_to_bitboard_index(target);
in_between::IN_BETWEEN_TABLE[source_index][target_index]
}

pub fn has_position(&self, position: &Position) -> bool {
check_bitboard(self.combined(), position)
}

pub fn has_piece(&self, position: &Position, piece: &PieceType) -> bool {
match piece {
PieceType::Pawn => check_bitboard(self.pawns, position),
PieceType::Knight => check_bitboard(self.knights, position),
PieceType::Bishop => check_bitboard(self.bishops, position),
PieceType::Rook => check_bitboard(self.rooks, position),
PieceType::Queen => check_bitboard(self.queens, position),
PieceType::King => check_bitboard(self.kings, position),
}
}

pub fn into_iter(bitboard: Bitboard) -> BitboardIter {
BitboardIter { bitboard }
}
Expand All @@ -133,6 +117,32 @@ impl PlayerBitboards {
PieceType::King => self.kings,
}
}

// Tables

pub fn in_between(source: &Position, target: &Position) -> Bitboard {
let source_index = position_to_bitboard_index(source);
let target_index = position_to_bitboard_index(target);
in_between::IN_BETWEEN_TABLE[source_index][target_index]
}

pub fn pawn_can_attack(&self, target_position: &Position) -> bool {
let target_index = position_to_bitboard_index(target_position);
match self.player {
Player::White => self.pawns & attack::BLACK_ATTACKED_BY_PAWN_TABLE[target_index] != 0,
Player::Black => self.pawns & attack::WHITE_ATTACKED_BY_PAWN_TABLE[target_index] != 0,
}
}

pub fn knight_can_attack(&self, target_position: &Position) -> bool {
let target_index = position_to_bitboard_index(target_position);
self.knights & attack::ATTACKED_BY_KNIGHT_TABLE[target_index] != 0
}

pub fn king_can_attack(&self, target_position: &Position) -> bool {
let target_index = position_to_bitboard_index(target_position);
self.kings & attack::ATTACKED_BY_KING_TABLE[target_index] != 0
}
}

impl Index<Position> for PlayerBitboards {
Expand Down
Loading

0 comments on commit c7c9d1f

Please sign in to comment.