Przeglądaj źródła

implementing first version of faster movegen

Nicolas Winkler 2 lat temu
rodzic
commit
2ab89276cb
4 zmienionych plików z 378 dodań i 5 usunięć
  1. 3 2
      src/engine.rs
  2. 371 1
      src/movegen.rs
  3. 2 1
      src/search.rs
  4. 2 1
      src/uci.rs

+ 3 - 2
src/engine.rs

@@ -154,6 +154,7 @@ impl Engine {
 
 
     fn set_position(&mut self, pos: Board, moves: &Vec<String>) {
+        generate_legal_moves_new(&mut pos.clone(), WHITE, false);
         self.board = pos;
 
         // ensure correct zobrist hash
@@ -198,9 +199,9 @@ impl Engine {
                         let after = Instant::now();
                         let nodes = pc.nodes;
                         let duration = after - before;
-                        let nps = nodes / duration.as_millis() as usize * 1000;
+                        let nps = (nodes * 1000).checked_div(duration.as_millis() as _);
                         println!("finished in {} ms", duration.as_millis());
-                        println!("nodes: {} nps: {}", nodes, nps);
+                        println!("nodes: {} nps: {}", nodes, nps.unwrap_or(0));
                     }
                     rv
                 };

+ 371 - 1
src/movegen.rs

@@ -8,6 +8,7 @@ use std::iter::Iterator;
 use std::sync::Arc;
 use std::sync::Mutex;
 
+use crate::bitboard;
 use crate::magic::magic_bishop;
 use crate::search::CountermoveTable;
 use crate::ttable::CacheEntry;
@@ -293,7 +294,7 @@ impl MoveGenerator {
     pub fn generate_legal_moves(game: &mut Board, ce: Option<&CacheEntry>, killers: &[Move], last_move: Option<Move>, counters_table: Arc<Mutex<CountermoveTable>>, side: Side) -> Self {
         MoveGenerator {
             board: game.clone(),
-            moves: generate_legal_moves(game, side, false),
+            moves: generate_legal_moves_new(game, side, false),
             side,
             captures: Vec::with_capacity(32),
             counters: Vec::with_capacity(32),
@@ -542,6 +543,369 @@ fn pinned_rays(king: Bitboard, friends: Bitboard, diag_enemies: Bitboard, straig
 }
 
 
+pub fn generate_legal_moves_new(board: &mut Board, side: Side, captures_only: bool) -> Vec<Move> {
+    let king = board.kings(side);
+    let king_square = square(king);
+
+    let friends = board.get_all_side(side);
+    let opponents = board.get_all_side(!side);
+
+    if king == 0 {
+        return Vec::new();
+    }
+
+    let all_pieces = friends | opponents;
+
+    let king_knights = get_knight_targets(king_square);
+    let king_bishops = crate::magic::magic_bishop(king_square, all_pieces);
+    let king_rooks = crate::magic::magic_rook(king_square, all_pieces);
+    
+    let king_pawns =
+        if side == BLACK    { southeast_one(king) | southwest_one(king) }
+        else                { northeast_one(king) | northwest_one(king) };
+
+    let king_kings = get_king_targets(king);
+
+    let check_doers =
+              (king_knights & board.knights(!side))
+            | (king_bishops & (board.bishops(!side) | board.queens(!side)))
+            | (king_rooks & (board.rooks(!side) | board.queens(!side)))
+            | (king_pawns & board.pawns(!side))
+            | (king_kings & board.kings(!side));
+
+
+    if check_doers != 0 {
+        return generate_legal_moves(board, side, captures_only);
+    }
+
+    /*let check_mask = 
+        if check_doers.count_ones() == 0 {
+            !0u64
+        }
+        else if check_doers.count_ones() == 1 {
+            return generate_legal_moves(board, side, captures_only);
+        } else {
+            return generate_legal_moves(board, side, captures_only);
+        };*/
+
+
+    let king_diag_all = crate::magic::magic_bishop(king_square, all_pieces);
+    let king_straight_all = crate::magic::magic_rook(king_square, all_pieces);
+
+    let diag_pin_mask = crate::magic::magic_bishop(king_square, opponents);
+    let straight_pin_mask = crate::magic::magic_rook(king_square, opponents);
+
+    let mut diag_attack: Bitboard = 0;
+    let mut straight_attack: Bitboard = 0;
+
+    for dg in BitboardIterator((board.queens(!side) | board.bishops(!side)) & diag_pin_mask) {
+        diag_attack |= crate::magic::magic_bishop(square(dg), all_pieces) | dg;
+    }
+
+    for sg in BitboardIterator((board.queens(!side) | board.rooks(!side)) & straight_pin_mask) {
+        straight_attack |= crate::magic::magic_rook(square(sg), all_pieces) | sg;
+    }
+
+    let diag_pinned_pieces = diag_attack & king_diag_all;
+    let straight_pinned_pieces = straight_attack & king_straight_all;
+    let all_pinned_pieces = diag_pinned_pieces | straight_pinned_pieces;
+
+    //let diag_pins = crate::magic::magic_bishop(king_square, all_pieces & !king_diagonals);
+    //let straight_pins = crate::magic::magic_rook(king_square, all_pieces & !king_straights);
+    // let straight_pins = 0u64;//crate::magic::magic_rook(king_square, all_pieces & !king_straights);
+
+    /*println!("{}", board.beautiful_print());
+    println!("diag_pins:\n{}", bitboard::print_board(diag_pins));
+    println!("straight_pins:\n{}", bitboard::print_board(straight_pins));*/
+
+    let pawns = board.pawns(side);
+    let knights = board.knights(side);
+    let bishops = board.bishops(side);
+    let rooks = board.rooks(side);
+    let queens = board.queens(side);
+
+    let pawns_straight_pinned = pawns & straight_pinned_pieces;
+    let pawns_diag_pinned = pawns & diag_pinned_pieces;
+    let pawns_unpinned = pawns & !all_pinned_pieces;
+
+    let _knights_pinned= knights & all_pinned_pieces; // pinned knights can never move
+    let knights_unpinned = knights & !all_pinned_pieces;
+    
+    let bishops_pinned = bishops & diag_pinned_pieces;
+    let bishops_unpinned = bishops & !all_pinned_pieces;
+
+    let rooks_pinned = rooks & straight_pinned_pieces;
+    let rooks_unpinned = rooks & !all_pinned_pieces;
+
+    let queens_diag_pinned = queens & diag_pinned_pieces;
+    let queens_straight_pinned = queens & straight_pinned_pieces;
+    let queens_unpinned = queens & !all_pinned_pieces;
+
+    let mut moves: Vec<Move>= Vec::with_capacity(64);
+    let mut testmoves = Vec::<Move>::with_capacity(64);
+
+    let promotion_mask = ROW_1 | ROW_8;
+    for pawn in BitboardIterator(pawns_diag_pinned | pawns_unpinned) {
+        let psquare = square(pawn);
+        let pc1 = if side == WHITE { northeast_one(pawn) } else { southeast_one(pawn) };
+        let pc2 = if side == WHITE { northwest_one(pawn) } else { southwest_one(pawn) };
+        for cap in [pc1, pc2] {
+            if pawn & pawns_diag_pinned != 0 {
+                if cap & diag_pin_mask == 0 {
+                    continue;
+                }
+            }
+            let capture = cap & opponents;
+            if capture != 0 {
+                if capture & promotion_mask != 0 {
+                    moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(capture) } , pc: PieceCaptureType::new(QUEEN, board.find_piece(capture)) });
+                    moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(capture) } , pc: PieceCaptureType::new(ROOK, board.find_piece(capture)) });
+                    moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(capture) } , pc: PieceCaptureType::new(BISHOP, board.find_piece(capture)) });
+                    moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(capture) } , pc: PieceCaptureType::new(KNIGHT, board.find_piece(capture)) });
+                }
+                else {
+                    moves.push(Move::Default { mov: SimpleMove { from: psquare, to: square(capture) } , pc: PieceCaptureType::new(PAWN, board.find_piece(capture)) });
+                }
+            }
+        }
+    }
+
+    let pushed_pawns = if side == WHITE { north_one(pawns_unpinned) } else { south_one(pawns_unpinned) } & !all_pieces;
+    for pawn in BitboardIterator(pushed_pawns) {
+        let tsquare = square(pawn);
+        let origin = if side == BLACK { north_one(pawn) } else { south_one(pawn) };
+        let psquare = square(origin);
+
+        if pawn & promotion_mask != 0 {
+            moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: tsquare }, pc: PieceCaptureType::new(QUEEN, None) });
+            moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: tsquare }, pc: PieceCaptureType::new(ROOK, None) });
+            moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: tsquare }, pc: PieceCaptureType::new(BISHOP, None) });
+            moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: tsquare }, pc: PieceCaptureType::new(KNIGHT, None) });
+        }
+        else {
+            moves.push(Move::Default { mov: SimpleMove { from: psquare, to: tsquare }, pc: PieceCaptureType::new(PAWN, None) });
+        }
+    }
+
+    for pawn in BitboardIterator(pawns_straight_pinned) {
+        let psquare = square(pawn);
+        let pushed_pawn = if side == WHITE { north_one(pawn) } else { south_one(pawn) };
+        if pushed_pawn & straight_pin_mask == 0 {
+            continue;
+        }
+        if pushed_pawn & all_pieces == 0 {
+            if pushed_pawn & promotion_mask != 0 {
+                moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(pushed_pawn) }, pc: PieceCaptureType::new(QUEEN, None) });
+                moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(pushed_pawn) }, pc: PieceCaptureType::new(ROOK, None) });
+                moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(pushed_pawn) }, pc: PieceCaptureType::new(BISHOP, None) });
+                moves.push(Move::Promotion { mov: SimpleMove { from: psquare, to: square(pushed_pawn) }, pc: PieceCaptureType::new(KNIGHT, None) });
+            }
+            else {
+                moves.push(Move::Default { mov: SimpleMove { from: psquare, to: square(pushed_pawn) }, pc: PieceCaptureType::new(PAWN, None) });
+            }
+        }
+    }
+
+    let double_push_row = if side == WHITE { ROW_2 } else { ROW_7 };
+    let double_pushables = (pawns_straight_pinned | pawns_unpinned) & double_push_row;
+    let dpushed_pawns = if side == WHITE { north_one(north_one(double_pushables) & !all_pieces) } else { south_one(south_one(double_pushables) & !all_pieces) } & !all_pieces;
+    for pawn in BitboardIterator(dpushed_pawns) {
+        let psquare = square(pawn);
+        let origin = if side == BLACK { north_one(north_one(pawn)) } else { south_one(south_one(pawn)) };
+
+        if (origin & straight_pinned_pieces != 0) && (pawn & straight_pin_mask == 0) {
+            continue;
+        }
+        moves.push(Move::Default { mov: SimpleMove { from: square(origin), to: psquare }, pc: PieceCaptureType::new(PAWN, None) });
+    }
+
+    /*for pawn in BitboardIterator(pawns_diag_pinned & !promote_row) {
+        let psquare = square(pawn);
+        let pc1 = if side == WHITE { northeast_one(pawn) } else { southeast_one(pawn) };
+        let pc2 = if side == WHITE { northwest_one(pawn) } else { southwest_one(pawn) };
+        if pc1 & diag_pins & opponents != 0 {
+            moves.push(Move::Default { mov: SimpleMove { from: psquare, to: square(pc1) } , pc: PieceCaptureType::new(PAWN, board.find_piece(pc1)) });
+        }
+        if pc2 & diag_pins & opponents != 0 {
+            moves.push(Move::Default { mov: SimpleMove { from: psquare, to: square(pc2) } , pc: PieceCaptureType::new(PAWN, board.find_piece(pc2)) });
+        }
+    }*/
+
+    generate_knight_moves_new(board, friends, opponents, knights_unpinned, &mut moves, captures_only);
+
+    for bishop in BitboardIterator(bishops_pinned) {
+        let bsq = square(bishop);
+        let pt = crate::magic::magic_bishop(bsq, all_pieces) & !friends & diag_pin_mask;
+        for target in BitboardIterator(pt & opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: bsq, to: square(target) } , pc: PieceCaptureType::new(BISHOP, board.find_piece(target)) });
+        }
+        for target in BitboardIterator(pt & !opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: bsq, to: square(target) } , pc: PieceCaptureType::new(BISHOP, None) });
+        }
+    }
+
+    for bishop in BitboardIterator(bishops_unpinned) {
+        let bsq = square(bishop);
+        let pt = crate::magic::magic_bishop(bsq, all_pieces) & !friends;
+        for target in BitboardIterator(pt & opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: bsq, to: square(target) } , pc: PieceCaptureType::new(BISHOP, board.find_piece(target)) });
+        }
+        for target in BitboardIterator(pt & !opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: bsq, to: square(target) } , pc: PieceCaptureType::new(BISHOP, None) });
+        }
+    }
+
+    for rook in BitboardIterator(rooks_pinned | rooks_unpinned) {
+        let rsq = square(rook);
+        let pt = crate::magic::magic_rook(rsq, all_pieces) & !friends &
+            if rook & rooks_pinned != 0 {
+                straight_pin_mask
+            } else {
+                !0u64
+            };
+        for target in BitboardIterator(pt & opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: rsq, to: square(target) } , pc: PieceCaptureType::new(ROOK, board.find_piece(target)) });
+        }
+        for target in BitboardIterator(pt & !opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: rsq, to: square(target) } , pc: PieceCaptureType::new(ROOK, None) });
+        }
+    }
+
+    for dqueen in BitboardIterator(queens_diag_pinned) {
+        let qsq = square(dqueen);
+        let pt = crate::magic::magic_bishop(qsq, all_pieces) & !friends & diag_pin_mask;
+        for target in BitboardIterator(pt & opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: qsq, to: square(target) } , pc: PieceCaptureType::new(QUEEN, board.find_piece(target)) });
+        }
+        for target in BitboardIterator(pt & !opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: qsq, to: square(target) } , pc: PieceCaptureType::new(QUEEN, None) });
+        }
+    }
+
+    for squeen in BitboardIterator(queens_straight_pinned) {
+        let qsq = square(squeen);
+        let pt = crate::magic::magic_rook(qsq, all_pieces) & !friends & straight_pin_mask;
+        for target in BitboardIterator(pt & opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: qsq, to: square(target) } , pc: PieceCaptureType::new(QUEEN, board.find_piece(target)) });
+        }
+        for target in BitboardIterator(pt & !opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: qsq, to: square(target) } , pc: PieceCaptureType::new(QUEEN, None) });
+        }
+    }
+
+
+
+    for queen in BitboardIterator(queens_unpinned) {
+        let qsq = square(queen);
+        let pt = (crate::magic::magic_rook(qsq, all_pieces) | 
+            crate::magic::magic_bishop(qsq, all_pieces)) & !friends;
+        for target in BitboardIterator(pt & opponents) {
+            let mov = Move::Default { mov: SimpleMove { from: qsq, to: square(target) } , pc: PieceCaptureType::new(QUEEN, board.find_piece(target)) };
+            moves.push(Move::Default { mov: SimpleMove { from: qsq, to: square(target) } , pc: PieceCaptureType::new(QUEEN, board.find_piece(target)) });
+        }
+        for target in BitboardIterator(pt & !opponents) {
+            moves.push(Move::Default { mov: SimpleMove { from: qsq, to: square(target) } , pc: PieceCaptureType::new(QUEEN, None) });
+        }
+    }
+
+    /*if pawns_straight_pinned != 0 {
+        println!("{}", board.beautiful_print());
+        println!("{}", print_board(pawns_straight_pinned));
+        println!("{}", print_board(straight_pin_mask));
+        println!("{}", print_board(straight_pinned_pieces));
+        println!("{}", print_board(straight_attack));
+        panic!("asfsadf");
+    }*/
+
+    //generate_pawn_pushes(board, side, &mut testmoves);
+    //generate_pawn_doublepushes(board, side, &mut testmoves);
+    //generate_pawn_captures(board, side, &mut testmoves);
+
+    generate_en_passant(board, side, &mut testmoves);
+
+    //generate_promotions(board, side, &mut moves);
+    /*generate_knight_moves(board, side, &mut testmoves, false);
+    generate_bishop_moves(board, side, &mut testmoves, false);
+    generate_rook_moves(board, side, &mut testmoves, false);
+    generate_queen_moves(board, side, &mut testmoves, false);*/
+    generate_king_moves(board, side, &mut testmoves, false);
+    generate_castling_moves(board, side, &mut testmoves);
+
+    let mut is_legal = |m: &Move| {
+        let undo = board.apply(*m);
+        let legal = !is_check(board, side);
+        board.undo_move(undo);
+        return legal;
+    };
+
+    /*for m in &moves {
+        if !is_legal(m) {
+            println!("{}", board.beautiful_print());
+            println!("{}", m.to_string());
+            println!("{}", print_board(pawns_straight_pinned));
+            println!("{}", print_board(straight_pin_mask));
+            panic!("asfsadf");
+        }
+    }*/
+    testmoves.retain(is_legal);
+    moves.append(&mut testmoves);
+
+    moves
+}
+
+
+fn generate_knight_moves_new(board: &Board,
+    friends: Bitboard,
+    others: Bitboard,
+    knights: Bitboard, move_list: &mut Vec<Move>, captures_only: bool) {
+
+    for k in BitboardIterator(knights) {
+        let cap_targets = BitboardIterator(get_knight_targets(square(k)) & (!friends & others));
+        let nocap_targets = BitboardIterator(get_knight_targets(square(k)) & (!friends & !others));
+
+        for target in cap_targets {
+            let simple_move = SimpleMove { from: square(k), to: square(target) };
+            let captured = board.find_piece(target);
+            move_list.push(Move::Default{ mov: simple_move, pc: PieceCaptureType::new(KNIGHT, captured) });
+        }
+        if !captures_only {
+            for target in nocap_targets {
+                let simple_move = SimpleMove { from: square(k), to: square(target) };
+                move_list.push(Move::Default{ mov: simple_move, pc: PieceCaptureType::new(KNIGHT, None) });
+            }
+        }
+    }
+
+}
+
+fn generate_moves_single_piece(board: &Board,
+    friends: Bitboard,
+    others: Bitboard,
+    pin_mask: Bitboard,
+    piece_type: PieceType,
+    piece: Bitboard,
+    targets: Bitboard,
+    move_list: &mut Vec<Move>, captures_only: bool) {
+
+    let cap_targets = BitboardIterator(targets & (!friends & others));
+    let nocap_targets = BitboardIterator(targets & (!friends & !others));
+
+    let psquare = square(piece);
+
+    for target in cap_targets {
+        let simple_move = SimpleMove { from: psquare, to: square(target) };
+        let captured = board.find_piece(target);
+        move_list.push(Move::Default{ mov: simple_move, pc: PieceCaptureType::new(piece_type, captured) });
+    }
+    if !captures_only {
+        for target in nocap_targets {
+            let simple_move = SimpleMove { from: psquare, to: square(target) };
+            move_list.push(Move::Default{ mov: simple_move, pc: PieceCaptureType::new(piece_type, None) });
+        }
+    }
+
+}
+
 pub fn generate_legal_moves(game: &mut Board, side: Side, captures_only: bool) -> Vec<Move> {
     let moves =
         if captures_only {
@@ -1050,6 +1414,12 @@ pub fn get_knight_targets(square: Square) -> Bitboard {
     TARGETS[square as usize]
 }
 
+pub fn get_king_targets(king: Bitboard) -> Bitboard {
+    let row = king | east_one(king) | west_one(king);
+
+    row | north_one(row) | south_one(row)
+}
+
 fn generate_bishop_moves(game: &Board, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);

+ 2 - 1
src/search.rs

@@ -676,7 +676,8 @@ impl SearchControl {
 
     pub fn perft(&mut self, depth: i32) -> bool {
         let board = &mut self.board;
-        let moves = generate_legal_moves(board, board.turn, false);
+        let moves = generate_legal_moves_new(board, board.turn, false);
+        //let moves = generate_legal_moves_new(board, board.turn, false);
 
         if depth <= 1 {
             self.nodes += moves.len();

+ 2 - 1
src/uci.rs

@@ -27,7 +27,7 @@ pub fn parse_command(command: &str) -> Result<Command, String> {
             //"setoption" => cmd_setoption(cmd, r, s),
             "quit" | "exit" => cmd_quit(cmd, r, s),*/
             "" =>  Ok(Command::Ping),
-            _cmd => { Err("unknown command".to_owned()) }
+            _cmd => { Err(format!("unknown command '{}'", command)) }
         }
     }
     else {
@@ -91,6 +91,7 @@ pub fn parse_go(args: &Vec<&str>) -> Result<Command, String> {
         }
         else if arg == "perft" {
             si.perft = true;
+            opt_name = Some("depth");
         }
         else {
             opt_name = Some(arg);