Nicolas Winkler 6 năm trước cách đây
mục cha
commit
89af58d13f
5 tập tin đã thay đổi với 375 bổ sung30 xóa
  1. 10 3
      src/bitboard.rs
  2. 6 3
      src/engine.rs
  3. 6 1
      src/interface.rs
  4. 139 21
      src/movegen.rs
  5. 214 2
      src/search.rs

+ 10 - 3
src/bitboard.rs

@@ -10,7 +10,6 @@ pub type Bitboard = u64;
 pub type Square = u8;
 
 
-
 pub const A_FILE: Bitboard = 0x_8080_8080_8080_8080;
 pub const B_FILE: Bitboard = A_FILE >> 1;
 pub const C_FILE: Bitboard = A_FILE >> 2;
@@ -71,17 +70,25 @@ pub fn bit_at(b: Bitboard, i: i32, j: i32) -> bool {
 }
 
 pub fn set_bit(b: &mut Bitboard, i: i32, j: i32) {
-    *b |= 1 << ((7 - i) * 8 + 7 - j);
+    *b |= 1_u64 << ((7 - i) * 8 + 7 - j);
 }
 
 pub fn set_bit_index(b: &mut Bitboard, index: u32) {
-    *b |= 1 << index;
+    *b |= 1_u64 << index;
 }
 
 pub fn square(b: Bitboard) -> Square {
     b.trailing_zeros() as Square
 }
 
