Nicolas Winkler 3 年之前
父节点
当前提交
d29caf483a
共有 10 个文件被更改,包括 342 次插入129 次删除
  1. 0 72
      Cargo.lock
  2. 0 1
      Cargo.toml
  3. 4 0
      src/bitboard.rs
  4. 1 1
      src/engine.rs
  5. 24 6
      src/evaluate.rs
  6. 8 3
      src/game.rs
  7. 1 1
      src/interface.rs
  8. 1 3
      src/main.rs
  9. 209 23
      src/movegen.rs
  10. 94 19
      src/search.rs

+ 0 - 72
Cargo.lock

@@ -1,26 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
 [[package]]
-name = "aho-corasick"
-version = "0.7.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
-]
-
-[[package]]
 name = "autocfg"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -30,7 +10,6 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 name = "bishop"
 version = "0.1.0"
 dependencies = [
- "env_logger",
  "log",
  "rand",
  "simplelog",
@@ -56,19 +35,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "env_logger"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
-dependencies = [
- "atty",
- "humantime",
- "log",
- "regex",
- "termcolor",
-]
-
-[[package]]
 name = "getrandom"
 version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -80,21 +46,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
 name = "libc"
 version = "0.2.119"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -110,12 +61,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "memchr"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
-
-[[package]]
 name = "num-integer"
 version = "0.1.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -171,23 +116,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "regex"
-version = "1.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
-
-[[package]]
 name = "simplelog"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"

+ 0 - 1
Cargo.toml

@@ -6,6 +6,5 @@ authors = ["Nicolas Winkler <nicolas.winkler@gmx.ch>"]
 [dependencies]
 
 log = "*"
-env_logger = "*"
 simplelog = "*"
 rand = "*"

+ 4 - 0
src/bitboard.rs

@@ -93,6 +93,10 @@ pub fn from_indices(file: u8, row: u8) -> Bitboard {
     1_u64 << square_from_indices(file, row)
 }
 
+pub fn indices_from_square(sq: Square) -> (u8, u8) {
+    (7 - sq % 8, 7 - sq / 8)
+}
+
 pub fn print_board(b: Bitboard) -> String {
     (0..8).map(
         |i| (0..8).map(

+ 1 - 1
src/engine.rs

@@ -46,7 +46,7 @@ pub fn run_engine(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) {
             },
             EngineMsg::SetPiece(_) => { println!("SetPiece") },
             EngineMsg::Search(depth) => {
-                info!("searching in pos\n {}", game.beautiful_print());
+                //info!("searching in pos\n {}", game.beautiful_print());
                 //println!("Search {}", depth);
                 let best_move = search(&game, depth);
                 println!("bestmove {}", best_move.to_string());

+ 24 - 6
src/evaluate.rs

@@ -2,15 +2,33 @@ use game::*;
 use bitboard::*;
 use movegen::*;
 
+fn value_castling_rights(game: &Game, side: Side) -> i32 {
+    let mut val: i32 = 0;
+
+    for i in 0..2 {
+        if game.castling_rights[i] { val += 5; }
+    }
+    for i in 2..4 {
+        if game.castling_rights[i] { val -= 5; }
+    }
+    
+    if side == BLACK {
+        return -val;
+    }
+    else {
+        return val;
+    }
+}
 
 fn side_value(game: &Game, side: Side) -> u32 {
-    game.get_piece(PAWN, game.turn).count_ones() * 100
-    + (game.get_piece(KNIGHT, game.turn) | game.get_piece(BISHOP, game.turn)).count_ones() * 300
-    + game.get_piece(ROOK, game.turn).count_ones() * 400
-    + game.get_piece(QUEEN, game.turn).count_ones() * 600
-    + game.get_piece(KING, game.turn).count_ones() * 100000
+    game.get_piece(PAWN, side).count_ones() * 100
+    + game.get_piece(KNIGHT, side).count_ones() * 300
+    + game.get_piece(BISHOP, side).count_ones() * 301
+    + game.get_piece(ROOK, side).count_ones() * 400
+    + game.get_piece(QUEEN, side).count_ones() * 600
+    + game.get_piece(KING, side).count_ones() * 100000
 }
 
 pub fn evaluate(game: &Game) -> i32 {
-    return side_value(game, game.turn) as i32 - side_value(game, !game.turn) as i32;
+    return side_value(game, game.turn) as i32 - side_value(game, !game.turn) as i32 + value_castling_rights(game, game.turn);
 }

+ 8 - 3
src/game.rs

@@ -1,11 +1,15 @@
 use bitboard::*;
 use movegen::*;
+use log::info;
 
 #[derive(Clone)]
 pub struct Game
 {
     pub pieces: [Bitboard; 12],
     pub turn: Side,
+
+    /// None if no en passant move is possible,
+    /// otherwise contains the column of the last (double-) pushed pawn
     pub en_passant: Option<u8>,
 
     ///
@@ -57,7 +61,7 @@ impl Game {
 
     pub fn from_fen(fen: &[&str]) -> Option<Game> {
         let mut game: Game = Game::empty();
-        let example = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+        //let example = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
 
         let position = fen[0];
         let turn = fen[1];
@@ -117,8 +121,9 @@ impl Game {
         game.castling_rights[2] = castling_rights.contains('k');
         game.castling_rights[3] = castling_rights.contains('q');
 
-        // parse castling rights
-        game.en_passant = en_passant.parse::<u8>().ok();
+        // parse en passant
+        game.en_passant = Move::parse_square(en_passant).map(|sq| indices_from_square(sq).0);
+        info!("en passant on file {:?}", game.en_passant);
 
         game.halfmoves_since_last_event = halfmoves_since_last_event.parse::<i32>().unwrap_or(0);
         game.turn_number = turn_number.parse::<i32>().unwrap_or(0);

+ 1 - 1
src/interface.rs

@@ -86,7 +86,7 @@ fn cmd_position(mut args: Vec<&str>, r: &Receiver<InterfaceMsg>, s: &Sender<Engi
 }
 
 fn cmd_go(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    s.send(EngineMsg::Search(7)).unwrap();
+    s.send(EngineMsg::Search(2)).unwrap();
 }
 
 fn cmd_newgame(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {

+ 1 - 3
src/main.rs

@@ -7,7 +7,6 @@ mod evaluate;
 mod search;
 
 extern crate log;
-extern crate env_logger;
 extern crate simplelog;
 extern crate rand;
 
@@ -16,7 +15,6 @@ use simplelog::*;
 use std::sync::mpsc;
 use std::{thread, fs::File};
 use log::*;
-use env_logger::{Target, Builder};
 
 fn main() {
     /*let mut builder = Builder::from_default_env();
@@ -26,7 +24,7 @@ fn main() {
         //.target(Target::Stderr)
         .init();
         */
-    simplelog::WriteLogger::init(LevelFilter::Info, Config::default(), File::create("C:\\\\Users\\Nicolas\\debug.log").unwrap()).unwrap();
+    simplelog::WriteLogger::init(LevelFilter::Info, Config::default(), File::create("debug.log").unwrap()).unwrap();
 
     let (esend, erecv) = mpsc::channel();
     let (isend, irecv) = mpsc::channel();

+ 209 - 23
src/movegen.rs

@@ -2,6 +2,7 @@ use bitboard::Bitboard;
 use bitboard::Square;
 use bitboard::*;
 use game::Game;
+use log::info;
 
 pub type Side = bool;
 
@@ -28,7 +29,7 @@ pub struct SimpleMove {
 pub enum Move {
     Default { mov: SimpleMove, piece_type: PieceType },
     Castling { side: Side, left: bool },
-    EnPassant { side: Side, column: u8 },
+    EnPassant { mov: SimpleMove, beaten: Square },
     Promotion { mov: SimpleMove, promote_to: PieceType },
 }
 
@@ -65,8 +66,25 @@ impl Move {
             Move::Default{ mov, piece_type } => {
                 Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
             },
-            Move::Castling{ side, left } => { "castling not implemented".to_owned() },
-            Move::EnPassant{ side, column } => { "en passant not implemented".to_owned() },
+            Move::Castling{ side, left } => {
+                match (side, left) {
+                    (&WHITE, false) => {
+                        Self::square_to_string(3) + &Self::square_to_string(1)
+                    },
+                    (&WHITE, true) => {
+                        Self::square_to_string(3) + &Self::square_to_string(5)
+                    },
+                    (&BLACK, false) => {
+                        Self::square_to_string(56 + 3) + &Self::square_to_string(56 + 1)
+                    },
+                    (&BLACK, true) => {
+                        Self::square_to_string(56 + 3) + &Self::square_to_string(56 + 5)
+                    }
+                }
+            },
+            Move::EnPassant{ mov, beaten } => {
+                Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
+            },
             Move::Promotion{ mov, promote_to } => {
                 Self::square_to_string(mov.from) + &Self::square_to_string(mov.to) + 
                 if *promote_to == QUEEN { "Q" }
@@ -109,21 +127,42 @@ impl SimpleMove {
 
 
 pub fn generate_moves(game: &Game, side: Side) -> Vec<Move> {
-    let mut moves: Vec<Move> = Vec::new();
+
+    let mut moves: Vec<Move> = Vec::with_capacity(128);
     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);
+    generate_promotions(game, side, &mut moves);
+    generate_en_passant(game, side, &mut moves);
+    generate_knight_moves(game, side, &mut moves, false);
+    generate_bishop_moves(game, side, &mut moves, false);
+    generate_rook_moves(game, side, &mut moves, false);
+    generate_queen_moves(game, side, &mut moves, false);
+    generate_king_moves(game, side, &mut moves, false);
+    generate_castling_moves(game, side, &mut moves);
+    return moves;
+}
+
+/**
+ * generates moves that could possibly attack a king,
+ * this function is used to check if the king is in check
+ */
+pub fn generate_possattacking_moves(game: &Game, side: Side) -> Vec<Move> {
+
+    let mut moves: Vec<Move> = Vec::with_capacity(32);
+    generate_pawn_captures(game, side, &mut moves);
+    generate_promotions(game, side, &mut moves);
+    generate_knight_moves(game, side, &mut moves, true);
+    generate_bishop_moves(game, side, &mut moves, true);
+    generate_rook_moves(game, side, &mut moves, true);
+    generate_queen_moves(game, side, &mut moves, true);
+    generate_king_moves(game, side, &mut moves, true);
     return moves;
 }
 
 
 pub fn generate_legal_moves(game: &Game, side: Side) -> Vec<Move> {
-    let mut moves = generate_moves(game, side);
+    let moves = generate_moves(game, side);
 
     moves.into_iter().filter(|mov| {
         let tryout = super::search::apply_move(game, *mov);
@@ -132,6 +171,32 @@ pub fn generate_legal_moves(game: &Game, side: Side) -> Vec<Move> {
 }
 
 
+pub fn generate_attacking_moves(game: &Game, side: Side) -> Vec<Move> {
+    let mut moves: Vec<Move> = Vec::with_capacity(32);
+    generate_pawn_captures(game, side, &mut moves);
+    generate_knight_moves(game, side, &mut moves, true);
+    generate_bishop_moves(game, side, &mut moves, true);
+    generate_rook_moves(game, side, &mut moves, true);
+    generate_queen_moves(game, side, &mut moves, true);
+    generate_king_moves(game, side, &mut moves, true);
+    return moves;
+}
+
+
+pub fn sort_moves(game: &Game, move_list: &mut Vec<Move>) {
+    let all_pieces = game.get_all_side(WHITE) | game.get_all_side(BLACK);
+
+    move_list.sort_by_key(|mov| {
+        match mov {
+            Move::Default { mov, piece_type } => if (from_square(mov.to) & all_pieces) != 0 { -10 } else { 0 },
+            Move::Castling { side: _, left: _ } => 0,
+            Move::Promotion { mov, promote_to } => -5,
+            Move::EnPassant { mov: _, beaten: _ } => -10,
+        }
+    });
+}
+
+
 /*
 pub fn generate_pawn_moves(game: &Game, side: Side) -> Bitboard {
     let pushed = generate_pawn_pushes(game, side);
@@ -219,7 +284,68 @@ 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>) {
+fn generate_promotions(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    let pawns = game.pawns(side);
+    let others = game.get_all_side(!side);
+    let pieces = game.get_all_side(side);
+
+    let moved_pawns =
+        match side {
+            WHITE => north_one(pawns),
+            BLACK => south_one(pawns)
+        } & (ROW_1 | ROW_8) & !(others | pieces);
+
+    let iter = BitboardIterator { board: moved_pawns };
+    for p in iter {
+        let origin = match side {
+            WHITE => south_one(p),
+            BLACK => north_one(p)
+        };
+        let movement = SimpleMove { from: square(origin), to: square(p) };
+        //move_list.push(Move::Default { mov: SimpleMove { from: square(origin), to: square(p) }, piece_type: PAWN });
+        move_list.push(Move::Promotion { mov: movement, promote_to: QUEEN });
+        move_list.push(Move::Promotion { mov: movement, promote_to: ROOK });
+        move_list.push(Move::Promotion { mov: movement, promote_to: BISHOP });
+        move_list.push(Move::Promotion { mov: movement, promote_to: KNIGHT });
+    }
+}
+
+
+fn generate_en_passant(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    if let Some(ep) = game.en_passant {
+        let pawncolumn = A_FILE >> ep;
+
+        let pawnrow: Bitboard = match side {
+            WHITE => ROW_5,
+            BLACK => ROW_4
+        };
+
+        let beaten_pawn = pawncolumn & pawnrow;
+        if beaten_pawn & game.get_piece(PAWN, !side) == 0 {
+            return;
+        }
+
+        let pawn_left = (beaten_pawn << 1) & pawnrow;
+        let pawn_right = (beaten_pawn >> 1) & pawnrow;
+
+        let attacking_pawns = game.get_piece(PAWN, side);
+        let target_square = pawncolumn & match side {
+            WHITE => ROW_6,
+            BLACK => ROW_3
+        };
+
+        if pawn_left & attacking_pawns != 0 {
+            let mov = Move::EnPassant{ mov: SimpleMove{ from: square(pawn_left), to: square(target_square) }, beaten: square(beaten_pawn) };
+            move_list.push(mov);
+        }
+        if pawn_right & attacking_pawns != 0 {
+            let mov = Move::EnPassant{ mov: SimpleMove{ from: square(pawn_right), to: square(target_square) }, beaten: square(beaten_pawn) };
+            move_list.push(mov);
+        }
+    }
+}
+
+fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
 
@@ -227,7 +353,12 @@ fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     let knight_iter = BitboardIterator{ board: knights };
 
     for k in knight_iter {
-        let targets = BitboardIterator { board: get_knight_targets(square(k)) & !friends };
+        let target_mask = if captures_only {
+            !friends & others
+        } else {
+            !friends
+        };
+        let targets = BitboardIterator { board: get_knight_targets(square(k)) & target_mask };
         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 });
@@ -254,34 +385,36 @@ fn get_knight_targets(square: Square) -> Bitboard {
     TARGETS[square as usize]
 }
 
-fn generate_bishop_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_bishop_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     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);
+    generate_sliding_moves(friends, others, game.bishops(side), BISHOP, false, true, move_list, captures_only);
 }
 
-fn generate_rook_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_rook_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     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);
+    generate_sliding_moves(friends, others, game.rooks(side), ROOK, true, false, move_list, captures_only);
 }
 
-fn generate_queen_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_queen_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
     
-    generate_sliding_moves(friends, others, game.queens(side), QUEEN, true, true, move_list);
+    generate_sliding_moves(friends, others, game.queens(side), QUEEN, true, true, move_list, captures_only);
 }
 
 fn generate_sliding_moves(friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
-                          straight: bool, diagonal: bool, move_list: &mut Vec<Move>) {
+                          straight: bool, diagonal: bool, move_list: &mut Vec<Move>, captures_only: bool) {
     let pieces_iter = BitboardIterator { board: pieces };
 
+    let mask = if captures_only { others } else { !(0 as Bitboard) };
+
     for piece in pieces_iter {
         let destinations = generate_sliding_destinations(friends, others, piece, straight, diagonal);
-        let dest_iter = BitboardIterator { board: destinations };
+        let dest_iter = BitboardIterator { board: destinations & mask };
         for dest in dest_iter {
             let smove = SimpleMove{ from: square(piece), to: square(dest) };
             move_list.push(Move::Default { mov: smove, piece_type: piece_type});
@@ -326,7 +459,7 @@ fn generate_direction(piece: Bitboard, direction: fn(Bitboard) -> Bitboard, occu
 }
 
 
-fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
     let friends = game.get_all_side(side);
     let others = game.get_all_side(!side);
 
@@ -336,6 +469,14 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
 
     if king.count_ones() != 1 { return; }
 
+    let mask =
+        if captures_only {
+            !friends & others
+        }
+        else {
+            !friends
+        };
+
     let area = (north_one(king)
               | south_one(king)
               | east_one(king)
@@ -344,7 +485,7 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
               | northwest_one(king)
               | southwest_one(king)
               | southeast_one(king))
-            & !friends;
+            & mask;
 
     let area_iter = BitboardIterator { board: area };
     for destination in area_iter {
@@ -353,12 +494,57 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     }
 }
 
+
+fn generate_castling_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+    let c1 = Move::Castling{ side: side, left: false };
+    let c2 = Move::Castling{ side: side, left: true };
+
+    let legal_castlings: &[bool] = match side {
+        WHITE => &game.castling_rights[0..2],
+        BLACK => &game.castling_rights[2..4],
+    };
+
+    let all_pieces = game.get_all_side(WHITE) | game.get_all_side(BLACK);
+
+    // kingside castling
+    if legal_castlings[0] {
+        //info!("possible castling? {} {} {}", game.get_piece(KING, side), game.get_piece(ROOK, side), ((game.get_piece(KING, side) >> 3) & game.get_piece(ROOK, side)));
+        if ((game.get_piece(KING, side) >> 3) & game.get_piece(ROOK, side)) != 0 &&
+            ((game.get_piece(KING, side) >> 1) | (game.get_piece(KING, side) >> 2)) & all_pieces == 0 {
+                let mut tg1 = game.clone();
+
+                *tg1.get_piece_mut(KING, side) = game.get_piece(KING, side) >> 1;
+
+                if !is_check(game, side) && !is_check(&tg1, side) {
+                    move_list.push(c1);
+                }
+        }
+    }
+    // queenside
+    if legal_castlings[1] {
+        //info!("possible castling? {} {} {}", game.get_piece(KING, side), game.get_piece(ROOK, side), ((game.get_piece(KING, side) >> 3) & game.get_piece(ROOK, side)));
+        if ((game.get_piece(KING, side) << 4) & game.get_piece(ROOK, side)) != 0 &&
+            ((game.get_piece(KING, side) << 1) | (game.get_piece(KING, side) << 2) | (game.get_piece(KING, side) << 3)) & all_pieces == 0 {
+
+                let mut tg1 = game.clone();
+                let mut tg2 = game.clone();
+
+                *tg1.get_piece_mut(KING, side) = game.get_piece(KING, side) << 1;
+                *tg2.get_piece_mut(KING, side) = game.get_piece(KING, side) << 2;
+
+                if !is_check(game, side) && !is_check(&tg1, side) && !is_check(&tg2, side) {
+                    move_list.push(c2);
+                }
+        }
+    }
+}
+
 /**
  * checks if side's king is in check
  */
 pub fn is_check(game: &Game, side: Side) -> bool {
     let king_square = square(game.get_piece(KING, side));
-    let possible_attacks = generate_moves(game, !side);
+    let possible_attacks = generate_possattacking_moves(game, !side);
     for mov in possible_attacks {
         if let Move::Default{ mov, piece_type: _ } = mov {
             if mov.to == king_square {

+ 94 - 19
src/search.rs

@@ -30,11 +30,8 @@ enum MoveUndo {
 //07         bestValue := max( bestValue, v )
 //08     return bestValue
 pub fn search(game: &Game, depth: i32) -> Move {
-    let mut best = i32::min_value();
-    let mut best_move = Move::default();
-
     if depth == 0 {
-        return best_move;
+        return Move::default();
     }
 
     let mut moves = generate_legal_moves(game, game.turn);
@@ -49,30 +46,51 @@ pub fn search(game: &Game, depth: i32) -> Move {
         return moves[0];
     }*/
 
+    sort_moves(game, &mut moves);
+    
+    info!("moves: {:?}", moves.iter().map(|mv| mv.to_string()).collect::<Vec<String>>());
+
+    let mut alpha = i32::min_value() + 1;
+    let mut beta = i32::max_value();
+
     let mut valued_moves = moves.iter().map(|mov| {
         let new_game = apply_move(game, *mov);
-        (*mov, negamax(&new_game, i32::min_value(), i32::max_value() - 1, depth - 1))
+        //info!("searching {}", mov.to_string());
+        let val = -negamax(&new_game, -beta, -alpha, depth - 1);
+
+        if val > -beta {
+            //beta = -val;
+            //info!("updated beta to {}", beta);
+        }
+
+        //info!("searched {} = {}", mov.to_string(), val);
+        (*mov, -val)
     }).collect::<Vec<(Move, i32)>>();
+    info!("movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
 
     valued_moves.sort_by_key(|mv| mv.1);
 
+
     if valued_moves.len() > 0 {
         let min_val = valued_moves[0].1;
         let best_moves = valued_moves.iter().filter(|mv| mv.1 <= min_val).map(|(mov, _)| *mov).collect::<Vec<Move>>();
 
+        info!("bestmove value {}", -min_val);
         return best_moves[(rng.next_u64() % best_moves.len() as u64) as usize];
     }
     else {
         return Move::Default{ mov: SimpleMove{ from: 0, to: 0 }, piece_type: PAWN };
     }
-
-    //info!("best is {}", best_move.to_string());
-    return best_move;
 }
 
 fn negamax(game: &Game, mut alpha: i32, beta: i32, depth: i32) -> i32 {
     if depth == 0 {
-        return evaluate(game);
+        return quiescent_search(game, alpha, beta);
+        let eval = evaluate(game);
+        if eval != 0 {
+            //info!("eval: {}", eval);
+        }
+        return eval;
     }
 
     const MIN_VALUE: i32 = i32::min_value() + 1;
@@ -80,7 +98,10 @@ fn negamax(game: &Game, mut alpha: i32, beta: i32, depth: i32) -> i32 {
     let mut best = MIN_VALUE;
     //let mut best_move = Move::default();
     //info!(" -> generate_legal_moves");
-    let moves = generate_moves(game, game.turn);
+
+    if game.get_piece(KING, game.turn) == 0 { return MIN_VALUE; }
+
+    let moves = generate_legal_moves(game, game.turn);
 
     if moves.len() == 0 {
         if is_check(game, game.turn) {
@@ -97,6 +118,7 @@ fn negamax(game: &Game, mut alpha: i32, beta: i32, depth: i32) -> i32 {
         let new_game = apply_move(game, mov);
         //info!(" -> applied {} -> {}", mov.to_string(), depth - 1);
         let val = -negamax(&new_game, -beta, -alpha, depth - 1);
+        //info!(" -> negamax returned {} -> {}", mov.to_string(), val);
         if val >= beta {
             return beta;
         }
@@ -105,12 +127,41 @@ fn negamax(game: &Game, mut alpha: i32, beta: i32, depth: i32) -> i32 {
         }
         //info!(" -> negamaxed {} -> {}", mov.to_string(), depth - 1);
         if val > best {
+
             best = val;
             //best_move = mov;
         }
     }
-    //println!("best {}", best);
-    return best;
+    //info!("best alpha {}", best);
+    return (alpha as f64 * 0.99) as i32;
+}
+
+fn quiescent_search(game: &Game, mut alpha: i32, beta: i32) -> i32 {
+    const MIN_VALUE: i32 = i32::min_value() + 1;
+
+    let val = evaluate(game);
+
+    if val >= beta {
+        return beta;
+    }
+    if val > alpha {
+        alpha = val;
+    }
+
+    if game.get_piece(KING, game.turn) == 0 { return MIN_VALUE; }
+
+    let moves = generate_attacking_moves(game, game.turn);
+    for mov in moves {
+        let new_game = apply_move(game, mov);
+        let val = -quiescent_search(&new_game, -beta, -alpha);
+        if val >= beta {
+            return beta;
+        }
+        if val > alpha {
+            alpha = val;
+        }
+    }
+    return alpha;
 }
 
 pub fn apply_move(game: &Game, mov: Move) -> Game {
@@ -121,6 +172,29 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
     let others = game.get_all_side(!side);
     match mov {
         Move::Default { mov, piece_type: pt } => {
+
+            if pt == KING {
+                // invalidate castling rights
+                new_game.castling_rights[if game.turn == BLACK { 2 } else { 0 }] = false;
+                new_game.castling_rights[if game.turn == BLACK { 3 } else { 1 }] = false;
+            }
+
+            if pt == ROOK {
+                // invalidate castling rights
+                if mov.from == square_from_indices(7, 7) {
+                    new_game.castling_rights[0] = false;
+                }
+                else if mov.from == square_from_indices(0, 7) {
+                    new_game.castling_rights[1] = false;
+                }
+                else if mov.from == square_from_indices(7, 7) {
+                    new_game.castling_rights[2] = false;
+                }
+                else if mov.from == square_from_indices(7, 7) {
+                    new_game.castling_rights[3] = false;
+                }
+            }
+
             if from_square(mov.to) | others != 0 {
                 new_game.apply_mask(!from_square(mov.to));
             }
@@ -128,6 +202,10 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
             new_game.set_piece(pt, side, moved_piece);
         },
         Move::Castling { side, left } => {
+            // invalidate future castling rights
+            new_game.castling_rights[if game.turn == BLACK { 2 } else { 0 }] = false;
+            new_game.castling_rights[if game.turn == BLACK { 3 } else { 1 }] = false;
+
             let king = game.get_piece(KING, side);
             let rook = if left {
                 game.get_piece(ROOK, side) & (king << 4)
@@ -142,16 +220,13 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
             new_game.set_piece(KING, side, new_king);
             new_game.set_piece(ROOK, side, new_rook);
         },
-        Move::EnPassant { side, column: _ } => {
+        Move::EnPassant { mov, beaten } => {
             let pawns = game.pawns(side);
             if let Some(ep) = game.en_passant {
-                if pawns & A_FILE >> ep & (ROW_4 | ROW_5) != 0 {
-                    if pawns & A_FILE >> ep & (ROW_4 | ROW_5) != 0 {
-
-                    }
-                }
+                *new_game.get_piece_mut(PAWN, side) &= !from_square(mov.from);
+                *new_game.get_piece_mut(PAWN, side) |= from_square(mov.to);
+                *new_game.get_piece_mut(PAWN, !side) |= from_square(beaten);
             }
-            panic!("oh no");
         },
         Move::Promotion { mov, promote_to } => {
             if from_square(mov.to) | others != 0 {