Forráskód Böngészése

movegen improvements and slight change to eval

Nicolas Winkler 2 éve
szülő
commit
64c77661c4
5 módosított fájl, 141 hozzáadás és 65 törlés
  1. 1 0
      src/engine.rs
  2. 3 3
      src/evaluate.rs
  3. 2 9
      src/game.rs
  4. 130 53
      src/movegen.rs
  5. 5 0
      src/search.rs

+ 1 - 0
src/engine.rs

@@ -243,6 +243,7 @@ impl Engine {
         if si.perft {
             if let Some(dep) = si.depth {
                 depth = dep;
+                sc.initial_depth = depth;
             }
             let invalid = perft(&mut self.game, &mut sc, depth as i32);
             if !invalid {

+ 3 - 3
src/evaluate.rs

@@ -137,7 +137,7 @@ pub fn material_value(game: &Game, side: Side) -> PosValue {
         + game.get_piece(KNIGHT, side).count_ones() * 220
         + game.get_piece(BISHOP, side).count_ones() * 320
         + game.get_piece(ROOK, side).count_ones() * 400
-        + game.get_piece(QUEEN, side).count_ones() * 700) as PosValue
+        + game.get_piece(QUEEN, side).count_ones() * 800) as PosValue
 }
 
 fn castling_rights(game: &Game, side: Side) -> PosValue {
@@ -201,8 +201,8 @@ pub fn evaluate(game: &Game) -> PosValue {
     let castling_rights = castling_rights(game, game.turn) - castling_rights(game, !game.turn);
 
     let pawn_structure = pawn_structure(game, game.turn) - pawn_structure(game, !game.turn);
-    let bishop_moves = (bishop_moves(game, game.turn) - bishop_moves(game, !game.turn)) * 5;
-    let queen_moves = (queen_moves(game, game.turn) - queen_moves(game, !game.turn)) * 5;
+    let bishop_moves = (bishop_moves(game, game.turn) - bishop_moves(game, !game.turn)) * 3;
+    let queen_moves = (queen_moves(game, game.turn) - queen_moves(game, !game.turn)) * 3;
 
     let mge = sv + kv + king_safety + king_there + castling_rights + pawn_structure + bishop_moves + queen_moves;
     let lge = late_game_eval(game);

+ 2 - 9
src/game.rs

@@ -636,6 +636,8 @@ impl Game {
                 }
 
 
+                // reset en passant and then check if possible
+                self.en_passant = None;
                 if pt == PAWN {
                     let row1 = indices_from_square(mov.from).1;
                     let row2 = indices_from_square(mov.to).1;
@@ -646,17 +648,8 @@ impl Game {
                         if (west_one(target_bb) | east_one(target_bb)) & opponent_pawns != 0 {
                             self.en_passant = Some(indices_from_square(mov.to).0);
                         }
-                        else {
-                            self.en_passant = None
-                        }
-                    }
-                    else {
-                        self.en_passant = None
                     }
                 }
-                else {
-                    self.en_passant = None;
-                }
 
                 let moved_piece = mov.apply_to(self.get_piece(pt, side));
                 self.set_piece(pt, side, moved_piece);

+ 130 - 53
src/movegen.rs

@@ -189,8 +189,7 @@ pub fn generate_possattacking_moves(game: &Game, side: Side) -> Vec<Move> {
     return moves;
 }
 
-
-pub fn generate_legal_moves(game: &mut Game, side: Side) -> Vec<Move> {
+pub fn generate_legal_moves_old(game: &mut Game, side: Side) -> Vec<Move> {
     let moves = generate_moves(game, side);
 
     let check_legality = |mov: &Move| {
@@ -203,34 +202,76 @@ pub fn generate_legal_moves(game: &mut Game, side: Side) -> Vec<Move> {
     moves.into_iter().filter(check_legality).collect::<Vec<Move>>()
 }
 
+fn generate_all_slides(origin: Bitboard) -> Bitboard {
+    let mut result = populate_files(origin) | populate_rows(origin);
 
-pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, side: Side) -> Vec<Move> {
-    let moves = generate_moves(game, side);
+    let mut ne = origin;
+    let mut se = origin;
+    let mut nw = origin;
+    let mut sw = origin;
+    for i in 1..7 {
+        result |= ne;
+        result |= se;
+        result |= nw;
+        result |= sw;
+        ne = northeast_one(ne);
+        se = southeast_one(se);
+        nw = northwest_one(nw);
+        sw = southwest_one(sw);
+    }
+    return result;
+}
 
-    let illegal_value = crate::evaluate::MAX_VALUE;
 
-    let mut mov_val_and_legality = |mov: &Move| {
-        let undo = game.apply(*mov);
-        let illegal = is_check(game, side);
+pub fn generate_legal_moves(game: &mut Game, side: Side) -> Vec<Move> {
+    let moves = generate_moves(game, side);
 
-        if illegal {
-            game.undo_move(undo);
-            return illegal_value;
+    let check = is_check(game, side);
+    let king = game.kings(side);
+    let possible_pins = generate_all_slides(king);
+
+    let check_legality = |m: &Move| {
+        if !check {
+            match m {
+                Move::Default { mov, piece_type, captured: _ } => {
+                    // check whether piece is not pinned and is not a king
+                    if from_square(mov.from) & possible_pins == 0 && *piece_type != KING {
+                        return true;
+                    }
+                },
+                _ => {}
+            }
         }
+        let undo = game.apply(*m);
+        let legal = !is_check(game, side);
+        game.undo_move(undo);
+        return legal;
+    };
+
+    moves.into_iter().filter(check_legality).collect::<Vec<Move>>()
+}
+
+
+pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, side: Side) -> Vec<Move> {
+    let mut moves = generate_legal_moves(game, side);
+
+    let mov_val= |mov: &Move| {
+        // if its a pv move from previously
+        if let Some(c) = &ce {
+            if &c.mov == mov { 
+                return -2000;
+            }
+        };
 
-        let pv_bonus = if let Some(c) = &ce { if &c.mov == mov { 1000 } else { 0 } } else { 0 };
         let killer_bonus = if killers.contains(mov) { 300 } else { 0 };
         let capture_bonus: i32 = if mov.is_capture() { 400 } else { 0 } - mvv_lva_score(mov) / 16;
-
-        let eval = crate::evaluate::evaluate(game) - killer_bonus - capture_bonus - pv_bonus;
-        game.undo_move(undo);
+        let eval = -killer_bonus - capture_bonus;
         return eval;
     };
 
-    let mut amovs: Vec<_> = moves.iter().map(|m| (*m, mov_val_and_legality(m))).collect();
-    amovs.sort_unstable_by_key(|x| x.1);
+    moves.sort_by_cached_key(|m| mov_val(m));
 
-    amovs.into_iter().filter(|x| x.1 != illegal_value).map(|(m, _v)| m).collect::<Vec<Move>>()
+    return moves;
 }
 
 
@@ -726,50 +767,86 @@ pub fn is_check(game: &Game, side: Side) -> bool {
     return false;
 }
 
-/**
- * checks if side's king is in check
- */
-pub fn is_check_old(game: &Game, side: Side) -> bool {
-    let king = game.get_piece(KING, side);
-    let king_square = square(king);
-    let possible_attacks = generate_possattacking_moves(game, !side);
-    for mov in possible_attacks {
-        if let Move::Default{ mov, piece_type: _, captured: _ } = mov {
-            if mov.to == king_square {
-                if !is_check(game, side) {
-                    panic!("incorrect non-check {}", game.beautiful_print());
-                }
-                return true;
-            }
+///
+/// checks wether the square sq is attacked by the specified side
+/// 
+pub fn is_attacked(game: &Game, sq: Square, side: Side) -> bool {
+    let others = game.get_all_side(!side);
+    let friends = game.get_all_side(side);
+
+    let sq_bb = from_square(sq);
+
+    let knights = get_knight_targets(sq);
+    if knights & game.get_piece(KNIGHT, side) != 0 {
+        return true;
+    }
+
+    let diagonal = generate_sliding_destinations(others, friends, sq_bb, false, true);
+    let straight = generate_sliding_destinations(others, friends, sq_bb, true, false);
+
+    if diagonal & game.get_piece(BISHOP, side) != 0 {
+        return true;
+    }
+    if diagonal & game.get_piece(QUEEN, side) != 0 {
+        return true;
+    }
+    if straight & game.get_piece(ROOK, side) != 0 {
+        return true;
+    }
+    if straight & game.get_piece(QUEEN, side) != 0 {
+        return true;
+    }
+
+    if side == WHITE {
+        if ((southeast_one(sq_bb) | southwest_one(sq_bb)) & game.get_piece(PAWN, side)) != 0 {
+            return true;
         }
-        else if let Move::Promotion{ mov, promote_to: _, captured: _ } = mov {
-            if mov.to == king_square {
-                if !is_check(game, side) {
-                    panic!("incorrect non-check {}", game.beautiful_print());
-                }
-                return true;
-            }
+    }
+    else {
+        if ((northeast_one(sq_bb) | northwest_one(sq_bb)) & game.get_piece(PAWN, side)) != 0 {
+            return true;
         }
     }
-    if is_check(game, side) {
-        panic!("incorrect check {}", game.beautiful_print());
+
+    let sq_lr = east_one(sq_bb) | sq_bb | west_one(sq_bb);
+    let sq_area = north_one(sq_lr) | sq_lr | south_one(sq_lr);
+    if sq_area & game.get_piece(KING, side) != 0 {
+        return true;
     }
-    false
+
+    return false;
 }
-/*
+
+
+
 #[cfg(test)]
 mod tests {
-    use search::Game;
     use movegen::*;
 
-    #[test]
-    fn pawn_pushes() {
-        let mut game: Game = Game::default();
-        let pawn_moves = generate_pawn_moves(&game, WHITE);
-        assert_eq!(pawn_moves, ());
-    }
-}
-*/
+    use crate::{search::{perft, SearchControl}, hash::RepetitionTable};
 
+    ///
+    /// tests the move generation from some positions to depth 4 and checks wether the correct
+    /// amount of moves has been generated
+    /// 
+    #[test]
+    fn some_positions_perft() {
+        assert_eq!(nodes_from_pos("5bk1/5p2/7p/q2r1p2/2Q5/P4N2/3prPPP/1R3RK1 b - - 1 34", 4), 3773096);
+        assert_eq!(nodes_from_pos("r1bqkbnr/1p1p1ppp/p1N1p3/8/4P3/2N5/PPP2PPP/R1BQKB1R b KQkq - 0 6", 4), 2008894);
+        assert_eq!(nodes_from_pos("1r5r/p2b2p1/1p1k1bp1/2pBpp2/1P1n1P2/P1NP3P/2PB2P1/R2KR3 w - - 1 23", 4), 1971751);
 
+        // some positions from https://www.chessprogramming.org/Perft_Results
+        assert_eq!(nodes_from_pos("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 4), 2103487);
+        assert_eq!(nodes_from_pos("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 5), 15833292);
+        assert_eq!(nodes_from_pos("r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1", 5), 15833292);
+    }
 
+    fn nodes_from_pos(fen: &str, depth: i32) -> usize {
+        let mut game: Game = Game::from_fen_str(fen).unwrap();
+        let mut check = || false;
+        let mut rt = &mut RepetitionTable::new();
+        let mut sc = SearchControl::new(&mut check, &mut rt, depth);
+        perft(&mut game, &mut sc, depth);
+        return sc.nodes;
+    }
+}

+ 5 - 0
src/search.rs

@@ -321,10 +321,15 @@ pub fn perft(game: &mut Game, sc: &mut SearchControl, depth: i32) -> bool {
 
     
     for mov in moves {
+        let nodes_before = sc.nodes;
         let undo = game.apply(mov);
         let do_return = perft(game, sc, depth - 1);
         game.undo_move(undo);
 
+        if depth >= sc.initial_depth {
+            println!("{}: {}", mov.to_string(), sc.nodes - nodes_before);
+        }
+
         if do_return {
             return true;
         }