+pub fn square_from_indices(file: u8, row: u8) -> Square {
+    (7 - row) * 8 + 7 - file
+}
+
+pub fn single_bit_board(file: u8, row: u8) -> Bitboard {
+    1_u64 << square_from_indices(file, row)
+}
+
 pub fn print_board(b: Bitboard) -> String {
     (0..8).map(
         |i| (0..8).map(

+ 6 - 3
src/engine.rs

@@ -30,9 +30,12 @@ pub enum InterfaceMsg {
 pub fn run_engine(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) {
     let mut game = Game::default();
     for msg in r {
-        game.pieces[0] = 0x00ff_0000_0000_0100;
-        game.pieces[QUEEN as usize] = 0x0000_0000_0000_0080;
-        game.pieces[10] = 0x4000_0000_0000_0000;
+        //game.pieces[0] = 0x00ff_0000_0000_0100;
+        //game.pieces[QUEEN as usize] = 0x0000_0000_0000_0080;
+        //game.pieces[10] = 0x4000_0000_0000_0000;
+
+        println!("{}", game.beautiful_print());
+
         let moves = generate_moves(&game, WHITE);
         for mov in moves {
             println!("move: {:?}", mov);

+ 6 - 1
src/interface.rs

@@ -1,6 +1,8 @@
 use std::io::{self, BufRead};
 use std::sync::mpsc::{Receiver, Sender};
 
+use std::process::exit;
+
 use engine::{EngineMsg, InterfaceMsg};
 
 
@@ -22,6 +24,7 @@ fn run_command(mut cmd: Vec<&str>, r: &Receiver<InterfaceMsg>, s: &Sender<Engine
             "uci" => cmd_uci(cmd),
             "isready" => cmd_isready(cmd),
             "go" => cmd_go(cmd, r, s),
+            "quit" | "exit" => cmd_quit(cmd, r, s),
             cmd => { println!("unknown command: {}", cmd); }
         }
     }
@@ -41,7 +44,9 @@ fn cmd_go(args: Vec<&str>, r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
     println!("searchin' da game!");
 }
 
-
+fn cmd_quit(args: Vec<&str>, r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
+    exit(0);
+}
 
 
 

+ 139 - 21
src/movegen.rs

@@ -10,6 +10,7 @@ pub const BLACK: Side = true;
 
 pub type PieceType = u8;
 
+pub const NO_PIECE: PieceType = 255;
 pub const PAWN: PieceType = 0;
 pub const KNIGHT: PieceType = 1;
 pub const BISHOP: PieceType = 2;
@@ -32,7 +33,7 @@ pub enum Move {
 }
 
 /**
- * \brief Iterator to serialize bitboard bit for bit
+ * @brief Iterator to serialize bitboard bit for bit
  *
  * Extracts every bit out of the bitboard and returns each one as a separate bitboard.
  */
@@ -43,7 +44,7 @@ impl Iterator for BitboardIterator {
 
     fn next(&mut self) -> Option<Bitboard> {
         if self.board != 0 {
-            let lsb = self.board & (0_u64.wrapping_sub(self.board));
+            let lsb = self.board & (self.board.wrapping_neg());
             self.board ^= lsb;
             //Some(lsb.trailing_zeros())
             Some(lsb)
@@ -57,8 +58,13 @@ impl Iterator for BitboardIterator {
 pub fn generate_moves(game: &Game, side: Side) -> Vec<Move> {
     let mut moves: Vec<Move> = Vec::new();
     generate_pawn_pushes(game, side, &mut moves);
+    generate_pawn_doublepushes(game, side, &mut moves);
     generate_pawn_captures(game, side, &mut moves);
+    generate_knight_moves(game, side, &mut moves);
+    generate_bishop_moves(game, side, &mut moves);
+    generate_rook_moves(game, side, &mut moves);
     generate_queen_moves(game, side, &mut moves);
+    generate_king_moves(game, side, &mut moves);
     return moves;
 }
 
@@ -93,6 +99,26 @@ fn generate_pawn_pushes(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     }
 }
 
+fn generate_pawn_doublepushes(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    let pawns = game.pawns(side) & match side { WHITE => ROW_2, BLACK => ROW_7 };
+    let others = game.get_all_side(!side);
+
+    let moved_pawns =
+        match side {
+            WHITE => north_one(north_one(pawns)),
+            BLACK => south_one(south_one(pawns))
+        };
+
+    let iter = BitboardIterator { board: moved_pawns & !others };
+    for p in iter {
+        let origin = match side {
+            WHITE => south_one(south_one(p)),
+            BLACK => north_one(north_one(p))
+        };
+        move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN });
+    }
+}
+
 fn generate_pawn_captures(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     let pawns = game.pawns(side);
     let others = game.get_all_side(!side);
@@ -126,45 +152,137 @@ fn generate_pawn_captures(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     }
 }
 
+fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    let friends = game.get_all_side(side);
+    let others = game.get_all_side(!side);
+
+    let knights = game.knights(side);
+    let knight_iter = BitboardIterator{ board: knights };
+
+    for k in knight_iter {
+        let targets = BitboardIterator { board: get_knight_targets(square(k)) & !friends };
+        for target in targets {
+            let simple_move = SimpleMove { from: square(k), to: square(target) };
+            move_list.push(Move::Default{ mov: simple_move, piece_type: KNIGHT });
+        }
+    }
+}
+
+fn get_knight_targets(square: Square) -> Bitboard {
+    /// @brief array containing the possible targets for a knight on each square.
+    /// entry at index i is a bitboard with all bits set where a knight on square i can jump
+    const TARGETS: [Bitboard; 64] = [132096, 329728, 659712, 1319424, 2638848, 5277696, 10489856,
+    4202496, 33816580, 84410376, 168886289, 337772578, 675545156, 1351090312, 2685403152,
+    1075839008, 8657044482, 21609056261, 43234889994, 86469779988, 172939559976, 345879119952,
+    687463207072, 275414786112, 2216203387392, 5531918402816, 11068131838464, 22136263676928,
+    44272527353856, 88545054707712, 175990581010432, 70506185244672, 567348067172352,
+    1416171111120896, 2833441750646784, 5666883501293568, 11333767002587136, 22667534005174272,
+    45053588738670592, 18049583422636032, 145241105196122112, 362539804446949376,
+    725361088165576704, 1450722176331153408, 2901444352662306816, 5802888705324613632,
+    11533718717099671552, 4620693356194824192, 288234782788157440, 576469569871282176,
+    1224997833292120064, 2449995666584240128, 4899991333168480256, 9799982666336960512,
+    1152939783987658752, 2305878468463689728, 1128098930098176, 2257297371824128, 4796069720358912,
+    9592139440717824, 19184278881435648, 38368557762871296, 4679521487814656, 9077567998918656];
+
+    TARGETS[square as usize]
+}
+
+fn generate_bishop_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    let friends = game.get_all_side(side);
+    let others = game.get_all_side(!side);
+    
+    generate_sliding_moves(friends, others, game.bishops(side), BISHOP, false, true, move_list);
+}
+
+fn generate_rook_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    let friends = game.get_all_side(side);
+    let others = game.get_all_side(!side);
+    
+    generate_sliding_moves(friends, others, game.rooks(side), ROOK, true, false, move_list);
+}
 
 fn generate_queen_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
     
-    let queens = BitboardIterator { board: game.queens(side) };
+    generate_sliding_moves(friends, others, game.queens(side), QUEEN, true, true, move_list);
+}
+
+fn generate_sliding_moves(friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
+                          straight: bool, diagonal: bool, move_list: &mut Vec<Move>) {
+    let pieces_iter = BitboardIterator { board: pieces };
 
-    for queen in queens {
-        let destinations = generate_queen_destinations(friends, others, queen);
+    for piece in pieces_iter {
+        let destinations = generate_sliding_destinations(friends, others, piece, straight, diagonal);
         let dest_iter = BitboardIterator { board: destinations };
         for dest in dest_iter {
-            let smove = SimpleMove{ from: square(queen), to: square(dest) };
-            move_list.push(Move::Default { mov: smove, piece_type: QUEEN });
+            let smove = SimpleMove{ from: square(piece), to: square(dest) };
+            move_list.push(Move::Default { mov: smove, piece_type: piece_type});
         }
     }
 }
 
-fn generate_queen_destinations(friends: Bitboard, others: Bitboard,
-                               queen: Bitboard) -> Bitboard {
+fn generate_sliding_destinations(friends: Bitboard, others: Bitboard,
+                               piece: Bitboard, straight: bool,
+                               diagonal: bool) -> Bitboard {
     let occupied = friends | others;
 
-    let directions = [north_one, south_one, east_one, west_one,
-                      northeast_one, southeast_one, northwest_one, southwest_one];
-
-    let mut result = 0;
-    for direction in directions.into_iter() {
-        let mut q = queen;
-        loop {
-            q = direction(q);
-            result |= q;
-            if q & (occupied) != 0 || q == 0 {
-                break;
-            }
+    let straights = [north_one, south_one, east_one, west_one];
+    let diagonals = [northeast_one, southeast_one, northwest_one, southwest_one];
+
+    let mut result: Bitboard = 0;
+    if straight {
+        for direction in straights.into_iter() {
+            result |= generate_direction(piece, *direction, occupied);
+        }
+    }
+    if diagonal {
+        for direction in diagonals.into_iter() {
+            result |= generate_direction(piece, *direction, occupied);
         }
     }
 
     return result & !friends;
 }
 
+fn generate_direction(piece: Bitboard, direction: fn(Bitboard) -> Bitboard, occupied: Bitboard) -> Bitboard {
+    let mut result: Bitboard = 0;
+    let mut b = piece;
+    loop {
+        b = direction(b);
+        result |= b;
+        if b & (occupied) != 0 || b == 0 {
+            break;
+        }
+    }
+    return result;
+}
+
+
+fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    let friends = game.get_all_side(side);
+    let others = game.get_all_side(!side);
+
+    let king = game.kings(side);
+
+    assert_eq!(king.count_ones(), 1); // assume only one king per player 
+
+    let area = (north_one(king)
+              | south_one(king)
+              | east_one(king)
+              | west_one(king)
+              | northeast_one(king)
+              | northwest_one(king)
+              | southwest_one(king)
+              | southeast_one(king))
+            & !friends;
+
+    let area_iter = BitboardIterator { board: area };
+    for destination in area_iter {
+        let simple_move = SimpleMove{ from: square(king), to: square(destination) };
+        move_list.push(Move::Default { mov: simple_move, piece_type: KING });
+    }
+}
 
 #[cfg(test)]
 mod tests {

+ 214 - 2
src/search.rs

@@ -1,4 +1,4 @@
-use bitboard::Bitboard;
+use bitboard::*;
 use movegen::*;
 
 
@@ -11,12 +11,77 @@ pub struct Game
 impl Default for Game {
     fn default () -> Game {
         Game {
-            pieces: [0; 12],
+            pieces: [
+                ROW_2,
+                0x42,
+                0x24,
+                0x81,
+                0x10,
+                0x08,
+                ROW_7,
+                0x42 << 56,
+                0x24 << 56,
+                0x81 << 56,
+                0x10 << 56,
+                0x08 << 56,
+            ],
             en_passant: 0,
         }
     }
 }
+
 impl Game {
+
+    pub fn from_fen(fen: &str) -> Option<Game> {
+        let mut game: Game = Game::default();
+        let example = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+        let mut fen_parts = fen.split_whitespace();
+
+        match fen_parts.next() {
+            Some(position) => {
+                let rows = position.split('/');
+                let mut row_index = 0;
+                for row in rows {
+                    if row_index >= 8 {
+                        return None;
+                    }
+                    let mut col_index = 0;
+                    for c in row.chars() {
+                        
+                        if col_index >= 8 {
+                            return None;
+                        }
+
+                        let bit_to_set = single_bit_board(col_index, row_index);
+                        match c {
+                            'p' => { *game.pawns_mut(BLACK) |= bit_to_set },
+                            'k' => { *game.knights_mut(BLACK) |= bit_to_set },
+                            'b' => { *game.bishops_mut(BLACK) |= bit_to_set },
+                            'r' => { *game.rooks_mut(BLACK) |= bit_to_set },
+                            'q' => { *game.queens_mut(BLACK) |= bit_to_set },
+                            'k' => { *game.kings_mut(BLACK) |= bit_to_set },
+                            'P' => { *game.pawns_mut(WHITE) |= bit_to_set },
+                            'K' => { *game.knights_mut(WHITE) |= bit_to_set },
+                            'B' => { *game.bishops_mut(WHITE) |= bit_to_set },
+                            'R' => { *game.rooks_mut(WHITE) |= bit_to_set },
+                            'Q' => { *game.queens_mut(WHITE) |= bit_to_set },
+                            'K' => { *game.kings_mut(WHITE) |= bit_to_set },
+                            num => {
+                                col_index += match num.to_digit(10) { Some(x) => x, None => {
+                                    return None;
+                                }} as u8 - 1;
+                            }
+                        }
+                        col_index += 1;
+                    }
+                }
+            },
+            None => return None
+        }
+
+        None
+    }
+
     pub fn bitboard(&self, pt: PieceType, side: Side) -> Bitboard {
         if side == BLACK {
             self.pieces[(pt + 6) as usize]
@@ -74,6 +139,48 @@ impl Game {
         }
     }
 
+    pub fn pawns_mut(&mut self, side: Side) -> &mut Bitboard {
+        match side {
+            WHITE => &mut self.pieces[0],
+            BLACK => &mut self.pieces[6],
+        }
+    }
+
+    pub fn knights_mut(&mut self, side: Side) -> &mut Bitboard {
+        match side {
+            WHITE => &mut self.pieces[1],
+            BLACK => &mut self.pieces[7],
+        }
+    }
+
+    pub fn bishops_mut(&mut self, side: Side) -> &mut Bitboard {
+        match side {
+            WHITE => &mut self.pieces[2],
+            BLACK => &mut self.pieces[8],
+        }
+    }
+
+    pub fn rooks_mut(&mut self, side: Side) -> &mut Bitboard {
+        match side {
+            WHITE => &mut self.pieces[3],
+            BLACK => &mut self.pieces[9],
+        }
+    }
+
+    pub fn queens_mut(&mut self, side: Side) -> &mut Bitboard {
+        match side {
+            WHITE => &mut self.pieces[4],
+            BLACK => &mut self.pieces[10],
+        }
+    }
+
+    pub fn kings_mut(&mut self, side: Side) -> &mut Bitboard {
+        match side {
+            WHITE => &mut self.pieces[5],
+            BLACK => &mut self.pieces[11],
+        }
+    }
+
     pub fn get_all_side(&self, side: Side) -> Bitboard {
         match side {
             WHITE => self.pieces[0] | self.pieces[1] | self.pieces[2] | self.pieces[3] |
@@ -82,6 +189,111 @@ impl Game {
                 self.pieces[10] | self.pieces[11]
         }
     }
+
+    pub fn get_square(&self, square: Square) -> (PieceType, Side) {
+        let square_mask = 1 << square;
+        for i in 0..6 {
+            if self.pieces[i] & square_mask != 0 {
+                return (i as PieceType, WHITE);
+            }
+            if self.pieces[i + 6] & square_mask != 0 {
+                return (i as PieceType, BLACK);
+            }
+        }
+        return (NO_PIECE, WHITE);
+    }
+
+    /**
+     * @brief creates a unicode string containing the board
+     *
+     * Example:
+     *
+     *   ┌───┬───┬───┬───┬───┬───┬───┬───┐
+     * 8 │ ♜ │ ♞ │ ♝ │ ♛ │ ♚ │ ♝ │ ♞ │ ♜ │
+     *   ├───┼───┼───┼───┼───┼───┼───┼───┤
+     * 7 │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │ ♟ │
+     *   ├───┼───┼───┼───┼───┼───┼───┼───┤
+     * 6 │   │   │   │   │   │   │   │   │
+     *   ├───┼───┼───┼───┼───┼───┼───┼───┤
+     * 5 │   │   │   │   │   │   │   │   │
+     *   ├───┼───┼───┼───┼───┼───┼───┼───┤
+     * 4 │   │   │   │   │   │   │   │   │
+     *   ├───┼───┼───┼───┼───┼───┼───┼───┤
+     * 3 │   │   │   │   │   │   │   │   │
+     *   ├───┼───┼───┼───┼───┼───┼───┼───┤
+     * 2 │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │ ♙ │
+     *   ├───┼───┼───┼───┼───┼───┼───┼───┤
+     * 1 │ ♖ │ ♘ │ ♗ │ ♕ │ ♔ │ ♗ │ ♘ │ ♖ │
+     *   └───┴───┴───┴───┴───┴───┴───┴───┘
+     *     a   b   c   d   e   f   g   h
+     *
+     */
+    pub fn beautiful_print(&self) -> String {
+        let chars = ['─', '│', '┌', '┐', '└', '┘', '├', '┤', '┬', '┴', '┼'];
+        let figures = ['♙', '♘', '♗', '♖', '♕', '♔', '♟', '♞', '♝', '♜', '♛', '♚'];
+        let mut board: String = String::new();
+
+        #[derive(Copy, Clone)]
+        struct GridLine {
+            left: char,
+            center: char,
+            right: char,
+        }
+
+        let top = GridLine { left: chars[2], center: chars[8], right: chars[3] };
+        let center = GridLine { left: chars[6], center: chars[10], right: chars[7] };
+        let bot = GridLine { left: chars[4], center: chars[9], right: chars[5] };
+
+        let generate_line = |gl: GridLine| {
+            let mut line = String::new();
+            for i in 0..33 {
+                match i {
+                    0 => { line.push(gl.left) }
+                    32 => { line.push(gl.right) }
+                    x => { line.push(if x % 4 == 0 { gl.center } else { chars[0] }) }
+                }
+            }
+            return line;
+        };
+
+        let piece_line = |row: u8| {
+            let mut line = String::new();
+            line.push(chars[1]);
+            line.push(' ');
+            for i in 0..8 {
+                let (pt, side) = self.get_square(square_from_indices(i, row));
+                if pt == NO_PIECE {
+                    line.push(' ');
+                }
+                else {
+                    let fig_index = pt as usize + if side == BLACK { 6 } else { 0 };
+                    line.push(figures[fig_index]);
+                }
+                line.push(' '); line.push(chars[1]); line.push(' ');
+            }
+            return line;
+        };
+
+        board.push_str("  ");
+        board.push_str(&generate_line(top));
+        board.push('\n');
+        for i in 0..8 {
+            board.push_str(&(8 - i).to_string());
+            board.push(' ');
+            board.push_str(&piece_line(i));
+            board.push('\n');
+            if i != 7 {
+                board.push_str("  ");
+                board.push_str(&generate_line(center));
+                board.push('\n');
+            }
+        }
+        board.push_str("  ");
+        board.push_str(&generate_line(bot));
+        board.push_str("\n    a   b   c   d   e   f   g   h");
+
+        return board;
+    }
 }
 
 pub fn search() {