diff --git a/.github/workflows/dispatcher.yml b/.github/workflows/dispatcher.yml index fd0a427..9f93a3f 100644 --- a/.github/workflows/dispatcher.yml +++ b/.github/workflows/dispatcher.yml @@ -77,7 +77,6 @@ jobs: release: - needs: test-data-upload if: github.ref == 'refs/heads/master' uses: ./.github/workflows/release.yml secrets: inherit diff --git a/engine/src/bitboard.rs b/engine/src/bitboard.rs index 9779d93..7da5b04 100644 --- a/engine/src/bitboard.rs +++ b/engine/src/bitboard.rs @@ -3,8 +3,8 @@ mod utils; mod legality; mod checkinfo; mod attacks; -mod movebuffer; mod movegen; pub mod board; -pub(in super) mod bitmove; \ No newline at end of file +pub(in super) mod bitmove; +pub(in super) mod movebuffer; \ No newline at end of file diff --git a/engine/src/boardsquare.rs b/engine/src/boardsquare.rs index 2e0b57f..3682c51 100644 --- a/engine/src/boardsquare.rs +++ b/engine/src/boardsquare.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct BoardSquare { pub x: usize, pub y: usize, diff --git a/engine/src/chessmove.rs b/engine/src/chessmove.rs index 4fa1319..ec14ef2 100644 --- a/engine/src/chessmove.rs +++ b/engine/src/chessmove.rs @@ -4,7 +4,7 @@ use super::boardsquare::BoardSquare; use super::piecetype::PieceType; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] /*pub struct ChessMove { pub move_type: MoveType, pub piece_type: PieceType, @@ -94,7 +94,7 @@ impl ChessMove { }; } - pub(in super) fn from_bitmove(bitmove: &BitMove, board: Board) -> Self { + pub(in super) fn from_bitmove(bitmove: &BitMove, board: &Board) -> Self { match bitmove.move_type() { BitMoveType::Quiet => { let from_square_index = bitmove.from_square(); @@ -128,10 +128,6 @@ impl ChessMove { let king_type = PieceType::from_index(board.piece_board(from_square_index)); let king_from = BoardSquare::from_index(from_square_index); let king_to = BoardSquare::from_index(to_square_index); - let promotion_piece = match bitmove.promotion_piece() { - Some(piece) => Some(PieceType::from_index(piece)), - None => None - }; let rook_type = if bitmove.from_square() < 32 { PieceType::WhiteRook } else { PieceType::BlackRook }; let rook_from_index = if bitmove.to_square() > bitmove.from_square() { bitmove.from_square() + 3 diff --git a/engine/src/gameend.rs b/engine/src/gameend.rs index 9238ea2..8212700 100644 --- a/engine/src/gameend.rs +++ b/engine/src/gameend.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Debug)] pub enum GameEnd { WhiteWon(String), BlackWon(String), diff --git a/engine/src/lib.rs b/engine/src/lib.rs index b7bc67b..f3f6d39 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -7,18 +7,204 @@ pub mod gameend; use chessmove::ChessMove; use gameend::GameEnd; +use bitboard::board::Board; +use bitboard::movebuffer::MoveBuffer; pub fn get_available_moves(fen: &str) -> Vec { - println!("get_available_moves answered"); - return vec![]; + let mut board = Board::build(fen); + let mut buffer = MoveBuffer::new(); + let mut temp_buffer = MoveBuffer::new(); + let mut generated_moves: Vec = vec![]; + + board.collect_moves(&mut buffer, &mut temp_buffer); + + for idx in 0..buffer.count() { + generated_moves.push(ChessMove::from_bitmove(buffer.get(idx), &board)); + } + + println!("get_available_moves resulted in {} moves", generated_moves.len()); + return generated_moves; } pub fn is_game_over(fen: &str) -> Option { + let mut board = Board::build(fen); + let mut buffer = MoveBuffer::new(); + let mut temp_buffer = MoveBuffer::new(); + let in_check = board.collect_moves(&mut buffer, &mut temp_buffer); + println!("is_game_over answered"); - return None; + if buffer.count() > 0 { + return None; + } + if !in_check { + return Some(GameEnd::Draw("".to_string())); + } + return if board.side_to_move() == 0 { Some(GameEnd::BlackWon("".to_string())) } else { Some(GameEnd::WhiteWon("".to_string())) }; } pub fn get_board_after_move(fen: &str, chess_move: &ChessMove) -> String { println!("get_board_after_move answered"); return String::from("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); +} + +#[cfg(test)] +mod tests { + use crate::boardsquare::BoardSquare; + use crate::piecetype::PieceType::*; + use crate::gameend::GameEnd; + + use super::*; + + impl PartialEq for ChessMove { + fn eq(&self, other: &Self) -> bool { + canonical(self) == canonical(other) + } + } + impl Eq for ChessMove { + + } + impl Ord for ChessMove { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let lhs = canonical(self); + let rhs = canonical(other); + lhs.cmp(&rhs) + } + } + impl PartialOrd for ChessMove { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl PartialEq for GameEnd { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (GameEnd::WhiteWon(a), GameEnd::WhiteWon(b)) => a == b, + (GameEnd::BlackWon(a), GameEnd::BlackWon(b)) => a == b, + (GameEnd::Draw(a), GameEnd::Draw(b)) => a == b, + _ => false, + } + } +} + + fn canonical(m: &ChessMove) -> (u8, u8, u8) { + match m { + ChessMove::Quiet { piece_type, from_square, to_square, promotion_piece } => + (0, from_square.to_index(), to_square.to_index()), + ChessMove::Capture { piece_type, from_square, to_square, captured_piece, promotion_piece } => + (1, from_square.to_index(), to_square.to_index()), + ChessMove::Castle { king_type, king_from, king_to, rook_type, rook_from, rook_to } => + (2, king_from.to_index(), king_to.to_index()), + ChessMove::EnPassant { pawn_type, from_square, to_square, captured_piece, captured_from } => + (3, from_square.to_index(), to_square.to_index()), + } + } + + #[test] + fn get_available_moves_test() { + let boards: [&str; 2] = [ + "rnbqkbnr/pppppppp/8/1B6/4P3/5P1N/PPPP2PP/RNBQK2R w KQkq e6 0 1", + "6Bn/B2Pk3/8/p1r3NK/3p4/b6P/3p2n1/2R5 w - - 0 1" + ]; + let mut expected_moves: Vec> = vec![ + vec![ + ChessMove::capture(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(3, 6), BlackPawn, None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(0, 1), BoardSquare::from_coord(0, 2), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(0, 1), BoardSquare::from_coord(0, 3), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(1, 1), BoardSquare::from_coord(1, 2), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(1, 1), BoardSquare::from_coord(1, 3), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(2, 1), BoardSquare::from_coord(2, 2), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(2, 1), BoardSquare::from_coord(2, 3), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(3, 1), BoardSquare::from_coord(3, 2), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(3, 1), BoardSquare::from_coord(3, 3), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(4, 3), BoardSquare::from_coord(4, 4), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(5, 2), BoardSquare::from_coord(5, 3), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(6, 1), BoardSquare::from_coord(6, 2), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(6, 1), BoardSquare::from_coord(6, 3), None), + ChessMove::quiet(WhiteKnight, BoardSquare::from_coord(1, 0), BoardSquare::from_coord(0, 2), None), + ChessMove::quiet(WhiteKnight, BoardSquare::from_coord(1, 0), BoardSquare::from_coord(2, 2), None), + ChessMove::quiet(WhiteKnight, BoardSquare::from_coord(7, 2), BoardSquare::from_coord(6, 0), None), + ChessMove::quiet(WhiteKnight, BoardSquare::from_coord(7, 2), BoardSquare::from_coord(5, 1), None), + ChessMove::quiet(WhiteKnight, BoardSquare::from_coord(7, 2), BoardSquare::from_coord(5, 3), None), + ChessMove::quiet(WhiteKnight, BoardSquare::from_coord(7, 2), BoardSquare::from_coord(6, 4), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(5, 0), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(4, 1), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(3, 2), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(2, 3), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(0, 3), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(0, 5), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(1, 4), BoardSquare::from_coord(2, 5), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(7, 0), BoardSquare::from_coord(6, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(7, 0), BoardSquare::from_coord(5, 0), None), + ChessMove::quiet(WhiteQueen, BoardSquare::from_coord(3, 0), BoardSquare::from_coord(4, 1), None), + ChessMove::quiet(WhiteKing, BoardSquare::from_coord(4, 0), BoardSquare::from_coord(4, 1), None), + ChessMove::quiet(WhiteKing, BoardSquare::from_coord(4, 0), BoardSquare::from_coord(5, 1), None), + ChessMove::quiet(WhiteKing, BoardSquare::from_coord(4, 0), BoardSquare::from_coord(5, 0), None), + ChessMove::castle(WhiteKing, BoardSquare::from_coord(4, 0), BoardSquare::from_coord(6, 0), WhiteRook, BoardSquare::from_coord(7, 0), BoardSquare::from_coord(5, 0)) + ], + vec![ + ChessMove::capture(WhiteBishop, BoardSquare::from_coord(0, 6), BoardSquare::from_coord(2, 4), BlackRook, None), + ChessMove::capture(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(2, 4), BlackRook, None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(7, 2), BoardSquare::from_coord(7, 3), None), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(3, 6), BoardSquare::from_coord(3, 7), Some(WhiteQueen)), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(3, 6), BoardSquare::from_coord(3, 7), Some(WhiteRook)), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(3, 6), BoardSquare::from_coord(3, 7), Some(WhiteBishop)), + ChessMove::quiet(WhitePawn, BoardSquare::from_coord(3, 6), BoardSquare::from_coord(3, 7), Some(WhiteKnight)), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(0, 6), BoardSquare::from_coord(1, 5), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(0, 6), BoardSquare::from_coord(1, 7), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(6, 7), BoardSquare::from_coord(7, 6), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(6, 7), BoardSquare::from_coord(5, 6), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(6, 7), BoardSquare::from_coord(4, 5), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(6, 7), BoardSquare::from_coord(3, 4), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(6, 7), BoardSquare::from_coord(2, 3), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(6, 7), BoardSquare::from_coord(1, 2), None), + ChessMove::quiet(WhiteBishop, BoardSquare::from_coord(6, 7), BoardSquare::from_coord(0, 1), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(0, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(1, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(3, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(4, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(5, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(6, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(7, 0), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(2, 1), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(2, 2), None), + ChessMove::quiet(WhiteRook, BoardSquare::from_coord(2, 0), BoardSquare::from_coord(2, 3), None), + ChessMove::quiet(WhiteKing, BoardSquare::from_coord(7, 4), BoardSquare::from_coord(6, 3), None), + ChessMove::quiet(WhiteKing, BoardSquare::from_coord(7, 4), BoardSquare::from_coord(7, 5), None) + ] + ]; + + for case in 0..2 { + + let mut generated_moves = get_available_moves(boards[case]); + + generated_moves.sort(); + expected_moves[case].sort(); + assert_eq!(generated_moves.len(), expected_moves[case].len()); + assert_eq!(generated_moves, expected_moves[case]); + } + } + + #[test] + fn is_game_over_test() { + + let boards: [&str; 4] = [ + "2k5/3pn3/2pP4/1R1P3B/1Np5/3RPp2/1B6/6Kb w - - 0 1", + "2K3B1/4P3/8/7p/4pPn1/1N1P1p1p/4bp2/2Rk4 b - - 0 1", + "6N1/B2PP3/pR1b4/3P2nb/6P1/3P1k2/2p5/4r1K1 w - - 0 1", + "3n1K2/p2k1p2/5P2/b1p2P2/P7/8/3p2r1/8 w - - 0 1" + ]; + let expected_results: [Option; 4] = [ + None, + Some(GameEnd::WhiteWon("".to_string())), + Some(GameEnd::BlackWon("".to_string())), + Some(GameEnd::Draw("".to_string())) + ]; + + for case in 0..4 { + let fen = boards[case]; + let actual = is_game_over(fen); + assert_eq!(actual, expected_results[case]); + } + } } \ No newline at end of file diff --git a/engine/src/piecetype.rs b/engine/src/piecetype.rs index 92a7e7b..7a6fc5f 100644 --- a/engine/src/piecetype.rs +++ b/engine/src/piecetype.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub enum PieceType { WhitePawn, WhiteKnight,