Compare commits
64 Commits
Engine/bit
...
Engine/mov
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b91777a64f | ||
|
|
775083fde7 | ||
|
|
84121fbeba | ||
|
|
f3d316d3e3 | ||
|
|
fad1a6d51b | ||
|
|
9b43c9ce9a | ||
|
|
dba7aca2cf | ||
|
|
47a22dbbaa | ||
|
|
1bbe33f3aa | ||
|
|
8c0fa09f1e | ||
|
|
d8eae9177b | ||
|
|
71ea2a05d0 | ||
|
|
33ca535547 | ||
|
|
08ba7ee436 | ||
|
|
5b3e84dab0 | ||
|
|
21d26902f0 | ||
|
|
e9f7663488 | ||
|
|
c2abc08206 | ||
|
|
9fa298ed51 | ||
|
|
a51d5e2652 | ||
|
|
0dd9a3d047 | ||
|
|
e896aba4ec | ||
|
|
9fb3d15bed | ||
|
|
35cb1eeb73 | ||
|
|
8165749845 | ||
|
|
06cb2b88c2 | ||
|
|
2d646f0ad0 | ||
|
|
40e2c50209 | ||
|
|
2e82a52f27 | ||
|
|
372d86b451 | ||
|
|
d66787f51d | ||
|
|
a4c63f19e5 | ||
|
|
a2f261778b | ||
|
|
3f7cc2df4d | ||
|
|
48272d6d70 | ||
|
|
d172afc50c | ||
|
|
ee61528891 | ||
|
|
0ace485bb6 | ||
|
|
1c1b9a96d9 | ||
|
|
e99b2defa3 | ||
|
|
f39c113ef9 | ||
|
|
f417a7e47c | ||
|
|
547b0e51cb | ||
|
|
5340744abe | ||
|
|
f64ebfa47f | ||
|
|
4e7ac2a195 | ||
|
|
dc176c103b | ||
|
|
85a7fa37ef | ||
|
|
a60658763d | ||
|
|
b76a009e4e | ||
|
|
a862d31c7d | ||
| 48f66aac75 | |||
|
|
431c65e075 | ||
|
|
7e64a7ca16 | ||
|
|
6e0efc76f3 | ||
|
|
1a9d7ad460 | ||
| 6dfe92f211 | |||
|
|
9209f1c4e0 | ||
| 4bb485e833 | |||
| c77e534ed8 | |||
| 1c92918692 | |||
| cca852d466 | |||
| a098c8051a | |||
| e599722e45 |
3
.github/workflows/upload_data.yml
vendored
3
.github/workflows/upload_data.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
echo "$GOOGLE_SERVICE_ACCOUNT_JSON" > service_account.json
|
echo "$GOOGLE_SERVICE_ACCOUNT_JSON" > service_account.json
|
||||||
|
|
||||||
python <<'PYCODE'
|
python <<'PYCODE'
|
||||||
import gspread, json, subprocess
|
import gspread, json, subprocess, time
|
||||||
|
|
||||||
creds = json.load(open("service_account.json"))
|
creds = json.load(open("service_account.json"))
|
||||||
gc = gspread.service_account_from_dict(creds)
|
gc = gspread.service_account_from_dict(creds)
|
||||||
@@ -38,6 +38,7 @@ jobs:
|
|||||||
|
|
||||||
for i, row in enumerate(rows_to_append):
|
for i, row in enumerate(rows_to_append):
|
||||||
worksheet.insert_row(row, start_row + i)
|
worksheet.insert_row(row, start_row + i)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
with open(f"{v}/test_data.log", "r") as f:
|
with open(f"{v}/test_data.log", "r") as f:
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
once_cell = "1.19"
|
once_cell = "1.19"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|||||||
@@ -4,5 +4,7 @@ mod legality;
|
|||||||
mod checkinfo;
|
mod checkinfo;
|
||||||
mod attacks;
|
mod attacks;
|
||||||
mod bitmove;
|
mod bitmove;
|
||||||
|
mod movebuffer;
|
||||||
|
mod movegen;
|
||||||
|
|
||||||
pub mod board;
|
pub mod board;
|
||||||
@@ -94,6 +94,64 @@ pub static RAY_TABLE: Lazy<[[u64; 8]; 64]> = Lazy::new(|| {
|
|||||||
return table;
|
return table;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ROOK_MOVE_MASK[<square_index>]
|
||||||
|
pub static ROOK_MOVE_MASK: Lazy<[u64; 64]> = Lazy::new(|| {
|
||||||
|
let mut table = [0u64; 64];
|
||||||
|
|
||||||
|
for sq in 0..64 {
|
||||||
|
for dir in [0, 2, 4, 6] {
|
||||||
|
table[sq] |= RAY_TABLE[sq][dir];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table
|
||||||
|
});
|
||||||
|
|
||||||
|
// BISHOP_MOVE_MASK[<square_index>]
|
||||||
|
pub static BISHOP_MOVE_MASK: Lazy<[u64; 64]> = Lazy::new(|| {
|
||||||
|
let mut table = [0u64; 64];
|
||||||
|
|
||||||
|
for sq in 0..64 {
|
||||||
|
for dir in [1, 3, 5, 7] {
|
||||||
|
table[sq] |= RAY_TABLE[sq][dir];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table
|
||||||
|
});
|
||||||
|
|
||||||
|
// KING_SAFETY_ROOK_MASK[<square_index>]
|
||||||
|
pub static KING_SAFETY_ROOK_MASK: Lazy<[u64; 64]> = Lazy::new(|| {
|
||||||
|
let mut table = [0u64; 64];
|
||||||
|
|
||||||
|
for sq in 0..64 {
|
||||||
|
let mut mask = KING_ATTACK_MAP[sq];
|
||||||
|
|
||||||
|
while mask != 0 {
|
||||||
|
let next_sq = mask.trailing_zeros();
|
||||||
|
table[sq] |= ROOK_MOVE_MASK[next_sq as usize];
|
||||||
|
mask &= !(1 << next_sq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table
|
||||||
|
});
|
||||||
|
|
||||||
|
// KING_SAFETY_BISHOP_MASK[<square_index>]
|
||||||
|
pub static KING_SAFETY_BISHOP_MASK: Lazy<[u64; 64]> = Lazy::new(|| {
|
||||||
|
let mut table = [0u64; 64];
|
||||||
|
|
||||||
|
for sq in 0..64 {
|
||||||
|
let mut mask = KING_ATTACK_MAP[sq];
|
||||||
|
|
||||||
|
while mask != 0 {
|
||||||
|
let next_sq = mask.trailing_zeros();
|
||||||
|
table[sq] |= BISHOP_MOVE_MASK[next_sq as usize];
|
||||||
|
mask &= !(1 << next_sq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// <----- TESTS ----->
|
// <----- TESTS ----->
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ impl Board {
|
|||||||
|
|
||||||
const RANK_2: u64 = 0x0000_0000_0000_FF00;
|
const RANK_2: u64 = 0x0000_0000_0000_FF00;
|
||||||
const RANK_7: u64 = 0x00FF_0000_0000_0000;
|
const RANK_7: u64 = 0x00FF_0000_0000_0000;
|
||||||
|
const A_FILE: u64 = 0x0101_0101_0101_0101;
|
||||||
|
const H_FILE: u64 = 0x8080_8080_8080_8080;
|
||||||
|
|
||||||
pub fn get_pseudo_pawn_moves(&self, sq: u32) -> u64 {
|
pub fn get_pseudo_pawn_moves(&self, sq: u32) -> u64 {
|
||||||
let pawn: u64 = 1 << sq;
|
let pawn: u64 = 1 << sq;
|
||||||
@@ -71,6 +73,111 @@ impl Board {
|
|||||||
pub fn get_pseudo_queen_moves(&self, sq: u32) -> u64 {
|
pub fn get_pseudo_queen_moves(&self, sq: u32) -> u64 {
|
||||||
return self.get_pseudo_bishop_moves(sq) | self.get_pseudo_rook_moves(sq);
|
return self.get_pseudo_bishop_moves(sq) | self.get_pseudo_rook_moves(sq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_pseudo_bishop_moves_ignore_king(&self, sq: u32) -> u64 {
|
||||||
|
let mut moves = 0u64;
|
||||||
|
let sq = sq as usize;
|
||||||
|
let king = self.bitboards[5 + 6*self.side_to_move as usize];
|
||||||
|
let occupancy = self.occupancy[2] & !king;
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 1);
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 3);
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 5);
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 7);
|
||||||
|
|
||||||
|
return moves;
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_pseudo_rook_moves_ignore_king(&self, sq: u32) -> u64 {
|
||||||
|
let mut moves: u64 = 0u64;
|
||||||
|
let sq = sq as usize;
|
||||||
|
let king = self.bitboards[5 + 6*self.side_to_move as usize];
|
||||||
|
let occupancy = self.occupancy[2] & !king;
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 0);
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 2);
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 4);
|
||||||
|
moves |= get_raycast_from_square_in_direction(occupancy, sq, 6);
|
||||||
|
|
||||||
|
return moves;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_square_attacked(&self, king_sq: u32) -> bool {
|
||||||
|
let offset: usize = 6 * self.side_to_move as usize;
|
||||||
|
|
||||||
|
// rook-queen checks (+)
|
||||||
|
let mut threat_mask: u64 = self.get_pseudo_rook_moves(king_sq);
|
||||||
|
let mut attacker_mask: u64 = self.bitboards[10 - offset] | self.bitboards[9 - offset];
|
||||||
|
if threat_mask & attacker_mask != 0 { return true; }
|
||||||
|
|
||||||
|
// bishop-queen checks (x)
|
||||||
|
threat_mask = self.get_pseudo_bishop_moves(king_sq);
|
||||||
|
attacker_mask = self.bitboards[10 - offset] | self.bitboards[8 - offset];
|
||||||
|
if threat_mask & attacker_mask != 0 { return true; }
|
||||||
|
|
||||||
|
// knight checks (L)
|
||||||
|
threat_mask = KNIGHT_ATTACK_MAP[king_sq as usize];
|
||||||
|
attacker_mask = self.bitboards[7 - offset];
|
||||||
|
if threat_mask & attacker_mask != 0 { return true; }
|
||||||
|
|
||||||
|
// pawn checks (v)
|
||||||
|
threat_mask = PAWN_ATTACK_MAP[king_sq as usize][self.side_to_move as usize];
|
||||||
|
attacker_mask = self.bitboards[6 - offset];
|
||||||
|
return threat_mask & attacker_mask != 0;
|
||||||
|
}
|
||||||
|
pub fn get_safe_king_squares(&self) -> u64 {
|
||||||
|
let offset: usize = 6 * (1 - self.side_to_move as usize);
|
||||||
|
let king_sq = self.bitboards[11 - offset].trailing_zeros() as usize;
|
||||||
|
let bishop_mask = KING_SAFETY_BISHOP_MASK[king_sq];
|
||||||
|
let rook_mask = KING_SAFETY_ROOK_MASK[king_sq];
|
||||||
|
let mut attack_map: u64 = 0u64;
|
||||||
|
|
||||||
|
let mut board: u64 = self.bitboards[offset];
|
||||||
|
if self.side_to_move() == 0 {
|
||||||
|
attack_map |= (board >> 9 & !Self::H_FILE) | (board >> 7 & !Self::A_FILE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
attack_map |= (board << 9 & !Self::A_FILE) | (board << 7 & !Self::H_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
board = self.bitboards[offset + 1];
|
||||||
|
while board != 0 {
|
||||||
|
let piece_sq: u32 = board.trailing_zeros();
|
||||||
|
board &= !(1 << piece_sq);
|
||||||
|
|
||||||
|
attack_map |= self.get_pseudo_knight_moves(piece_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
board = self.bitboards[offset + 2] & bishop_mask;
|
||||||
|
while board != 0 {
|
||||||
|
let piece_sq: u32 = board.trailing_zeros();
|
||||||
|
board &= !(1 << piece_sq);
|
||||||
|
|
||||||
|
attack_map |= self.get_pseudo_bishop_moves_ignore_king(piece_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
board = self.bitboards[offset + 3] & rook_mask;
|
||||||
|
while board != 0 {
|
||||||
|
let piece_sq: u32 = board.trailing_zeros();
|
||||||
|
board &= !(1 << piece_sq);
|
||||||
|
|
||||||
|
attack_map |= self.get_pseudo_rook_moves_ignore_king(piece_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
board = self.bitboards[offset + 4] & (bishop_mask | rook_mask);
|
||||||
|
while board != 0 {
|
||||||
|
let piece_sq: u32 = board.trailing_zeros();
|
||||||
|
board &= !(1 << piece_sq);
|
||||||
|
|
||||||
|
attack_map |= self.get_pseudo_rook_moves_ignore_king(piece_sq) | self.get_pseudo_bishop_moves_ignore_king(piece_sq);
|
||||||
|
}
|
||||||
|
|
||||||
|
board = self.bitboards[offset + 5];
|
||||||
|
let piece_sq: u32 = board.trailing_zeros();
|
||||||
|
attack_map |= self.get_pseudo_king_moves(piece_sq);
|
||||||
|
|
||||||
|
return !attack_map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl Board {
|
|||||||
|
|
||||||
for (i, c) in coming_up.chars().enumerate() {
|
for (i, c) in coming_up.chars().enumerate() {
|
||||||
if pieces.contains(&c) {
|
if pieces.contains(&c) {
|
||||||
// board.place_piece(row*8 + col, c);
|
board.place_piece(row*8 + col, c);
|
||||||
col += 1;
|
col += 1;
|
||||||
}
|
}
|
||||||
else if ('1'..='8').contains(&c) {
|
else if ('1'..='8').contains(&c) {
|
||||||
@@ -179,4 +179,21 @@ impl Board {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn place_piece(&mut self, sq: i32, piece: char) {
|
||||||
|
match piece {
|
||||||
|
'p' => {self.bitboards[6] |= 1 << sq}
|
||||||
|
'n' => {self.bitboards[7] |= 1 << sq}
|
||||||
|
'b' => {self.bitboards[8] |= 1 << sq}
|
||||||
|
'r' => {self.bitboards[9] |= 1 << sq}
|
||||||
|
'q' => {self.bitboards[10] |= 1 << sq}
|
||||||
|
'k' => {self.bitboards[11] |= 1 << sq}
|
||||||
|
'P' => {self.bitboards[0] |= 1 << sq}
|
||||||
|
'N' => {self.bitboards[1] |= 1 << sq}
|
||||||
|
'B' => {self.bitboards[2] |= 1 << sq}
|
||||||
|
'R' => {self.bitboards[3] |= 1 << sq}
|
||||||
|
'Q' => {self.bitboards[4] |= 1 << sq}
|
||||||
|
'K' => {self.bitboards[5] |= 1 << sq}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,56 @@
|
|||||||
use super::board::Board;
|
use super::board::Board;
|
||||||
use super::attackmaps::RAY_TABLE;
|
use super::attackmaps::RAY_TABLE;
|
||||||
|
use super::checkinfo::CheckInfo;
|
||||||
|
use super::attacks::get_raycast_from_square_in_direction;
|
||||||
|
|
||||||
impl Board {
|
impl Board {
|
||||||
|
|
||||||
|
pub fn check_test(&self) -> CheckInfo {
|
||||||
|
let mut check_info: CheckInfo = CheckInfo::new();
|
||||||
|
let offset: usize = 6 * self.side_to_move as usize;
|
||||||
|
let king: u64 = self.bitboards[5 + offset];
|
||||||
|
let king_sq = king.trailing_zeros() as usize;
|
||||||
|
let occupancy = self.occupancy[2];
|
||||||
|
|
||||||
|
// queen-rook checks (+)
|
||||||
|
let attacker_mask = self.bitboards[10 - offset] | self.bitboards[9 - offset];
|
||||||
|
|
||||||
|
for dir in [0, 2, 4, 6] {
|
||||||
|
let threat_mask: u64 = get_raycast_from_square_in_direction(occupancy, king_sq, dir);
|
||||||
|
if threat_mask & attacker_mask != 0 {
|
||||||
|
check_info.add_checker(threat_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// queen-bishop checks (x)
|
||||||
|
let attacker_mask = self.bitboards[10 - offset] | self.bitboards[8 - offset];
|
||||||
|
|
||||||
|
for dir in [1, 3, 5, 7] {
|
||||||
|
let threat_mask = get_raycast_from_square_in_direction(occupancy, king_sq, dir);
|
||||||
|
if threat_mask & attacker_mask != 0 {
|
||||||
|
check_info.add_checker(threat_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// knight checks (L)
|
||||||
|
let attacker_mask = self.bitboards[7 - offset];
|
||||||
|
let threat_mask = self.get_pseudo_knight_moves(king_sq as u32);
|
||||||
|
let checker = threat_mask & attacker_mask;
|
||||||
|
if checker != 0 {
|
||||||
|
check_info.add_checker(checker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pawn checks (v)
|
||||||
|
let attacker_mask = self.bitboards[6 - offset];
|
||||||
|
let threat_mask = self.get_pseudo_pawn_captures(king_sq as u32);
|
||||||
|
let checker = threat_mask & attacker_mask;
|
||||||
|
if checker != 0 {
|
||||||
|
check_info.add_checker(checker);
|
||||||
|
}
|
||||||
|
|
||||||
|
return check_info;
|
||||||
|
}
|
||||||
|
|
||||||
pub(in super) fn calc_pinned_squares(&mut self) {
|
pub(in super) fn calc_pinned_squares(&mut self) {
|
||||||
self.pinned_squares = [4; 64];
|
self.pinned_squares = [4; 64];
|
||||||
self.pin_mask = 0u64;
|
self.pin_mask = 0u64;
|
||||||
@@ -45,4 +93,42 @@ impl Board {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(in super) fn get_pin_masked_moves(&self, moves: u64, sq: u32) -> u64 {
|
||||||
|
let sq: usize = sq as usize;
|
||||||
|
if self.pinned_squares[sq] == 4 { return moves; }
|
||||||
|
let dir: u8 = self.pinned_squares[sq];
|
||||||
|
return moves & (RAY_TABLE[sq][dir as usize] | RAY_TABLE[sq][4 + dir as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <----- TESTS ----->
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_test_test() {
|
||||||
|
|
||||||
|
let fens = [
|
||||||
|
"rnb1k1nr/pppppppp/4q3/8/1b6/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", // no check
|
||||||
|
"rnb1k1nr/pppppppp/4q3/8/1b1P4/8/PPP1PPPP/RNBQKBNR w KQkq d3 0 1", // single check
|
||||||
|
"rnb1k1nr/ppp1p2p/3pq1p1/8/1b1P1P2/8/PPP2PPP/RNBQKBNR w KQkq - 0 1" // double check
|
||||||
|
];
|
||||||
|
let expected_results = [
|
||||||
|
CheckInfo { check_count: 0, move_mask: 0xFFFF_FFFF_FFFF_FFFF },
|
||||||
|
CheckInfo { check_count: 1, move_mask: 0x0000_0000_0204_0800 },
|
||||||
|
CheckInfo { check_count: 2, move_mask: 0x0000_0000_0000_0000 }
|
||||||
|
];
|
||||||
|
|
||||||
|
for test_nr in 0..3 {
|
||||||
|
let fen = fens[test_nr];
|
||||||
|
let board = Board::build(fen);
|
||||||
|
let check_test_actual = board.check_test();
|
||||||
|
assert_eq!(check_test_actual.check_count, expected_results[test_nr].check_count);
|
||||||
|
assert_eq!(check_test_actual.move_mask, expected_results[test_nr].move_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
44
engine/src/bitboard/movebuffer.rs
Normal file
44
engine/src/bitboard/movebuffer.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use super::bitmove::BitMove;
|
||||||
|
|
||||||
|
pub struct MoveBuffer {
|
||||||
|
|
||||||
|
buffer: [BitMove; 256],
|
||||||
|
count: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MoveBuffer {
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
return Self {
|
||||||
|
buffer: [BitMove::quiet(0, 0, None); 256],
|
||||||
|
count: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn add(&mut self, bitmove: BitMove) {
|
||||||
|
self.buffer[self.count] = bitmove;
|
||||||
|
self.count += 1;
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn append(&mut self, other: &MoveBuffer) {
|
||||||
|
self.buffer[self.count..self.count + other.count()].copy_from_slice(other.contents());
|
||||||
|
self.count += other.count();
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn count(&self) -> usize{
|
||||||
|
return self.count;
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&self, idx: usize) -> &BitMove {
|
||||||
|
return &self.buffer[idx];
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn contents(&self) -> &[BitMove] {
|
||||||
|
return &self.buffer[0..self.count];
|
||||||
|
}
|
||||||
|
}
|
||||||
67
engine/src/bitboard/movegen.rs
Normal file
67
engine/src/bitboard/movegen.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
mod pawns;
|
||||||
|
mod knights;
|
||||||
|
mod bishops;
|
||||||
|
mod rooks;
|
||||||
|
mod queens;
|
||||||
|
mod kings;
|
||||||
|
|
||||||
|
use super::board::Board;
|
||||||
|
use super::movebuffer::MoveBuffer;
|
||||||
|
use super::bitmove::BitMove;
|
||||||
|
use super::checkinfo::CheckInfo;
|
||||||
|
use super::utils::*;
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
|
||||||
|
const NO_FILTER: u64 = 0xFFFF_FFFF_FFFF_FFFF;
|
||||||
|
|
||||||
|
pub fn collect_moves(&mut self, buffer: &mut MoveBuffer, temp_buffer: &mut MoveBuffer) -> bool {
|
||||||
|
buffer.clear();
|
||||||
|
self.calc_pinned_squares();
|
||||||
|
let check_info = self.check_test();
|
||||||
|
|
||||||
|
match check_info.check_count {
|
||||||
|
0 => self.collect_all_moves(buffer, temp_buffer),
|
||||||
|
1 => self.collect_moves_single_check(buffer, temp_buffer, &check_info),
|
||||||
|
2 => self.collect_king_evasion(buffer, temp_buffer),
|
||||||
|
_ => panic!("More than 2 checking pieces found as the same time!")
|
||||||
|
}
|
||||||
|
return check_info.check_count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in super) fn collect_all_moves(&self, buffer: &mut MoveBuffer, temp_buffer: &mut MoveBuffer) {
|
||||||
|
let safe_squares = self.get_safe_king_squares();
|
||||||
|
|
||||||
|
self.add_pawn_moves(buffer, temp_buffer, Self::NO_FILTER);
|
||||||
|
self.add_knight_moves(buffer, temp_buffer, Self::NO_FILTER);
|
||||||
|
self.add_bishop_moves(buffer, temp_buffer, Self::NO_FILTER);
|
||||||
|
self.add_rook_moves(buffer, temp_buffer, Self::NO_FILTER);
|
||||||
|
self.add_queen_moves(buffer, temp_buffer, Self::NO_FILTER);
|
||||||
|
self.add_king_moves(buffer, temp_buffer, safe_squares);
|
||||||
|
self.add_king_castles(buffer, safe_squares);
|
||||||
|
|
||||||
|
buffer.append(temp_buffer);
|
||||||
|
temp_buffer.clear();
|
||||||
|
}
|
||||||
|
pub(in super) fn collect_moves_single_check(&self, buffer: &mut MoveBuffer, temp_buffer: &mut MoveBuffer, check_info: &CheckInfo) {
|
||||||
|
let safe_squares = self.get_safe_king_squares();
|
||||||
|
|
||||||
|
self.add_pawn_moves(buffer, temp_buffer, check_info.move_mask);
|
||||||
|
self.add_knight_moves(buffer, temp_buffer, check_info.move_mask);
|
||||||
|
self.add_bishop_moves(buffer, temp_buffer, check_info.move_mask);
|
||||||
|
self.add_rook_moves(buffer, temp_buffer, check_info.move_mask);
|
||||||
|
self.add_queen_moves(buffer, temp_buffer, check_info.move_mask);
|
||||||
|
self.add_king_moves(buffer, temp_buffer, safe_squares);
|
||||||
|
|
||||||
|
buffer.append(temp_buffer);
|
||||||
|
temp_buffer.clear();
|
||||||
|
}
|
||||||
|
pub(in super) fn collect_king_evasion(&self, buffer: &mut MoveBuffer, temp_buffer: &mut MoveBuffer) {
|
||||||
|
let safe_squares = self.get_safe_king_squares();
|
||||||
|
|
||||||
|
self.add_king_moves(buffer, temp_buffer, safe_squares);
|
||||||
|
|
||||||
|
buffer.append(&temp_buffer);
|
||||||
|
temp_buffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
55
engine/src/bitboard/movegen/bishops.rs
Normal file
55
engine/src/bitboard/movegen/bishops.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
|
||||||
|
pub fn add_bishop_moves(&self, capture_buffer: &mut MoveBuffer, quiet_buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let piece_index = 2 + self.side_to_move * 6;
|
||||||
|
let mut bishops = self.bitboards[piece_index as usize];
|
||||||
|
let empty = !self.occupancy[2];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while bishops != 0 {
|
||||||
|
let from_sq = pop_lsb(&mut bishops);
|
||||||
|
let raw_move_map = self.get_pseudo_bishop_moves(from_sq) & move_mask;
|
||||||
|
let move_map = self.get_pin_masked_moves(raw_move_map, from_sq);
|
||||||
|
|
||||||
|
let mut quiet_map = move_map & empty;
|
||||||
|
let mut capture_map = move_map & opponents;
|
||||||
|
|
||||||
|
while quiet_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut quiet_map);
|
||||||
|
quiet_buffer.add(BitMove::quiet(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
while capture_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut capture_map);
|
||||||
|
capture_buffer.add(BitMove::capture(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_bishop_captures(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let offset = 6 * self.side_to_move as usize;
|
||||||
|
let mut bishops: u64 = self.bitboards[2 + offset];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while bishops != 0 {
|
||||||
|
let next_sq: u32 = pop_lsb(&mut bishops);
|
||||||
|
let mut attacks: u64 = self.get_pseudo_bishop_moves(next_sq) & opponents & move_mask;
|
||||||
|
attacks = self.get_pin_masked_moves(attacks, next_sq);
|
||||||
|
|
||||||
|
while attacks != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut attacks);
|
||||||
|
buffer.add(BitMove::capture(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
88
engine/src/bitboard/movegen/kings.rs
Normal file
88
engine/src/bitboard/movegen/kings.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
|
||||||
|
pub fn add_king_moves(&self, capture_buffer: &mut MoveBuffer, quiet_buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let piece_index = 5 + self.side_to_move * 6;
|
||||||
|
let mut kings = self.bitboards[piece_index as usize];
|
||||||
|
let empty = !self.occupancy[2];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while kings != 0 {
|
||||||
|
let from_sq = pop_lsb(&mut kings);
|
||||||
|
let move_map = self.get_pseudo_king_moves(from_sq) & move_mask;
|
||||||
|
|
||||||
|
let mut quiet_map = move_map & empty;
|
||||||
|
let mut capture_map = move_map & opponents;
|
||||||
|
|
||||||
|
while quiet_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut quiet_map);
|
||||||
|
quiet_buffer.add(BitMove::quiet(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
while capture_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut capture_map);
|
||||||
|
capture_buffer.add(BitMove::capture(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_king_captures(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let offset = 6 * self.side_to_move as usize;
|
||||||
|
let mut kings: u64 = self.bitboards[5 + offset];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while kings != 0 {
|
||||||
|
let next_sq: u32 = pop_lsb(&mut kings);
|
||||||
|
let mut attacks: u64 = self.get_pseudo_king_moves(next_sq) & opponents & move_mask;
|
||||||
|
attacks = self.get_pin_masked_moves(attacks, next_sq);
|
||||||
|
|
||||||
|
while attacks != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut attacks);
|
||||||
|
buffer.add(BitMove::capture(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_king_castles(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
if self.castling_rights & (0b11 << (2 - 2 * self.side_to_move)) == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = 5 + 6 * self.side_to_move as u8;
|
||||||
|
let castle_offset = 2 - 2 * self.side_to_move as u8;
|
||||||
|
let castling_rights = self.castling_rights & 3 << castle_offset;
|
||||||
|
let occupied = self.occupancy[2];
|
||||||
|
let king_sq = self.bitboards[offset as usize].trailing_zeros();
|
||||||
|
|
||||||
|
let queenside_mask = 0b111 << (king_sq - 3);
|
||||||
|
let kingside_mask = 0b11 << (king_sq + 1);
|
||||||
|
|
||||||
|
if (castling_rights & 1 << castle_offset) != 0
|
||||||
|
&& queenside_mask & occupied == 0
|
||||||
|
&& !move_mask & 0b11 << (king_sq - 2) == 0
|
||||||
|
&& !self.is_square_attacked(king_sq - 2) {
|
||||||
|
buffer.add(BitMove::castle(
|
||||||
|
king_sq as u8,
|
||||||
|
(king_sq - 2) as u8
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (castling_rights & 2 << castle_offset) != 0
|
||||||
|
&& kingside_mask & occupied == 0
|
||||||
|
&& !move_mask & 0b11 << (king_sq + 1) == 0
|
||||||
|
&& !self.is_square_attacked(king_sq + 2) {
|
||||||
|
buffer.add(BitMove::castle(
|
||||||
|
king_sq as u8,
|
||||||
|
(king_sq + 2) as u8
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
55
engine/src/bitboard/movegen/knights.rs
Normal file
55
engine/src/bitboard/movegen/knights.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
|
||||||
|
pub fn add_knight_moves(&self, capture_buffer: &mut MoveBuffer, quiet_buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let piece_index = 1 + self.side_to_move * 6;
|
||||||
|
let mut knights = self.bitboards[piece_index as usize];
|
||||||
|
let empty = !self.occupancy[2];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while knights != 0 {
|
||||||
|
let from_sq = pop_lsb(&mut knights);
|
||||||
|
let raw_move_map = self.get_pseudo_knight_moves(from_sq) & move_mask;
|
||||||
|
let move_map = self.get_pin_masked_moves(raw_move_map, from_sq);
|
||||||
|
|
||||||
|
let mut quiet_map = move_map & empty;
|
||||||
|
let mut capture_map = move_map & opponents;
|
||||||
|
|
||||||
|
while quiet_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut quiet_map);
|
||||||
|
quiet_buffer.add(BitMove::quiet(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
while capture_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut capture_map);
|
||||||
|
capture_buffer.add(BitMove::capture(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_knight_captures(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let offset = 6 * self.side_to_move as usize;
|
||||||
|
let mut knights: u64 = self.bitboards[1 + offset];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while knights != 0 {
|
||||||
|
let next_sq: u32 = pop_lsb(&mut knights);
|
||||||
|
let mut attacks: u64 = self.get_pseudo_knight_moves(next_sq) & opponents & move_mask;
|
||||||
|
attacks = self.get_pin_masked_moves(attacks, next_sq);
|
||||||
|
|
||||||
|
while attacks != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut attacks);
|
||||||
|
buffer.add(BitMove::capture(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
engine/src/bitboard/movegen/pawns.rs
Normal file
76
engine/src/bitboard/movegen/pawns.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
|
||||||
|
pub fn add_pawn_quiets(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let offset: u8 = self.side_to_move * 6;
|
||||||
|
let mut pawns: u64 = self.bitboards[offset as usize];
|
||||||
|
while pawns != 0 {
|
||||||
|
let next_sq = pop_lsb(&mut pawns);
|
||||||
|
|
||||||
|
let mut quiets: u64 = self.get_pseudo_pawn_moves(next_sq) & move_mask;
|
||||||
|
quiets = self.get_pin_masked_moves(quiets, next_sq);
|
||||||
|
while quiets != 0 {
|
||||||
|
let to_sq = quiets.trailing_zeros();
|
||||||
|
|
||||||
|
if (self.side_to_move == 0 && quiets.trailing_zeros() / 8 == 7)
|
||||||
|
|| (self.side_to_move == 1 && quiets.trailing_zeros() / 8 == 0) {
|
||||||
|
for piece_type in [4, 3, 2, 1] {
|
||||||
|
buffer.add(BitMove::quiet(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
Some(piece_type)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer.add(BitMove::quiet(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
quiets &= !(1 << to_sq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_pawn_captures(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let offset = 6 * self.side_to_move as usize;
|
||||||
|
let mut pawns: u64 = self.bitboards[offset];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while pawns != 0 {
|
||||||
|
let next_sq = pop_lsb(&mut pawns);
|
||||||
|
|
||||||
|
let mut attacks: u64 = self.get_pseudo_pawn_captures(next_sq) & move_mask;
|
||||||
|
attacks = self.get_pin_masked_moves(attacks, next_sq);
|
||||||
|
attacks &= opponents;
|
||||||
|
|
||||||
|
while attacks != 0 {
|
||||||
|
let to_sq = attacks.trailing_zeros();
|
||||||
|
|
||||||
|
if (self.side_to_move == 0 && attacks.trailing_zeros() / 8 == 7)
|
||||||
|
|| (self.side_to_move == 1 && attacks.trailing_zeros() / 8 == 0) {
|
||||||
|
for piece_type in [4, 3, 2, 1] {
|
||||||
|
buffer.add(BitMove::capture(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
Some(piece_type)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer.add(BitMove::capture(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
attacks &= !(1 << to_sq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_pawn_moves(&self, capture_buffer: &mut MoveBuffer, quiet_buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
self.add_pawn_captures(capture_buffer, move_mask);
|
||||||
|
self.add_pawn_quiets(quiet_buffer, move_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
engine/src/bitboard/movegen/queens.rs
Normal file
55
engine/src/bitboard/movegen/queens.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
|
||||||
|
pub fn add_queen_moves(&self, capture_buffer: &mut MoveBuffer, quiet_buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let piece_index = 4 + self.side_to_move * 6;
|
||||||
|
let mut queens = self.bitboards[piece_index as usize];
|
||||||
|
let empty = !self.occupancy[2];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while queens != 0 {
|
||||||
|
let from_sq = pop_lsb(&mut queens);
|
||||||
|
let raw_move_map = self.get_pseudo_queen_moves(from_sq) & move_mask;
|
||||||
|
let move_map = self.get_pin_masked_moves(raw_move_map, from_sq);
|
||||||
|
|
||||||
|
let mut quiet_map = move_map & empty;
|
||||||
|
let mut capture_map = move_map & opponents;
|
||||||
|
|
||||||
|
while quiet_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut quiet_map);
|
||||||
|
quiet_buffer.add(BitMove::quiet(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
while capture_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut capture_map);
|
||||||
|
capture_buffer.add(BitMove::capture(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_queen_captures(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let offset = 6 * self.side_to_move as usize;
|
||||||
|
let mut queens: u64 = self.bitboards[4 + offset];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while queens != 0 {
|
||||||
|
let next_sq: u32 = pop_lsb(&mut queens);
|
||||||
|
let mut attacks: u64 = self.get_pseudo_queen_moves(next_sq) & opponents & move_mask;
|
||||||
|
attacks = self.get_pin_masked_moves(attacks, next_sq);
|
||||||
|
|
||||||
|
while attacks != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut attacks);
|
||||||
|
buffer.add(BitMove::capture(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
engine/src/bitboard/movegen/rooks.rs
Normal file
55
engine/src/bitboard/movegen/rooks.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
|
||||||
|
pub fn add_rook_moves(&self, capture_buffer: &mut MoveBuffer, quiet_buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let piece_index = 3 + self.side_to_move * 6;
|
||||||
|
let mut rooks = self.bitboards[piece_index as usize];
|
||||||
|
let empty = !self.occupancy[2];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while rooks != 0 {
|
||||||
|
let from_sq = pop_lsb(&mut rooks);
|
||||||
|
let raw_move_map = self.get_pseudo_rook_moves(from_sq) & move_mask;
|
||||||
|
let move_map = self.get_pin_masked_moves(raw_move_map, from_sq);
|
||||||
|
|
||||||
|
let mut quiet_map = move_map & empty;
|
||||||
|
let mut capture_map = move_map & opponents;
|
||||||
|
|
||||||
|
while quiet_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut quiet_map);
|
||||||
|
quiet_buffer.add(BitMove::quiet(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
while capture_map != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut capture_map);
|
||||||
|
capture_buffer.add(BitMove::capture(
|
||||||
|
from_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_rook_captures(&self, buffer: &mut MoveBuffer, move_mask: u64) {
|
||||||
|
let offset = 6 * self.side_to_move as usize;
|
||||||
|
let mut rooks: u64 = self.bitboards[3 + offset];
|
||||||
|
let opponents = self.occupancy[1 - self.side_to_move as usize];
|
||||||
|
while rooks != 0 {
|
||||||
|
let next_sq: u32 = pop_lsb(&mut rooks);
|
||||||
|
let mut attacks: u64 = self.get_pseudo_rook_moves(next_sq) & opponents & move_mask;
|
||||||
|
attacks = self.get_pin_masked_moves(attacks, next_sq);
|
||||||
|
|
||||||
|
while attacks != 0 {
|
||||||
|
let to_sq = pop_lsb(&mut attacks);
|
||||||
|
buffer.add(BitMove::capture(
|
||||||
|
next_sq as u8,
|
||||||
|
to_sq as u8,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pop_lsb(value: &mut u64) -> usize {
|
pub fn pop_lsb(value: &mut u64) -> u32 {
|
||||||
let idx = value.trailing_zeros() as usize;
|
let idx = value.trailing_zeros();
|
||||||
*value &= !(1 << idx);
|
*value &= !(1 << idx);
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pop_msb(value: &mut u64) -> usize {
|
pub fn pop_msb(value: &mut u64) -> u32 {
|
||||||
let idx = 63 - value.leading_zeros() as usize;
|
let idx = 63 - value.leading_zeros();
|
||||||
*value &= !(1 << idx);
|
*value &= !(1 << idx);
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ mod tests {
|
|||||||
0xBEAC_DBE0_903A_AC00,
|
0xBEAC_DBE0_903A_AC00,
|
||||||
0x01E8_C895_A6F0_0000
|
0x01E8_C895_A6F0_0000
|
||||||
];
|
];
|
||||||
let expected_values: [usize; 6] = [63, 0, 4, 2, 10, 20];
|
let expected_values: [u32; 6] = [63, 0, 4, 2, 10, 20];
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
for index in 0..6 {
|
for index in 0..6 {
|
||||||
@@ -92,7 +92,7 @@ mod tests {
|
|||||||
0x0000_C1C3_201C_0DB1,
|
0x0000_C1C3_201C_0DB1,
|
||||||
0x0000_0203_0DE4_E944
|
0x0000_0203_0DE4_E944
|
||||||
];
|
];
|
||||||
let expected_values: [usize; 6] = [63, 0, 61, 57, 47, 41];
|
let expected_values: [u32; 6] = [63, 0, 61, 57, 47, 41];
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
for index in 0..6 {
|
for index in 0..6 {
|
||||||
|
|||||||
@@ -1,32 +1,31 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct BoardSquare {
|
pub struct BoardSquare {
|
||||||
pub x: usize,
|
pub x: usize,
|
||||||
pub y: usize
|
pub y: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BoardSquare {
|
impl BoardSquare {
|
||||||
|
pub fn new() -> Self {
|
||||||
pub fn new() -> Self {
|
return Self { x: 0, y: 0 };
|
||||||
return Self{
|
}
|
||||||
x: 0,
|
|
||||||
y: 0
|
pub fn from_coord(x: usize, y: usize) -> Self {
|
||||||
};
|
#[cfg(debug_assertions)]
|
||||||
}
|
{
|
||||||
|
if x > 7 {
|
||||||
pub fn from_coord(x: usize, y: usize) -> Self {
|
println!(
|
||||||
|
"Warning: x coordinate of square is bigger than 7, it might not be on the board!"
|
||||||
#[cfg(debug_assertions)]
|
);
|
||||||
{
|
}
|
||||||
if x > 7 {
|
if y > 7 {
|
||||||
println!("Warning: x coordinate of square is bigger than 7, it might not be on the board!");
|
println!(
|
||||||
}
|
"Warning: y coordinate of square is bigger than 7, it might not be on the board!"
|
||||||
if y > 7 {
|
);
|
||||||
println!("Warning: y coordinate of square is bigger than 7, it might not be on the board!");
|
}
|
||||||
}
|
}
|
||||||
|
return Self { x: x, y: y };
|
||||||
}
|
}
|
||||||
return Self {
|
|
||||||
x: x,
|
|
||||||
y: y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +1,71 @@
|
|||||||
use crate::piecetype;
|
use crate::piecetype;
|
||||||
|
|
||||||
use super::boardsquare::BoardSquare;
|
use super::boardsquare::BoardSquare;
|
||||||
use super::piecetype::PieceType;
|
|
||||||
use super::movetype::MoveType;
|
use super::movetype::MoveType;
|
||||||
|
use super::piecetype::PieceType;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct ChessMove {
|
pub struct ChessMove {
|
||||||
pub move_type: MoveType,
|
pub move_type: MoveType,
|
||||||
pub piece_type: PieceType,
|
pub piece_type: PieceType,
|
||||||
pub from_square: BoardSquare,
|
pub from_square: BoardSquare,
|
||||||
pub to_square: BoardSquare,
|
pub to_square: BoardSquare,
|
||||||
pub rook_from: BoardSquare,
|
pub rook_from: BoardSquare,
|
||||||
pub rook_to: BoardSquare,
|
pub rook_to: BoardSquare,
|
||||||
pub promotion_piece: Option<PieceType>
|
pub promotion_piece: Option<PieceType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChessMove {
|
impl ChessMove {
|
||||||
|
pub fn quiet(
|
||||||
pub fn quiet(
|
piece_type: PieceType,
|
||||||
piece_type: PieceType,
|
from_square: BoardSquare,
|
||||||
from_square: BoardSquare,
|
to_square: BoardSquare,
|
||||||
to_square: BoardSquare,
|
promotion_piece: Option<PieceType>,
|
||||||
promotion_piece: Option<PieceType>
|
) -> Self {
|
||||||
) -> Self {
|
return Self {
|
||||||
return Self {
|
move_type: MoveType::Quiet,
|
||||||
move_type: MoveType::Quiet,
|
piece_type: piece_type,
|
||||||
piece_type: piece_type,
|
from_square: from_square,
|
||||||
from_square: from_square,
|
to_square: to_square,
|
||||||
to_square: to_square,
|
rook_from: BoardSquare::new(),
|
||||||
rook_from: BoardSquare::new(),
|
rook_to: BoardSquare::new(),
|
||||||
rook_to: BoardSquare::new(),
|
promotion_piece: promotion_piece,
|
||||||
promotion_piece: promotion_piece
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn capture(
|
pub fn capture(
|
||||||
piece_type: PieceType,
|
piece_type: PieceType,
|
||||||
from_square: BoardSquare,
|
from_square: BoardSquare,
|
||||||
to_square: BoardSquare,
|
to_square: BoardSquare,
|
||||||
promotion_piece: Option<PieceType>
|
promotion_piece: Option<PieceType>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
return Self {
|
return Self {
|
||||||
move_type: MoveType::Capture,
|
move_type: MoveType::Capture,
|
||||||
piece_type: piece_type,
|
piece_type: piece_type,
|
||||||
from_square: from_square,
|
from_square: from_square,
|
||||||
to_square: to_square,
|
to_square: to_square,
|
||||||
rook_from: BoardSquare::new(),
|
rook_from: BoardSquare::new(),
|
||||||
rook_to: BoardSquare::new(),
|
rook_to: BoardSquare::new(),
|
||||||
promotion_piece: promotion_piece
|
promotion_piece: promotion_piece,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn castle(
|
pub fn castle(
|
||||||
piece_type: PieceType,
|
piece_type: PieceType,
|
||||||
from_square: BoardSquare,
|
from_square: BoardSquare,
|
||||||
to_square: BoardSquare,
|
to_square: BoardSquare,
|
||||||
rook_from: BoardSquare,
|
rook_from: BoardSquare,
|
||||||
rook_to: BoardSquare
|
rook_to: BoardSquare,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
return Self {
|
return Self {
|
||||||
move_type: MoveType::Quiet,
|
move_type: MoveType::Quiet,
|
||||||
piece_type: piece_type,
|
piece_type: piece_type,
|
||||||
from_square: from_square,
|
from_square: from_square,
|
||||||
to_square: to_square,
|
to_square: to_square,
|
||||||
rook_from: rook_from,
|
rook_from: rook_from,
|
||||||
rook_to: rook_to,
|
rook_to: rook_to,
|
||||||
promotion_piece: None
|
promotion_piece: None,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub enum MoveType {
|
pub enum MoveType {
|
||||||
Quiet,
|
Quiet,
|
||||||
Capture,
|
Capture,
|
||||||
Castle,
|
Castle,
|
||||||
EnPassant
|
EnPassant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub enum PieceType {
|
pub enum PieceType {
|
||||||
WhitePawn,
|
WhitePawn,
|
||||||
WhiteKnight,
|
WhiteKnight,
|
||||||
WhiteBishop,
|
WhiteBishop,
|
||||||
WhiteRook,
|
WhiteRook,
|
||||||
WhiteQueen,
|
WhiteQueen,
|
||||||
WhiteKing,
|
WhiteKing,
|
||||||
BlackPawn,
|
BlackPawn,
|
||||||
BlackKnight,
|
BlackKnight,
|
||||||
BlackBishop,
|
BlackBishop,
|
||||||
BlackRook,
|
BlackRook,
|
||||||
BlackQueen,
|
BlackQueen,
|
||||||
BlackKing
|
BlackKing,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,3 +14,9 @@ url = "2.5.7"
|
|||||||
uuid = {version = "1.18.1", features = ["v4", "serde"] }
|
uuid = {version = "1.18.1", features = ["v4", "serde"] }
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
|
engine = {path = "../engine/"}
|
||||||
|
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "client"
|
||||||
|
path = "src/bin/client.rs"
|
||||||
|
|||||||
195
server/src/bin/client.rs
Normal file
195
server/src/bin/client.rs
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use tokio_tungstenite::{connect_async, tungstenite::Message};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
enum ClientMessage {
|
||||||
|
Join { username: String },
|
||||||
|
FindMatch,
|
||||||
|
Move { from: String, to: String },
|
||||||
|
Resign,
|
||||||
|
Chat { text: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct ServerMessage {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
message_type: String,
|
||||||
|
player_id: Option<String>,
|
||||||
|
match_id: Option<String>,
|
||||||
|
opponent: Option<String>,
|
||||||
|
color: Option<String>,
|
||||||
|
reason: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
println!("Knightly Chess Client");
|
||||||
|
println!("========================");
|
||||||
|
|
||||||
|
// Get server address from user
|
||||||
|
print!("Enter server address [ws://127.0.0.1:9001]: ");
|
||||||
|
io::stdout().flush()?;
|
||||||
|
let mut server_addr = String::new();
|
||||||
|
io::stdin().read_line(&mut server_addr)?;
|
||||||
|
let server_addr = server_addr.trim();
|
||||||
|
let server_addr = if server_addr.is_empty() {
|
||||||
|
"ws://127.0.0.1:9001".to_string()
|
||||||
|
} else {
|
||||||
|
server_addr.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connect to server
|
||||||
|
println!("Connecting to {}...", server_addr);
|
||||||
|
let url = Url::parse(&server_addr)?;
|
||||||
|
let (ws_stream, _) = connect_async(url).await?;
|
||||||
|
println!("Connected to server!");
|
||||||
|
|
||||||
|
let (mut write, mut read) = ws_stream.split();
|
||||||
|
|
||||||
|
// Spawn a task to handle incoming messages
|
||||||
|
let read_handle = tokio::spawn(async move {
|
||||||
|
while let Some(message) = read.next().await {
|
||||||
|
match message {
|
||||||
|
Ok(msg) => {
|
||||||
|
if msg.is_text() {
|
||||||
|
let text = msg.to_text().unwrap();
|
||||||
|
println!("\nServer: {}", text);
|
||||||
|
|
||||||
|
// Try to parse as structured message
|
||||||
|
if let Ok(parsed) = serde_json::from_str::<ServerMessage>(text) {
|
||||||
|
match parsed.message_type.as_str() {
|
||||||
|
"welcome" => {
|
||||||
|
if let Some(player_id) = parsed.player_id {
|
||||||
|
println!("Welcome! Your player ID: {}", player_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("cucc: {:?}", parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error receiving message: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main loop for sending messages
|
||||||
|
println!("\nAvailable commands:");
|
||||||
|
println!(" join <username> - Join the server");
|
||||||
|
println!(" findmatch - Find a match");
|
||||||
|
println!(" move <from> <to> - Make a move (e.g., move e2 e4)");
|
||||||
|
println!(" chat <message> - Send chat message");
|
||||||
|
println!(" resign - Resign from current game");
|
||||||
|
println!(" quit - Exit client");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
print!("➡️ Enter command: ");
|
||||||
|
io::stdout().flush()?;
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input)?;
|
||||||
|
let input = input.trim();
|
||||||
|
|
||||||
|
if input.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parts: Vec<&str> = input.split_whitespace().collect();
|
||||||
|
let command = parts[0].to_lowercase();
|
||||||
|
|
||||||
|
match command.as_str() {
|
||||||
|
"quit" | "exit" => {
|
||||||
|
println!("👋 Goodbye!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
"join" => {
|
||||||
|
if parts.len() >= 2 {
|
||||||
|
let username = parts[1..].join(" ");
|
||||||
|
let message = ClientMessage::Join { username };
|
||||||
|
send_message(&mut write, &message).await?;
|
||||||
|
} else {
|
||||||
|
println!("Usage: join <username>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"findmatch" | "find" => {
|
||||||
|
let message = ClientMessage::FindMatch;
|
||||||
|
send_message(&mut write, &message).await?;
|
||||||
|
println!("🔍 Searching for a match...");
|
||||||
|
}
|
||||||
|
"move" => {
|
||||||
|
if parts.len() >= 3 {
|
||||||
|
let from = parts[1].to_string();
|
||||||
|
let to = parts[2].to_string();
|
||||||
|
let message = ClientMessage::Move { from, to };
|
||||||
|
send_message(&mut write, &message).await?;
|
||||||
|
println!("♟️ Sent move: {} -> {}", parts[1], parts[2]);
|
||||||
|
} else {
|
||||||
|
println!("Usage: move <from> <to> (e.g., move e2 e4)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"chat" => {
|
||||||
|
if parts.len() >= 2 {
|
||||||
|
let text = parts[1..].join(" ");
|
||||||
|
let message = ClientMessage::Chat { text };
|
||||||
|
send_message(&mut write, &message).await?;
|
||||||
|
} else {
|
||||||
|
println!("Usage: chat <message>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"resign" => {
|
||||||
|
let message = ClientMessage::Resign;
|
||||||
|
send_message(&mut write, &message).await?;
|
||||||
|
println!("Resigned from current game");
|
||||||
|
}
|
||||||
|
"help" => {
|
||||||
|
print_help();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"Unknown command: {}. Type 'help' for available commands.",
|
||||||
|
command
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
read_handle.abort();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_message(
|
||||||
|
write: &mut futures_util::stream::SplitSink<
|
||||||
|
tokio_tungstenite::WebSocketStream<
|
||||||
|
tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
|
||||||
|
>,
|
||||||
|
Message,
|
||||||
|
>,
|
||||||
|
message: &ClientMessage,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let json = serde_json::to_string(message)?;
|
||||||
|
write.send(Message::Text(json)).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_help() {
|
||||||
|
println!("\n📖 Available Commands:");
|
||||||
|
println!(" join <username> - Register with a username");
|
||||||
|
println!(" findmatch - Enter matchmaking queue");
|
||||||
|
println!(" move <from> <to> - Make a chess move");
|
||||||
|
println!(" chat <message> - Send chat to opponent");
|
||||||
|
println!(" resign - Resign from current game");
|
||||||
|
println!(" help - Show this help");
|
||||||
|
println!(" quit - Exit the client");
|
||||||
|
println!();
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::connection::ClientEvent::*;
|
||||||
|
use engine::get_available_moves;
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
@@ -26,6 +28,28 @@ pub fn new_waiting_queue() -> WaitingQueue {
|
|||||||
Arc::new(Mutex::new(VecDeque::new()))
|
Arc::new(Mutex::new(VecDeque::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Step {
|
||||||
|
pub from: String,
|
||||||
|
pub to: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
enum ClientEvent {
|
||||||
|
Join { username: String },
|
||||||
|
FindMatch,
|
||||||
|
Move { from: String, to: String },
|
||||||
|
Resign,
|
||||||
|
Chat { text: String },
|
||||||
|
RequestLegalMoves { fen: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct EventResponse {
|
||||||
|
pub response: Result<(), String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PlayerConnection {
|
pub struct PlayerConnection {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
@@ -43,12 +67,6 @@ pub struct GameMatch {
|
|||||||
pub move_history: Vec<String>,
|
pub move_history: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Step {
|
|
||||||
pub from: String,
|
|
||||||
pub to: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message sending utilities
|
// Message sending utilities
|
||||||
pub async fn send_message_to_player(
|
pub async fn send_message_to_player(
|
||||||
connections: &ConnectionMap,
|
connections: &ConnectionMap,
|
||||||
@@ -102,7 +120,6 @@ pub async fn handle_connection(
|
|||||||
connections: ConnectionMap,
|
connections: ConnectionMap,
|
||||||
matches: MatchMap,
|
matches: MatchMap,
|
||||||
waiting_queue: WaitingQueue,
|
waiting_queue: WaitingQueue,
|
||||||
event_system: crate::events::EventSystem,
|
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
use tokio_tungstenite::accept_async;
|
use tokio_tungstenite::accept_async;
|
||||||
|
|
||||||
@@ -141,8 +158,52 @@ pub async fn handle_connection(
|
|||||||
let text = message.to_text()?;
|
let text = message.to_text()?;
|
||||||
println!("Received from {}: {}", player_id, text);
|
println!("Received from {}: {}", player_id, text);
|
||||||
|
|
||||||
// TODO: Parse and handle message with event system
|
let client_data: ClientEvent = serde_json::from_str(text)
|
||||||
// This will be implemented when we integrate the event system
|
.expect("Failed to convert data into json at handle_connection");
|
||||||
|
|
||||||
|
println!("client: {:?}", client_data);
|
||||||
|
|
||||||
|
match client_data {
|
||||||
|
Join { username } => {
|
||||||
|
{
|
||||||
|
let mut conn_map = connections.lock().await;
|
||||||
|
let player = conn_map.get_mut(&player_id).unwrap();
|
||||||
|
player.username = Some(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
//respone to client
|
||||||
|
let response: EventResponse = EventResponse {
|
||||||
|
response: core::result::Result::Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("response: {:?}", response);
|
||||||
|
|
||||||
|
let _ = send_message_to_player(
|
||||||
|
&connections,
|
||||||
|
player_id,
|
||||||
|
&serde_json::to_string(&response).unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
FindMatch => {
|
||||||
|
let mut wait_queue = waiting_queue.lock().await;
|
||||||
|
wait_queue.push_back(player_id.clone());
|
||||||
|
println!("Appended {} to the waiting queue", player_id);
|
||||||
|
println!("queue {:?}", wait_queue);
|
||||||
|
}
|
||||||
|
Move { from, to } => {}
|
||||||
|
RequestLegalMoves { fen } => {
|
||||||
|
let moves = get_available_moves(&fen);
|
||||||
|
let _ = send_message_to_player(
|
||||||
|
&connections,
|
||||||
|
player_id,
|
||||||
|
&serde_json::to_string(&moves).unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
println!("Sent moves to player: {}", player_id);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Step {
|
|
||||||
pub from: String,
|
|
||||||
pub to: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum ClientEvent {
|
|
||||||
Join { username: String },
|
|
||||||
FindMatch,
|
|
||||||
Move { from: String, to: String },
|
|
||||||
Resign,
|
|
||||||
Chat { text: String },
|
|
||||||
RequestLegalMoves { fen: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ServerEvent {
|
|
||||||
PlayerJoined(Uuid, String),
|
|
||||||
PlayerLeft(Uuid),
|
|
||||||
PlayerJoinedQueue(Uuid),
|
|
||||||
PlayerJoinedMatch(Uuid, Uuid), // player_id, match_id
|
|
||||||
PlayerMove(Uuid, Step),
|
|
||||||
PlayerResigned(Uuid),
|
|
||||||
MatchCreated(Uuid, Uuid, Uuid), // match_id, white_id, black_id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EventSystem {
|
|
||||||
sender: mpsc::UnboundedSender<(Uuid, ClientEvent)>,
|
|
||||||
receiver: Arc<Mutex<mpsc::UnboundedReceiver<(Uuid, ClientEvent)>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for EventSystem {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
sender: self.sender.clone(),
|
|
||||||
receiver: Arc::clone(&self.receiver),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventSystem {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let (sender, receiver) = mpsc::unbounded_channel();
|
|
||||||
Self {
|
|
||||||
sender,
|
|
||||||
receiver: Arc::new(Mutex::new(receiver)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_event(
|
|
||||||
&self,
|
|
||||||
player_id: Uuid,
|
|
||||||
event: ClientEvent,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
self.sender.send((player_id, event))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn next_event(&self) -> Option<(Uuid, ClientEvent)> {
|
|
||||||
let mut receiver = self.receiver.lock().await;
|
|
||||||
receiver.recv().await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EventSystem {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_event_system_send_and_receive() {
|
|
||||||
let event_system = EventSystem::new();
|
|
||||||
let player_id = Uuid::new_v4();
|
|
||||||
|
|
||||||
let join_event = ClientEvent::Join {
|
|
||||||
username: "test_user".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let send_result = event_system.send_event(player_id, join_event).await;
|
|
||||||
assert!(send_result.is_ok(), "Should send event successfully");
|
|
||||||
|
|
||||||
let received = event_system.next_event().await;
|
|
||||||
assert!(received.is_some(), "Should receive sent event");
|
|
||||||
|
|
||||||
let (received_id, received_event) = received.unwrap();
|
|
||||||
assert_eq!(received_id, player_id, "Should receive correct player ID");
|
|
||||||
|
|
||||||
match received_event {
|
|
||||||
ClientEvent::Join { username } => {
|
|
||||||
assert_eq!(username, "test_user", "Should receive correct username");
|
|
||||||
}
|
|
||||||
_ => panic!("Should receive Join event"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_event_system_clone() {
|
|
||||||
let event_system1 = EventSystem::new();
|
|
||||||
let event_system2 = event_system1.clone();
|
|
||||||
|
|
||||||
let player_id = Uuid::new_v4();
|
|
||||||
let event = ClientEvent::FindMatch;
|
|
||||||
|
|
||||||
event_system1.send_event(player_id, event).await.unwrap();
|
|
||||||
|
|
||||||
let received = event_system2.next_event().await;
|
|
||||||
assert!(
|
|
||||||
received.is_some(),
|
|
||||||
"Cloned event system should receive events"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
mod connection;
|
mod connection;
|
||||||
mod events;
|
|
||||||
mod matchmaking;
|
mod matchmaking;
|
||||||
|
mod messages;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -14,15 +14,11 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let matches = connection::new_match_map();
|
let matches = connection::new_match_map();
|
||||||
let waiting_queue = connection::new_waiting_queue();
|
let waiting_queue = connection::new_waiting_queue();
|
||||||
|
|
||||||
// Event system for communication between components
|
|
||||||
let event_system = events::EventSystem::new();
|
|
||||||
|
|
||||||
// Start matchmaking background task
|
// Start matchmaking background task
|
||||||
let matchmaker = matchmaking::MatchmakingSystem::new(
|
let matchmaker = matchmaking::MatchmakingSystem::new(
|
||||||
connections.clone(),
|
connections.clone(),
|
||||||
matches.clone(),
|
matches.clone(),
|
||||||
waiting_queue.clone(),
|
waiting_queue.clone(),
|
||||||
event_system.clone(),
|
|
||||||
);
|
);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
matchmaker.run().await;
|
matchmaker.run().await;
|
||||||
@@ -33,17 +29,10 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let connections = connections.clone();
|
let connections = connections.clone();
|
||||||
let matches = matches.clone();
|
let matches = matches.clone();
|
||||||
let waiting_queue = waiting_queue.clone();
|
let waiting_queue = waiting_queue.clone();
|
||||||
let event_system = event_system.clone();
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = connection::handle_connection(
|
if let Err(e) =
|
||||||
stream,
|
connection::handle_connection(stream, connections, matches, waiting_queue).await
|
||||||
connections,
|
|
||||||
matches,
|
|
||||||
waiting_queue,
|
|
||||||
event_system,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
eprintln!("Connection error: {}", e);
|
eprintln!("Connection error: {}", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::connection::{ConnectionMap, GameMatch, MatchMap, WaitingQueue};
|
use crate::connection::{ConnectionMap, GameMatch, MatchMap, WaitingQueue};
|
||||||
use crate::events::EventSystem;
|
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -7,21 +6,14 @@ pub struct MatchmakingSystem {
|
|||||||
connections: ConnectionMap,
|
connections: ConnectionMap,
|
||||||
matches: MatchMap,
|
matches: MatchMap,
|
||||||
waiting_queue: WaitingQueue,
|
waiting_queue: WaitingQueue,
|
||||||
event_system: EventSystem,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchmakingSystem {
|
impl MatchmakingSystem {
|
||||||
pub fn new(
|
pub fn new(connections: ConnectionMap, matches: MatchMap, waiting_queue: WaitingQueue) -> Self {
|
||||||
connections: ConnectionMap,
|
|
||||||
matches: MatchMap,
|
|
||||||
waiting_queue: WaitingQueue,
|
|
||||||
event_system: EventSystem,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
connections,
|
connections,
|
||||||
matches,
|
matches,
|
||||||
waiting_queue,
|
waiting_queue,
|
||||||
event_system,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +106,6 @@ impl MatchmakingSystem {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::events::EventSystem;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::connection::new_connection_map;
|
use crate::connection::new_connection_map;
|
||||||
@@ -126,14 +117,9 @@ mod tests {
|
|||||||
let connections = new_connection_map();
|
let connections = new_connection_map();
|
||||||
let matches = new_match_map();
|
let matches = new_match_map();
|
||||||
let waiting_queue = new_waiting_queue();
|
let waiting_queue = new_waiting_queue();
|
||||||
let event_system = EventSystem::new();
|
|
||||||
|
|
||||||
let matchmaking = MatchmakingSystem::new(
|
let matchmaking =
|
||||||
connections.clone(),
|
MatchmakingSystem::new(connections.clone(), matches.clone(), waiting_queue.clone());
|
||||||
matches.clone(),
|
|
||||||
waiting_queue.clone(),
|
|
||||||
event_system.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let player1 = Uuid::new_v4();
|
let player1 = Uuid::new_v4();
|
||||||
let player2 = Uuid::new_v4();
|
let player2 = Uuid::new_v4();
|
||||||
@@ -172,14 +158,9 @@ mod tests {
|
|||||||
let connections = new_connection_map();
|
let connections = new_connection_map();
|
||||||
let matches = new_match_map();
|
let matches = new_match_map();
|
||||||
let waiting_queue = new_waiting_queue();
|
let waiting_queue = new_waiting_queue();
|
||||||
let event_system = EventSystem::new();
|
|
||||||
|
|
||||||
let matchmaking = MatchmakingSystem::new(
|
let matchmaking =
|
||||||
connections.clone(),
|
MatchmakingSystem::new(connections.clone(), matches.clone(), waiting_queue.clone());
|
||||||
matches.clone(),
|
|
||||||
waiting_queue.clone(),
|
|
||||||
event_system.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let player1 = Uuid::new_v4();
|
let player1 = Uuid::new_v4();
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user