|  | @@ -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;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |