Selaa lähdekoodia

reworked a bit

Nicolas Winkler 2 vuotta sitten
vanhempi
commit
666655dff5
10 muutettua tiedostoa jossa 418 lisäystä ja 1559 poistoa
  1. 116 345
      src/engine.rs
  2. 16 16
      src/evaluate.rs
  3. 0 950
      src/game.rs
  4. 0 142
      src/interface.rs
  5. 1 1
      src/magic/mod.rs
  6. 7 11
      src/main.rs
  7. 39 35
      src/movegen.rs
  8. 236 54
      src/search.rs
  9. 0 2
      src/settings.rs
  10. 3 3
      src/ttable.rs

+ 116 - 345
src/engine.rs

@@ -1,25 +1,25 @@
 use bitboard::Bitboard;
-use game::Game;
+use board::Board;
 use search::*;
 use movegen::*;
-use evaluate::PosValue;
-use log::{info};
-use std::time::{Duration, Instant};
-use std::collections::{VecDeque};
+use std::process::exit;
+use std::sync::mpsc::{Sender, self};
+use std::time::Duration;
 use zobrist::ZobristTable;
-use ttable::{Cache, RepetitionTable};
+use std::thread;
 
-use std::sync::Arc;
-use std::sync::mpsc::{Receiver, Sender};
-use std::panic;
+use std::sync::{Arc, RwLock};
+use crate::ttable::{RepetitionTable, Cache};
+use crate::uci;
 
 ///
 /// Message types that are sent from the uci interface to the engine thread.
 /// 
-pub enum EngineMsg {
-    SetBoard{ pos: Game, moves: Vec<String> },
+pub enum Command {
+    Uci,
+    Position{ pos: Board, moves: Vec<String> },
     SetPiece(Bitboard),
-    Search(SearchInfo),
+    Go(SearchInfo),
     Print,
     IsReady,
     Ping,
@@ -27,6 +27,7 @@ pub enum EngineMsg {
     NewGame,
     GetState,
     GetInfo,
+    Quit,
 }
 
 ///
@@ -44,25 +45,26 @@ pub struct SearchInfo {
     pub infinite: bool
 }
 
-pub enum InterfaceMsg {
+#[derive(PartialEq)]
+pub enum SearchMessage {
+    Stop
 }
 
 ///
 /// Stores the state of the chess engine.
 /// 
-/// The engine is run in a dedicated thread and will receive messages on [Engine::r] 
-/// 
 pub struct Engine {
-    game: Game,
+    board: Board,
     move_history: RepetitionTable,
-    messages: VecDeque<EngineMsg>,
-    r: Receiver<EngineMsg>,
+    hash: Arc<RwLock<Cache>>,
+    zobrist_table: Arc<ZobristTable>,
+
+    search_thread: Option<SearchThread>,
+}
 
-    #[allow(unused)]
-    s: Sender<InterfaceMsg>,
-    
-    hash: Cache,
-    zobrist_table: Arc<ZobristTable>
+struct SearchThread {
+    channel: Sender<SearchMessage>,
+    join_handle: thread::JoinHandle<bool>,
 }
 
 impl SearchInfo {
@@ -82,90 +84,124 @@ impl SearchInfo {
 }
 
 impl Engine {
-    pub fn new(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) -> Self {
+    pub fn new() -> Self {
         let mut eng = Engine {
-            game: Game::default(),
+            board: Board::default(),
             move_history: RepetitionTable::new(),
-            messages: VecDeque::new(),
-            r, s,
-            hash: Cache::megabytes(4),
-            zobrist_table: Arc::new(ZobristTable::new())
+            hash: Arc::new(RwLock::new(Cache::megabytes(4))),
+            zobrist_table: Arc::new(ZobristTable::new()),
+            search_thread: None,
         };
-        eng.game.zobrist = Some((eng.zobrist_table.clone(), eng.game.calculate_zobrist(&eng.zobrist_table)));
+        eng.board.zobrist = Some((eng.zobrist_table.clone(), eng.board.calculate_zobrist(&eng.zobrist_table)));
         eng
     }
 
-    pub fn run(&mut self) {
-        /*panic::set_hook(Box::new(|panic_info| {
-            if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
-                info!("panic occurred: {s:?}");
-                println!("panic occurred: {s:?}");
-            } else {
-                info!("panic occurred");
-                println!("panic occurred");
+    pub fn run(&mut self, input: std::io::Stdin) {
+        for line_m in input.lines() {
+            let line = line_m.unwrap();
+
+            //println!("received line: {}", line);
+
+            let command = uci::parse_command(&line);
+
+            if let Ok(cmd) = command {
+                self.run_command(cmd);
             }
-        }));*/
-        while let Some(msg) = self.dequeue_message() {
-            match msg {
-                EngineMsg::SetBoard{ pos, moves } => {
-                    self.set_position(pos, &moves);
-                },
-                EngineMsg::Search(ref info) => {
-                    self.start_search(info);
-                },
-                EngineMsg::Print => {
-                    println!("{}", self.game.beautiful_print());
-                },
-                EngineMsg::IsReady => {
-                    println!("readyok");
-                }
-                _ => {}
+            else if let Err(err) = command {
+                println!("Error parsing command: {}", err);
+            }
+        }
+    }
+
+    fn run_command(&mut self, cmd: Command) {
+        match cmd {
+            Command::Uci => {
+                println!("id name bishop 1.0");
+                println!("id author Geile Siech");
+                println!("uciok");
+            },
+            Command::IsReady => {
+                println!("readyok");
+            },
+            Command::Position { pos, moves } => {
+                self.set_position(pos, &moves);
+            },
+            Command::Go(si) => {
+                self.stop_search();
+                self.start_search(&si);
+            },
+            Command::Stop => {
+                self.stop_search();
+            },
+            Command::Quit => {
+                exit(0);
+            }
+            _ => {
+                println!("not yet implemented!");
             }
         }
     }
 
-    fn set_position(&mut self, pos: Game, moves: &Vec<String>) {
-        self.game = pos;
-        self.game.zobrist = Some((self.zobrist_table.clone(), self.game.calculate_zobrist(&self.zobrist_table)));
+
+    fn set_position(&mut self, pos: Board, moves: &Vec<String>) {
+        self.board = pos;
+
+        self.board.zobrist = Some((self.zobrist_table.clone(), self.board.calculate_zobrist(&self.zobrist_table)));
         self.move_history.clear();
-        self.move_history.increment(self.game.calculate_zobrist(&self.zobrist_table));
+        self.move_history.increment(self.board.calculate_zobrist(&self.zobrist_table));
         for mov in moves {
-            let m = self.game.parse_move(&mov);
+            let m = self.board.parse_move(&mov);
             if let Ok(mm) = m {
-                self.game.apply(mm);
-                self.move_history.increment(self.game.calculate_zobrist(&self.zobrist_table));
+                self.board.apply(mm);
+                self.move_history.increment(self.board.calculate_zobrist(&self.zobrist_table));
             }
             else {
-                println!("{}", self.game.beautiful_print());
+                println!("{}", self.board.beautiful_print());
                 println!("error parsing move {}", mov);
                 break;
             }
         }
     }
 
-    /**
-     * blocks until a message is available
-     */
-    fn dequeue_message(&mut self) -> Option<EngineMsg> {
-        if self.messages.is_empty() {
-            self.r.recv().ok()
-        }
-        else {
-            self.messages.pop_front()
-        }
+    fn start_search(&mut self, si: &SearchInfo) {
+        let (snd, rcv) = mpsc::channel::<SearchMessage>();
+
+        let join_handle = 
+            if si.perft {
+                let depth = si.depth.unwrap_or(0);
+                let mut pc = SearchControl::new_perft(&self.board, rcv, depth);
+                let search_rtn = move || pc.perft(depth);
+                thread::spawn(search_rtn)
+            }
+            else {
+                let mut sc = SearchControl::new(&self.board, rcv, self.hash.clone());
+                sc.hash = self.hash.clone();
+                sc.movetime = si.movetime.map(|ms| Duration::from_millis(ms as _));
+                if self.board.turn == WHITE {
+                    sc.remaining_time = si.wtime.map(|ms| Duration::from_millis(ms as _));
+                }
+                else if self.board.turn == BLACK {
+                    sc.remaining_time = si.btime.map(|ms| Duration::from_millis(ms as _));
+                }
+                let search_rtn = move || sc.iterative_deepening(None);
+                thread::spawn(search_rtn)
+            };
+
+        self.search_thread = Some(SearchThread {
+            channel: snd,
+            join_handle
+        });
+        //self.search_threads.push(st);
     }
 
-    #[allow(unused)]
-    fn try_dequeue_message(&mut self) -> Option<EngineMsg> {
-        if self.messages.is_empty() {
-            self.r.try_recv().ok()
-        }
-        else {
-            self.messages.pop_front()
+    fn stop_search(&mut self) {
+        if let Some(st) = std::mem::replace(&mut self.search_thread, None) {
+            let _msg_res = st.channel.send(SearchMessage::Stop);
+            let _res = st.join_handle.join().unwrap();
         }
     }
 
-    fn reconstruct_pv(game: &Game, hash: &Cache) -> Vec<Move> {
+    fn reconstruct_pv(game: &Board, hash: &Cache) -> Vec<Move> {
         let mut pv: Vec<Move> = Vec::new();
         let mut g = game.clone();
 
@@ -192,269 +228,4 @@ impl Engine {
         }
         return pv;
     }
-
-    fn start_search(&mut self, si: &SearchInfo) {
-        if let None = self.game.zobrist {
-            self.game.zobrist = Some((self.zobrist_table.clone(), self.game.calculate_zobrist(&self.zobrist_table)));
-        }
-        let side = self.game.turn;
-        let receiver = &mut self.r;
-        let messages = &mut self.messages;
-        let before = Instant::now();
-        let mut depth = 1;
-        let mut check_fn =
-            || -> bool {
-                if !messages.is_empty() {
-                    if let EngineMsg::Stop = messages[0] {
-                        return true;
-                    }
-                }
-                if let Ok(msg) = receiver.try_recv() {
-                    if let EngineMsg::Stop = msg {
-                        messages.push_back(msg);
-                        return true;
-                    }
-                    if let EngineMsg::IsReady = msg {
-                        println!("readyok");
-                        return false;
-                    }
-                    else {
-                        messages.push_back(msg);
-                    }
-                }
-
-                if let Some(millis) = si.movetime {
-                    if before.elapsed() > Duration::from_millis(millis as _) {
-                        return true;
-                    }
-                }
-                else if side == WHITE {
-                    if let Some(wtime) = si.wtime {
-                        if before.elapsed() > Duration::from_millis((wtime / 17) as _) {
-                            return true;
-                        }
-                    }
-                }
-                else if side == BLACK {
-                    if let Some(btime) = si.btime {
-                        if before.elapsed() > Duration::from_millis((btime / 17) as _) {
-                            return true;
-                        }
-                    }
-                }
-                return false;
-            };
-
-        let mut sc = SearchControl::new(&mut check_fn, &mut self.move_history, 0);
-        let mut best_move: Option<Move> = None;
-        let mut best_val: PosValue;
-
-        sc.halfmove_age = (self.game.turn_number * 2 + if self.game.turn == BLACK { 1 } else { 0 }) as _;
-
-        if si.perft {
-            if let Some(dep) = si.depth {
-                depth = dep;
-                sc.set_depth(depth);
-            }
-            let invalid = perft(&mut self.game, &mut sc, depth as i32);
-            if !invalid {
-                let elapsed = before.elapsed();
-                let nodes = sc.nodes;
-                let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64;
-                println!("info depth {} nodes {} nps {}", depth, nodes, nps);
-            }
-            return;
-        }
-
-        let mut alpha = crate::evaluate::MIN_VALUE;
-        let mut beta = crate::evaluate::MAX_VALUE;
-        let window_size = 10;
-        let mut is_windowed = false;
-
-        loop {
-
-            sc.set_depth(depth);
-
-            let search_result = search(&mut self.game, &mut sc, &mut self.hash, alpha, beta, depth as i32);
-
-            if let SearchResult::Invalid = search_result {
-                if is_windowed {
-                    alpha = crate::evaluate::MIN_VALUE;
-                    beta = crate::evaluate::MAX_VALUE;
-                    is_windowed = false;
-                    continue;
-                }
-            }
-
-            if let SearchResult::Finished(bm, bv) = search_result {
-                info!("depth: {} bm: {}, bv: {}", depth, bm.to_string(), bv);
-
-                if bv >= beta || bv <= alpha {
-                    alpha = crate::evaluate::MIN_VALUE;
-                    beta = crate::evaluate::MAX_VALUE;
-                    is_windowed = false;
-                    continue;
-                }
-                else {
-                    //alpha = crate::evaluate::MIN_VALUE;
-                    //beta = crate::evaluate::MAX_VALUE;
-                    is_windowed = true;
-                    alpha = bv - window_size;
-                    beta = bv + window_size;
-                }
-
-                best_move = Some(bm);
-                best_val = bv;
-
-                let elapsed = before.elapsed();
-                let nodes = sc.nodes;
-                let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64;
-                let cp = best_val as i64;
-                let hashfull = self.hash.fullness_permill();
-                
-                let pv_array = Self::reconstruct_pv(&self.game, &self.hash);
-                let pv = &pv_array.iter().filter(|&m| !m.is_null()).map(|m| m.to_string()).fold(String::new(), |a, b| a + " " + &b)[0..];
-
-                if let Some(plies) = crate::evaluate::is_mate_in_p1(bv) {
-                    println!("plies = {}", plies);
-                    let turns = (plies + 1) / 2;
-                    let infostring = format!("info depth {} score mate {} time {} nodes {} nps {} hashfull {} pv{}", depth, turns, elapsed.as_millis(), nodes, nps, hashfull, pv);
-                    println!("{}", infostring);
-                    info!("{}", infostring);
-                    if si.depth.is_none() && plies.abs() * 2 <= depth {
-                        break;
-                    }
-                }
-                else {
-                    let infostring = format!("info depth {} score cp {} time {} nodes {} nps {} hashfull {} pv{}", depth, cp, elapsed.as_millis(), nodes, nps, hashfull, pv);
-                    println!("{}", infostring);
-                    info!("{}", infostring);
-                }
-                depth += 1;
-            }
-            else if let SearchResult::Cancelled(Some((bm, _bv))) = search_result {
-                if best_move.is_none() {
-                    best_move = Some(bm);
-                    //best_val = bv;
-                }
-                break;
-            }
-            else {
-                break;
-            }
-
-            if let Some(d) = si.depth {
-                if depth > d {
-                    break;
-                }
-            }
-        }
-
-        if let Some(best) = best_move {
-            info!("bestmove {}", best.to_string());
-            println!("bestmove {}", best.to_string());
-        }
-    }
 }
-
-/*
-
-fn dequeue_message(queue: &mut VecDeque<EngineMsg>, r: &Receiver<EngineMsg>) -> Option<EngineMsg> {
-    if queue.is_empty() {
-        r.recv().ok()
-    }
-    else {
-        queue.pop_front()
-    }
-}
-
-pub fn run_engine(r: Receiver<EngineMsg>, _s: Sender<InterfaceMsg>) {
-    let mut game = Game::default();
-    let mut messages: VecDeque<EngineMsg> = VecDeque::new();
-    loop {
-        let msg = dequeue_message(&mut messages, &r).unwrap();
-
-        match msg {
-            EngineMsg::SetBoard(g) => {
-                game = g;
-            },
-            EngineMsg::SetPiece(_) => { println!("SetPiece") },
-            EngineMsg::Search(info) => {
-
-                match info {
-                    SearchInfo::Depth(depth) => {
-                        let mut check_fn = || -> bool {
-                            if let Ok(msg) = r.try_recv() {
-                                if let EngineMsg::Stop = msg {
-                                    return true;
-                                }
-                                else {
-                                    messages.push_back(msg);
-                                }
-                            }
-                            return false;
-                        };
-                        let mut sc = SearchControl{ nodes: 0, check: &mut check_fn };
-                        let before = Instant::now();
-                        let best_move = search(&game, &mut sc, depth as i32);
-
-                        let time = before.elapsed();
-                        let nps = (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64;
-                        info!("bestmove {}", best_move.to_string());
-                        info!("searched {} nodes in {} ms ({} nodes/s)", sc.nodes, time.as_millis(), (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64);
-
-                        println!("info nps {}", nps.to_string());
-                        println!("bestmove {}", best_move.to_string());
-                    },
-                    SearchInfo::Movetime(millis) => {
-                        let before = Instant::now();
-                        let mut depth = 1;
-                        let mut check_fn = || -> bool {
-                            if let Ok(msg) = r.try_recv() {
-                                if let EngineMsg::Stop = msg {
-                                    return true;
-                                }
-                                else {
-                                    messages.push_back(msg);
-                                }
-                            }
-                            if before.elapsed() > Duration::from_millis(millis as _) {
-                                return true;
-                            }
-                            return false;
-                        };
-
-                        let mut sc = SearchControl{ nodes: 0, check: &mut check_fn };
-                        let mut best_move = Move::default();
-                        loop {
-                            let bm = search(&game, &mut sc, depth as i32);
-                            if bm != Move::default() {
-                                best_move = bm;
-                            }
-                            else {
-                                break;
-                            }
-                            depth += 1;
-                        }
-
-                        println!("bestmove {}", best_move.to_string());
-                    },
-                    _ => {}
-                }
-            },
-            EngineMsg::Ping => { println!("Ping") },
-            EngineMsg::Stop => { println!("Stop") },
-            EngineMsg::NewGame => {
-                //println!("NewGame")
-            },
-            EngineMsg::GetState => { println!("GetState") },
-            EngineMsg::GetInfo => { println!("GetInfo") },
-        }
-
-        //println!("{}", game.beautiful_print());
-
-        //let moves = generate_moves(&game, WHITE);
-    }
-}
-
-*/

+ 16 - 16
src/evaluate.rs

@@ -1,4 +1,4 @@
-use game::*;
+use board::Board; 
 use bitboard::*;
 use movegen::*;
 
@@ -91,7 +91,7 @@ pub fn decrease_mate_in(mut val: PosValue) -> PosValue {
 
 
 
-fn side_value(game: &Game, side: Side) -> u32 {
+fn side_value(game: &Board, side: Side) -> u32 {
     let adv_pawn_mask = match side { WHITE => ROW_7, BLACK => ROW_2 };
     let semi_adv_pawn_mask = match side { WHITE => ROW_6, BLACK => ROW_3 };
     let advanced_pawns = (game.get_piece(PAWN, side) & adv_pawn_mask).count_ones() * 200;
@@ -99,7 +99,7 @@ fn side_value(game: &Game, side: Side) -> u32 {
     advanced_pawns + semi_advanced_pawns
 }
 
-fn pawn_structure(game: &Game, side: Side) -> PosValue {
+fn pawn_structure(game: &Board, side: Side) -> PosValue {
     let pawns = game.pawns(side);
     let all_own_pieces = game.get_all_side(side);
     let all_pieces = all_own_pieces | game.get_all_side(!side);
@@ -119,7 +119,7 @@ fn pawn_structure(game: &Game, side: Side) -> PosValue {
     return (protections * 15 + pushable_pawns * 10) as _;
 }
 
-fn knight_value(game: &Game, side: Side) -> PosValue {
+fn knight_value(game: &Board, side: Side) -> PosValue {
     let knights = game.get_piece(KNIGHT, side);
     let ks = BitboardIterator(knights);
     let mut k_attacks: u32 = 0;
@@ -138,7 +138,7 @@ fn knight_value(game: &Game, side: Side) -> PosValue {
     center_knights * 6 + k_attacks as PosValue + ((num_knights * 75 * num_opp_pawns) / 8) as PosValue
 }
 
-pub fn material_value(game: &Game, side: Side) -> PosValue {
+pub fn material_value(game: &Board, side: Side) -> PosValue {
     (game.get_piece(PAWN, side).count_ones() * 100
         + game.get_piece(KNIGHT, side).count_ones() * 220
         + game.get_piece(BISHOP, side).count_ones() * 320
@@ -146,7 +146,7 @@ pub fn material_value(game: &Game, side: Side) -> PosValue {
         + game.get_piece(QUEEN, side).count_ones() * 800) as PosValue
 }
 
-fn castling_rights(game: &Game, side: Side) -> PosValue {
+fn castling_rights(game: &Board, side: Side) -> PosValue {
     let castling_value = 15;
 
     (if side == WHITE {
@@ -162,7 +162,7 @@ fn castling_rights(game: &Game, side: Side) -> PosValue {
     * castling_value) as PosValue
 }
 
-fn king_safety(game: &Game, side: Side) -> PosValue {
+fn king_safety(game: &Board, side: Side) -> PosValue {
     let king = game.get_piece(KING, side);
     let king_leftright = west_one(king) | king | east_one(king);
     let area = north_one(king_leftright) | king_leftright | south_one(king_leftright);
@@ -193,14 +193,14 @@ fn king_safety(game: &Game, side: Side) -> PosValue {
     }
 }
 
-fn king_there(game: &Game, side: Side) -> PosValue {
+fn king_there(game: &Board, side: Side) -> PosValue {
     if game.get_piece(KING, side) != 0 { 10000 } else { 0 }
 }
 
 ///
 /// The evaluate function of the engine
 /// 
-pub fn evaluate(game: &Game) -> PosValue {
+pub fn evaluate(game: &Board) -> PosValue {
     let sv = side_value(game, game.turn) as PosValue - side_value(game, !game.turn) as PosValue;
     let material_value_us = material_value(game, game.turn);
     let material_value_them = material_value(game, !game.turn);
@@ -227,7 +227,7 @@ pub fn evaluate(game: &Game) -> PosValue {
     return mat_val + ge;
 }
 
-pub fn game_lateness(game: &Game) -> i32 {
+pub fn game_lateness(game: &Board) -> i32 {
     let all_pieces = game.get_all_side(WHITE) | game.get_all_side(BLACK);
     let n_pieces = all_pieces.count_ones() as i32;
     let captured_pieces = 32 - n_pieces;
@@ -242,7 +242,7 @@ pub fn game_lateness(game: &Game) -> i32 {
 ///
 /// counts the number of moves the rooks of side can do
 /// 
-fn rook_moves(game: &Game, side: Side) -> PosValue {
+fn rook_moves(game: &Board, side: Side) -> PosValue {
     let rooks = game.rooks(side);
     let rook_moves = count_sliding_move_bitboard(
         game.get_all_side(side),
@@ -257,7 +257,7 @@ fn rook_moves(game: &Game, side: Side) -> PosValue {
 ///
 /// counts the number of moves the bishops of side can do
 /// 
-fn bishop_moves(game: &Game, side: Side) -> PosValue {
+fn bishop_moves(game: &Board, side: Side) -> PosValue {
     let bishops = game.bishops(side);
     let bishop_moves = count_sliding_move_bitboard(
         game.get_all_side(side),
@@ -272,7 +272,7 @@ fn bishop_moves(game: &Game, side: Side) -> PosValue {
 ///
 /// counts the number of moves the queens of side can do
 /// 
-fn queen_moves(game: &Game, side: Side) -> PosValue {
+fn queen_moves(game: &Board, side: Side) -> PosValue {
     let queens = game.queens(side);
     let queen_moves = count_sliding_move_bitboard(
         game.get_all_side(side),
@@ -284,7 +284,7 @@ fn queen_moves(game: &Game, side: Side) -> PosValue {
     return queen_moves as _;
 }
 
-pub fn late_game_eval(game: &Game) -> PosValue {
+pub fn late_game_eval(game: &Board) -> PosValue {
     let pp = pawn_push_value(game, game.turn) - pawn_push_value(game, !game.turn);
     let bp = blocked_pawns(game, game.turn) - blocked_pawns(game, !game.turn);
     let rook_moves = rook_moves(game, game.turn) - rook_moves(game, !game.turn);
@@ -292,7 +292,7 @@ pub fn late_game_eval(game: &Game) -> PosValue {
     pp + bp + rook_moves * 3
 }
 
-pub fn pawn_push_value(game: &Game, side: Side) -> PosValue {
+pub fn pawn_push_value(game: &Board, side: Side) -> PosValue {
     let val = if side == WHITE {
         (game.get_piece(PAWN, side) & ROW_3).count_ones() * 10
         + (game.get_piece(PAWN, side) & ROW_4).count_ones() * 20
@@ -310,7 +310,7 @@ pub fn pawn_push_value(game: &Game, side: Side) -> PosValue {
     val as PosValue
 }
 
-pub fn blocked_pawns(game: &Game, side: Side) -> PosValue {
+pub fn blocked_pawns(game: &Board, side: Side) -> PosValue {
     let pawns = game.get_piece(PAWN, side);
     let pushed_pawns = if side == WHITE {
         north_one(pawns)

+ 0 - 950
src/game.rs

@@ -1,950 +0,0 @@
-use bitboard::*;
-use movegen::*;
-use log::info;
-use std::sync::Arc;
-use zobrist::{ZobristTable};
-use zobrist;
-
-use crate::movegen;
-
-#[derive(Clone)]
-pub struct Game
-{
-    pub pieces: [Bitboard; 12],
-    pub turn: Side,
-
-    /// None if no en passant move is possible,
-    /// otherwise contains the file of the last (double-) pushed pawn
-    pub en_passant: Option<u8>,
-
-    ///
-    /// castling rights in the following order
-    /// white kingside, white queenside, black kingside, black queenside
-    /// 
-    pub castling_rights: [bool; 4],
-
-    //
-    // non-hashed part
-    // vvv
-
-
-    pub turn_number: i32,
-    pub halfmoves_since_last_event: i8,
-    pub zobrist: Option<(Arc<ZobristTable>, zobrist::Hash)>
-}
-
-impl Default for Game {
-    fn default () -> Game {
-        Game {
-            pieces: [
-                ROW_2,
-                0x42,
-                0x24,
-                0x81,
-                0x10,
-                0x08,
-                ROW_7,
-                0x42 << 56,
-                0x24 << 56,
-                0x81 << 56,
-                0x10 << 56,
-                0x08 << 56,
-            ],
-            turn: WHITE,
-            en_passant: None,
-            castling_rights: [true; 4],
-            halfmoves_since_last_event: 0,
-            turn_number: 0,
-            zobrist: None
-        }
-    }
-}
-
-
-impl std::hash::Hash for Game {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        if let Some((ref _zt, ref zb)) = &self.zobrist {
-            zb.hash(state);
-        }
-    }
-}
-
-impl PartialEq for Game {
-    fn eq(&self, other: &Self) -> bool {
-        self.pieces == other.pieces &&
-        self.turn == other.turn &&
-        self.en_passant == other.en_passant &&
-        self.castling_rights == other.castling_rights
-    }
-}
-
-impl Eq for Game {
-}
-
-
-impl Game {
-    pub fn empty() -> Game {
-        Game {
-            pieces: [0; 12],
-            turn: WHITE,
-            en_passant: None,
-            castling_rights: [true; 4],
-            halfmoves_since_last_event: 0,
-            turn_number: 0,
-            zobrist: None
-        }
-    }
-
-    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 position = fen[0];
-        let turn = fen[1];
-        let castling_rights = fen[2];
-        let en_passant = fen[3];
-        let halfmoves_since_last_event = fen[4];
-        let turn_number = fen[5];
-
-        {
-            let rows = position.split('/');
-            let mut row_index: i32 = 7;
-            for row in rows {
-                if row_index < 0 {
-                    return None;
-                }
-                let mut col_index = 0;
-                for c in row.chars() {
-
-                    if col_index >= 8 {
-                        return None;
-                    }
-
-                    let bit_to_set = from_indices(col_index, row_index as _);
-                    match c {
-                        'p' => { *game.pawns_mut(BLACK) |= bit_to_set },
-                        'n' => { *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 },
-                        'N' => { *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;
-                }
-                row_index -= 1;
-            }
-        }
-
-        if turn == "b" {
-            game.turn = BLACK
-        }
-
-        // parse castling rights
-        game.castling_rights[0] = castling_rights.contains('K');
-        game.castling_rights[1] = castling_rights.contains('Q');
-        game.castling_rights[2] = castling_rights.contains('k');
-        game.castling_rights[3] = castling_rights.contains('q');
-
-        // 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::<i8>().unwrap_or(0);
-        game.turn_number = turn_number.parse::<i32>().unwrap_or(0);
-        
-        Some(game)
-    }
-
-    pub fn from_fen_str(fen: &str) -> Option<Game> {
-        let fen_parts = fen.split_whitespace();
-        Self::from_fen(fen_parts.collect::<Vec<&str>>().as_slice())
-    }
-
-    pub fn parse_move(&self, mov: &str) -> Result<Move, ()> {
-        if mov.len() < 4 {
-            Err(())
-        }
-        else {
-            let origin = Move::parse_square(&mov[0..2]);
-            let target = Move::parse_square(&mov[2..4]);
-
-            let promote_to = if mov.len() == 5 {
-                Some(match mov.chars().nth(4) {
-                    Some('Q') | Some('q') => QUEEN,
-                    Some('N') | Some('n') => KNIGHT,
-                    Some('B') | Some('b') => BISHOP,
-                    Some('R') | Some('r') => ROOK,
-                    _ => panic!("invalid move: {}", mov)
-                })
-            }
-            else {
-                None
-            };
-
-            if let (Some(from), Some(to)) = (origin, target) {
-                let (piece_type, side) = self.get_square(from);
-                //println!("from: {}", from);
-
-                let (cap, cs) = self.get_square(to);
-                let captured = if cap != NO_PIECE { Some(cap) } else { None };
-
-                if cap != NO_PIECE && cs == side {
-                    // cannot capture own piece
-                    return Err(());
-                }
-
-                if piece_type == NO_PIECE {
-                    return Err(());
-                }
-
-                if side != self.turn { // wrong side to move
-                    return Err(());
-                }
-
-                if let Some(promote_to) = promote_to {
-                    if piece_type != PAWN {
-                        return Err(());
-                    }
-                    Ok(Move::Promotion{ mov: SimpleMove{ from, to }, pc: PieceCaptureType::new(promote_to, captured) })
-                }
-                else {
-                    if piece_type == KING && (from as i32 - to as i32).abs() == 2 {
-                        let left = from < to;
-                        //println!("OMG castling!");
-                        return Ok(Move::Castling{ mov: SimpleMove { from, to }, side, left });
-                    }
-
-                    // pawn capture
-                    let (from_file, from_row) = indices_from_square(from);
-                    let (target_file, _target_row) = indices_from_square(to);
-                    if piece_type == PAWN && from_file != target_file {
-                        let others = self.get_all_side(!side);
-                        if others & from_square(to) == 0 {
-                            let beaten = square_from_indices(target_file, from_row);
-                            return Ok(Move::EnPassant{ mov: SimpleMove{ from, to }, beaten });
-                        }
-                    }
-
-                    //println!("pt: {}", piece_type);
-                    Ok(Move::Default{ mov: SimpleMove{ from, to }, pc: PieceCaptureType::new(piece_type, captured) })
-                }
-            }
-            else {
-                Err(())
-            }
-        }
-    }
-
-    pub fn bitboard(&self, pt: PieceType, side: Side) -> Bitboard {
-        if side == BLACK {
-            self.pieces[(pt + 6) as usize]
-        } else {
-            self.pieces[pt as usize]
-        }
-    }
-
-    /**
-     * finds the first bitboard that has overlapping bits with the given bitboard
-     * and returns the piece type of that bitboard
-     */
-    pub fn find_piece(&self, bitboard: Bitboard) -> Option<PieceType> {
-        for i in 0..12 {
-            if self.pieces[i] & bitboard != 0 {
-                return Some(if i >= 6 { i - 6 } else { i } as PieceType);
-            }
-        }
-        return None;
-    }
-
-    /**
-     * \return bitboard containig all occupied squares
-     */
-    pub fn occupied(&self) -> Bitboard {
-        self.pieces.iter().fold(0, |a, b| { a | b } )
-    }
-
-    pub fn pawns(&self, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[0],
-            BLACK => self.pieces[6],
-        }
-    }
-
-    pub fn knights(&self, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[1],
-            BLACK => self.pieces[7],
-        }
-    }
-
-    pub fn bishops(&self, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[2],
-            BLACK => self.pieces[8],
-        }
-    }
-
-    pub fn rooks(&self, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[3],
-            BLACK => self.pieces[9],
-        }
-    }
-
-    pub fn queens(&self, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[4],
-            BLACK => self.pieces[10],
-        }
-    }
-
-    pub fn kings(&self, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[5],
-            BLACK => self.pieces[11],
-        }
-    }
-
-    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_piece(&self, piece_type: PieceType, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[piece_type as usize],
-            BLACK => self.pieces[piece_type as usize + 6],
-        }
-    }
-
-    pub fn set_piece(&mut self, piece_type: PieceType, side: Side, bitboard: Bitboard) {
-        match side {
-            WHITE => self.pieces[piece_type as usize] = bitboard,
-            BLACK => self.pieces[piece_type as usize + 6] = bitboard,
-        }
-    }
-
-    pub fn get_piece_mut(&mut self, piece_type: PieceType, side: Side) -> &mut Bitboard {
-        match side {
-            WHITE => &mut self.pieces[piece_type as usize],
-            BLACK => &mut self.pieces[piece_type as usize + 6],
-        }
-    }
-
-    pub fn get_all_side(&self, side: Side) -> Bitboard {
-        match side {
-            WHITE => self.pieces[0] | self.pieces[1] | self.pieces[2] | self.pieces[3] |
-                self.pieces[4] | self.pieces[5],
-            BLACK => self.pieces[6] | self.pieces[7] | self.pieces[8] | self.pieces[9] |
-                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);
-            }
-        }
-        for i in 0..6 {
-            if self.pieces[i + 6] & square_mask != 0 {
-                return (i as PieceType, BLACK);
-            }
-        }
-        return (NO_PIECE, WHITE);
-    }
-
-    /**
-     * @brief masks all bitboards.
-     */
-    pub fn apply_mask(&mut self, mask: Bitboard) {
-        for board in &mut self.pieces {
-            *board &= mask;
-        }
-    }
-
-    /**
-     * @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, 7 - 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");
-
-        let chars = ['K', 'Q', 'k', 'q'];
-        let mut cr: [char; 4] = [' '; 4];
-        for i in 0..4 {
-            cr[i] = if self.castling_rights[i] {
-                chars[i]
-            } else { ' ' };
-        }
-
-        if self.turn == WHITE {
-            board.push_str("\nWhite to move\n");
-        }
-        else {
-            board.push_str("\nBlack to move\n");
-        }
-
-        board.push_str(&format!("\nCastling      : {}{}{}{}", cr[0], cr[1], cr[2], cr[3]));
-        board.push_str(&format!("\nEn Passant    : {}", self.en_passant.map(|f| f.to_string()).unwrap_or("-".to_owned())));
-        board.push_str(&format!("\nTurn number   : {}", self.turn_number));
-        board.push_str(&format!("\nHalfmoves slt : {}", self.halfmoves_since_last_event));
-
-        return board;
-    }
-
-    pub fn update_zobrist(&mut self, h: zobrist::Hash) {
-        if let Some((ref _zt, ref mut zh)) = self.zobrist {
-            *zh ^= h;
-        }
-    }
-
-    pub fn calculate_zobrist(&self, zt: &ZobristTable) -> zobrist::Hash {
-        let mut hash: zobrist::Hash = 0;
-        for pt in 0..6 {
-            let bb_it_w = BitboardIterator(self.get_piece(pt, WHITE));
-            let bb_it_b = BitboardIterator(self.get_piece(pt, BLACK));
-            for bb_square in bb_it_w {
-                hash ^= zt.piece_hash(pt as _, WHITE, square(bb_square));
-            }
-            for bb_square in bb_it_b {
-                hash ^= zt.piece_hash(pt as _, BLACK, square(bb_square));
-            }
-        }
-
-        for cr_id in 0..4 {
-            if self.castling_rights[cr_id] {
-                hash ^= zt.castling_rights_hash(cr_id as _);
-            }
-        }
-
-        if let Some(file) = self.en_passant {
-            hash ^= zt.en_passant_hash(file);
-        }
-
-        if self.turn {
-            hash ^= zt.turn_hash();
-        }
-
-        return hash;
-    }
-
-    pub fn is_zobrist_correct(&self) -> bool {
-        if let Some((ref zt, zv)) = self.zobrist {
-            self.calculate_zobrist(zt) == zv
-        }
-        else {
-            false
-        }
-    }
-
-    ///
-    /// TODO: improve
-    /// 
-    pub fn is_legal(&self, mov: Move) -> bool {
-        /*match mov {
-            Move::Default { mov, pc } => {
-                let piece_type = pc.piece_type();
-                let capture = pc.capture_type();
-
-                let legal_from = self.get_piece(piece_type, self.turn);
-
-                match piece_type {
-                    PAWN => {
-                        if pawn
-                    }
-                }
-            }
-        }*/
-        movegen::generate_legal_moves(&mut self.clone(), self.turn, false).contains(&mov)
-    }
-
-    pub fn apply(&mut self, mov: Move) -> MoveUndo {
-        /*if !self.is_zobrist_correct() {
-            println!("{}", self.beautiful_print());
-            println!("incorrect zobrist before apply {} {:?}", mov.to_string(), mov);
-            info!("incorrect zobrist before apply");
-            panic!("zobrist");
-            let val = if let Some((ref zt, _zv)) = self.zobrist {
-                self.calculate_zobrist(zt)
-            } else { 0 };
-            if let Some((ref _zt, ref mut zv)) = self.zobrist {
-                *zv = val;
-            }
-        }*/
-
-        // save irrecoverable values
-        let castling_rights_before = self.castling_rights.clone();
-        let halfmoves_since_last_event_before = self.halfmoves_since_last_event.clone();
-        let en_passant_before = self.en_passant;
-
-        let side = self.turn;
-        let others = self.get_all_side(!side);
-
-        if !mov.is_null() {
-            match mov {
-                Move::Default{ mov, pc } => {
-                    let pt = pc.piece_type();
-
-                    if pt == KING {
-                        // invalidate castling rights
-                        self.castling_rights[if side == BLACK { 2 } else { 0 }] = false;
-                        self.castling_rights[if side == BLACK { 3 } else { 1 }] = false;
-                    }
-
-                    // invalidate castling rights
-                    if mov.from == square_from_indices(7, 0) || mov.to == square_from_indices(7, 0) {
-                        self.castling_rights[0] = false;
-                    }
-                    if mov.from == square_from_indices(0, 0) || mov.to == square_from_indices(0, 0) {
-                        self.castling_rights[1] = false;
-                    }
-                    if mov.from == square_from_indices(7, 7) || mov.to == square_from_indices(7, 7) {
-                        self.castling_rights[2] = false;
-                    }
-                    if mov.from == square_from_indices(0, 7) || mov.to == square_from_indices(0, 7) {
-                        self.castling_rights[3] = false;
-                    }
-
-                    // if it is a capture
-                    if from_square(mov.to) & others != 0 {
-                        let (other_piece, other_side) = self.get_square(mov.to);
-
-                        // update zobrist
-                        if let Some((ref zt, ref mut zv)) = self.zobrist {
-                            let hup = zt.piece_hash(other_piece, other_side, mov.to);
-                            *zv ^= hup;
-                        }
-                        *self.get_piece_mut(other_piece, other_side) &= !from_square(mov.to);
-                    }
-
-
-                    // 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;
-                        // check for double push
-                        if i32::abs(row1 as i32 - row2 as i32) >= 2 {
-                            let opponent_pawns = self.get_piece(PAWN, !side);
-                            let target_bb = from_square(mov.to);
-                            if (west_one(target_bb) | east_one(target_bb)) & opponent_pawns != 0 {
-                                self.en_passant = Some(indices_from_square(mov.to).0);
-                            }
-                        }
-                    }
-
-                    let moved_piece = mov.apply_to(self.get_piece(pt, side));
-                    self.set_piece(pt, side, moved_piece);
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let hup = zt.piece_hash(pt, side, mov.from) ^ zt.piece_hash(pt, side, mov.to);
-                        *zv ^= hup;
-                    }
-                },
-                Move::Castling { mov:_, side, left } => {
-                    // invalidate future castling rights
-                    self.castling_rights[if side == BLACK { 2 } else { 0 }] = false;
-                    self.castling_rights[if side == BLACK { 3 } else { 1 }] = false;
-
-                    let king = self.get_piece(KING, side);
-                    let rook = if left {
-                        self.get_piece(ROOK, side) & (king << 4)
-                    }
-                    else {
-                        self.get_piece(ROOK, side) & (king >> 3)
-                    };
-
-                    let new_king = if left { king << 2 } else { king >> 2 };
-                    let new_rook = if left { rook >> 3 } else { rook << 2 };
-
-                    self.set_piece(KING, side, new_king);
-                    *self.get_piece_mut(ROOK, side) &= !rook;
-                    *self.get_piece_mut(ROOK, side) |= new_rook;
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let hupk = zt.piece_hash(KING, side, square(king)) ^ zt.piece_hash(KING, side, square(new_king));
-                        let hup = zt.piece_hash(ROOK, side, square(rook)) ^ zt.piece_hash(ROOK, side, square(new_rook));
-                        *zv ^= hup ^ hupk;
-                    }
-
-                    self.en_passant = None;
-                },
-                Move::EnPassant { mov, beaten } => {
-                    if let Some(_ep) = self.en_passant {
-                        *self.get_piece_mut(PAWN, side) &= !from_square(mov.from);
-                        *self.get_piece_mut(PAWN, side) |= from_square(mov.to);
-                        *self.get_piece_mut(PAWN, !side) &= !from_square(beaten);
-
-                        if let Some((ref zt, ref mut zv)) = self.zobrist {
-                            let hup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(PAWN, side, mov.to);
-                            let hupo = zt.piece_hash(PAWN, !side, beaten);
-                            *zv ^= hup ^ hupo;
-                        }
-
-                        self.en_passant = None;
-                    }
-                    else {
-                        info!("internal en passant error");
-                        panic!("internal en passant error");
-                    }
-                },
-                Move::Promotion { mov, pc } => {
-                    let promote_to = pc.piece_type();
-                    let captured = pc.capture_type();
-                    //if from_square(mov.to) & others != 0 {
-                    if let Some(pt) = captured {
-                        *self.get_piece_mut(pt, !side) &= !from_square(mov.to);
-
-                        if let Some((ref zt, ref mut zv)) = self.zobrist {
-                            let hup = zt.piece_hash(pt, !side, mov.to);
-                            *zv ^= hup;
-                        }
-                    }
-                    *self.get_piece_mut(PAWN, side) &= !from_square(mov.from);
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let hup = zt.piece_hash(PAWN, side, mov.from);
-                        *zv ^= hup;
-                    }
-
-                    match promote_to {
-                        QUEEN => { *self.queens_mut(side) |= from_square(mov.to); }
-                        ROOK => { *self.rooks_mut(side) |= from_square(mov.to); }
-                        BISHOP => { *self.bishops_mut(side) |= from_square(mov.to); }
-                        KNIGHT => { *self.knights_mut(side) |= from_square(mov.to); }
-                        _ => {
-                            info!("internal error");
-                        }
-                    }
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let hup = zt.piece_hash(promote_to, side, mov.to);
-                        *zv ^= hup;
-                    }
-                    self.en_passant = None;
-                },
-            }
-        }
-
-
-        if self.turn == BLACK {
-            self.turn_number += 1;
-        }
-
-        self.turn = !self.turn;
-        if let Some((ref zt, ref mut zv)) = self.zobrist {
-            let hup = zt.turn_hash();
-            let castling_hup = zt.all_castling_rights_hash(castling_rights_before) ^ zt.all_castling_rights_hash(self.castling_rights);
-            let enpass_hup = en_passant_before.map(|f| zt.en_passant_hash(f)).unwrap_or(0) ^ self.en_passant.map(|f| zt.en_passant_hash(f)).unwrap_or(0);
-            *zv ^= hup ^ castling_hup ^ enpass_hup;
-        }
-
-        /*if !self.is_zobrist_correct() {
-            println!("{}", self.beautiful_print());
-            println!("incorrect zobrist after apply {} {:?}", mov.to_string(), mov);
-            info!("incorrect zobrist after apply");
-            panic!("zobrist");
-            let val = if let Some((ref zt, _zv)) = self.zobrist {
-                self.calculate_zobrist(zt)
-            } else { 0 };
-            if let Some((ref _zt, ref mut zv)) = self.zobrist {
-                *zv = val;
-            }
-        }*/
-
-        MoveUndo {
-            castling_rights_before,
-            halfmoves_since_last_event_before,
-            en_passant_before,
-            mov
-        }
-    }
-
-
-    pub fn undo_move(&mut self, umov: MoveUndo) {
-
-        /*if !self.is_zobrist_correct() {
-            println!("{}", self.beautiful_print());
-            println!("incorrect zobrist before undo {} {:?}", umov.mov.to_string(), umov.mov);
-            info!("incorrect zobrist before undo {} {:?}", umov.mov.to_string(), umov.mov);
-            panic!("zobrist");
-        }*/
-
-        if let Some((ref zt, ref mut zv)) = self.zobrist {
-            let crhup = zt.all_castling_rights_hash(self.castling_rights) ^ zt.all_castling_rights_hash(umov.castling_rights_before);
-            let enpass_hup = self.en_passant.map(|f| zt.en_passant_hash(f)).unwrap_or(0) ^ umov.en_passant_before.map(|f| zt.en_passant_hash(f)).unwrap_or(0);
-            *zv ^= crhup ^ enpass_hup;
-        }
-
-        self.castling_rights = umov.castling_rights_before;
-        self.halfmoves_since_last_event = umov.halfmoves_since_last_event_before;
-        self.en_passant = umov.en_passant_before;
-        
-        let mov = umov.mov;
-
-        // the side that played the turn that is to be reverted
-        let side = !self.turn;
-
-        if !mov.is_null() {
-            match mov {
-                Move::Default{ mov, pc } => {
-                    let piece_type = pc.piece_type();
-                    let captured = pc.capture_type();
-
-                    let moved_piece_bb = self.get_piece_mut(piece_type, side);
-                    *moved_piece_bb &= !from_square(mov.to);
-                    *moved_piece_bb |= from_square(mov.from);
-
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let hup = zt.piece_hash(piece_type, side, mov.from) ^ zt.piece_hash(piece_type, side, mov.to);
-                        *zv ^= hup;
-                    }
-
-                    if let Some(pt) = captured {
-                        *self.get_piece_mut(pt, !side) |= from_square(mov.to);
-
-                        if let Some((ref zt, ref mut zv)) = self.zobrist {
-                            let hup = zt.piece_hash(pt, !side, mov.to);
-                            *zv ^= hup;
-                        }
-                    }
-                },
-                Move::Castling { mov: _, side, left } => {
-                    let king = self.get_piece(KING, side);
-
-                    let rook = if left {
-                        self.get_piece(ROOK, side) & (king >> 1)
-                    }
-                    else {
-                        self.get_piece(ROOK, side) & (king << 1)
-                    };
-
-                    let old_king = if left { king >> 2 } else { king << 2 };
-                    let old_rook = if left { rook << 3 } else { rook >> 2 };
-
-                    self.set_piece(KING, side, old_king);
-                    *self.get_piece_mut(ROOK, side) &= !rook;
-                    *self.get_piece_mut(ROOK, side) |= old_rook;
-
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let khup = zt.piece_hash(KING, side, square(old_king)) ^ zt.piece_hash(KING, side, square(king));
-                        let rhup = zt.piece_hash(ROOK, side, square(rook)) ^ zt.piece_hash(ROOK, side, square(old_rook));
-                        *zv ^= khup ^ rhup;
-                    }
-                },
-                Move::EnPassant { mov, beaten } => {
-                    *self.get_piece_mut(PAWN, side) |= from_square(mov.from);
-                    *self.get_piece_mut(PAWN, side) &= !from_square(mov.to);
-                    *self.get_piece_mut(PAWN, !side) |= from_square(beaten);
-
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let phup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(PAWN, side, mov.to);
-                        let chup = zt.piece_hash(PAWN, !side, beaten);
-                        *zv ^= phup ^ chup;
-                    }
-
-                    // should already be reverted
-                    //self.en_passant = Some(indices_from_square(beaten).0);
-                },
-                Move::Promotion { mov, pc } => {
-                    let promote_to = pc.piece_type();
-                    let captured = pc.capture_type();
-
-                    match promote_to {
-                        QUEEN => { *self.queens_mut(side) &= !from_square(mov.to); }
-                        ROOK => { *self.rooks_mut(side) &= !from_square(mov.to); }
-                        BISHOP => { *self.bishops_mut(side) &= !from_square(mov.to); }
-                        KNIGHT => { *self.knights_mut(side) &= !from_square(mov.to); }
-                        _ => {
-                            info!("internal error");
-                        }
-                    }
-                    *self.pawns_mut(side) |= from_square(mov.from);
-
-                    if let Some((ref zt, ref mut zv)) = self.zobrist {
-                        let hup = zt.piece_hash(PAWN, side, mov.from) ^ zt.piece_hash(promote_to, side, mov.to);
-                        *zv ^= hup;
-                    }
-
-                    if let Some(pt) = captured {
-                        *self.get_piece_mut(pt, !side) |= from_square(mov.to);
-
-                        if let Some((ref zt, ref mut zv)) = self.zobrist {
-                            let hup = zt.piece_hash(pt, !side, mov.to);
-                            *zv ^= hup;
-                        }
-                    }
-                },
-            }
-        }
-
-        self.turn = side;
-        if side == BLACK {
-            self.turn_number -= 1;
-        }
-
-        if let Some((ref zt, ref mut zv)) = self.zobrist {
-            *zv ^= zt.turn_hash();
-        }
-
-        /*if !self.is_zobrist_correct() {
-            println!("{}", self.beautiful_print());
-            println!("incorrect zobrist after undo {} {:?}", umov.mov.to_string(), umov.mov);
-            info!("incorrect zobrist after undo {} {:?}", umov.mov.to_string(), umov.mov);
-            panic!("zobrist");
-        }*/
-
-        /*let val = if let Some((ref zt, _zv)) = self.zobrist {
-            self.calculate_zobrist(zt)
-        } else { 0 };
-        if let Some((ref _zt, ref mut zv)) = self.zobrist {
-            *zv = val;
-        }*/
-    }
-    
-    pub fn to_vector(&self) -> Vec<f64> {
-        let mut v = Vec::new();
-        for side in [WHITE, BLACK] {
-            for pt in 0..6 {
-                for sq in 0..64 {
-                    if self.get_square(sq) == (pt, side) {
-                        v.push(1f64);
-                    }
-                    else {
-                        v.push(0f64);
-                    }
-                }
-            }
-        }
-        v
-    }
-}

+ 0 - 142
src/interface.rs

@@ -1,142 +0,0 @@
-use std::io::{self, BufRead};
-use std::collections::BTreeMap;
-use std::sync::mpsc::{Receiver, Sender};
-
-use std::process::exit;
-
-use engine::{EngineMsg, InterfaceMsg, SearchInfo};
-use game::{Game};
-
-use log::info;
-
-pub fn run(r: Receiver<InterfaceMsg>, s: Sender<EngineMsg>) {
-    //unsafe { debug_log = Box::new(File::open("debug.log").unwrap()); }
-    //unsafe { debug_log = Arc::new(Some(Mutex::new(File::create("~/debug.log").unwrap()))) };
-    let stdin = io::stdin();
-    for line_m in stdin.lock().lines() {
-        let line = line_m.unwrap();
-
-        info!("received line: {}", line);
-
-        let split = line.split_whitespace().collect::<Vec<&str>>();
-        run_command(split, &r, &s);
-    }
-}
-
-
-fn run_command(mut cmd: Vec<&str>, r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    if cmd.len() > 0 {
-        let command = cmd[0];
-        cmd.drain(0..1);
-        match command {
-            "uci" => cmd_uci(cmd),
-            "isready" => cmd_isready(cmd, r, s),
-            "position" => cmd_position(cmd, r, s),
-            "print" => cmd_print(cmd, r, s),
-            "go" => cmd_go(cmd, r, s),
-            "stop" => cmd_stop(cmd, r, s),
-            "ucinewgame" => cmd_newgame(cmd, r, s),
-            //"setoption" => cmd_setoption(cmd, r, s),
-            "quit" | "exit" => cmd_quit(cmd, r, s),
-            cmd => { println!("unknown command: {}", cmd); }
-        }
-    }
-}
-
-fn cmd_uci(_args: Vec<&str>) {
-    println!("id name bishop 1.0");
-    println!("id author Geile Siech");
-    println!("uciok");
-}
-
-fn cmd_isready(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    s.send(EngineMsg::IsReady).unwrap();
-}
-
-fn cmd_position(mut args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    let position = args[0];
-    args.drain(0..1);
-
-    let mut game = Game::default();
-
-    if position == "startpos" {
-    }
-    else if position == "fen" {
-        let fen_parts: Vec<&str> = Vec::from(&args[0..6]).into_iter().collect::<Vec<&str>>();
-        game = Game::from_fen(fen_parts.as_slice()).unwrap_or_else(|| {
-            info!("invalid fen");
-            println!("invalid fen");
-            Game::default()
-        });
-        args.drain(0..6);
-    }
-    let moves = 
-        if args.len() > 0 {
-            if args[0] != "moves" {
-                info!("unexpected {}", args[6]);
-                println!("unexpected {}", args[6]);
-            }
-            args.drain(0..1);
-
-
-            args.into_iter().map(|m| String::from(m)).collect::<Vec<String>>()
-        }
-        else {
-            Vec::new()
-        };
-
-    s.send(EngineMsg::SetBoard{ pos: game, moves }).unwrap();
-}
-
-fn cmd_print(mut _args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    s.send(EngineMsg::Print).unwrap();
-}
-
-fn cmd_go(args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    let mut options: BTreeMap<String, String> = BTreeMap::new();
-    let mut opt_name: Option<&str> = None;
-    let mut si = SearchInfo::new();
-
-    for arg in args {
-        if let Some(on) = opt_name {
-            options.insert(on.to_owned(), arg.to_owned());
-            opt_name = None;
-        }
-        else if arg == "infinite" {
-            si.infinite = true;
-        }
-        else if arg == "perft" {
-            si.perft = true;
-        }
-        else {
-            opt_name = Some(arg);
-        } 
-    }
-
-    si.movetime = options.get("movetime").map(|x| x.parse::<_>().unwrap_or(0));
-    si.depth = options.get("depth").map(|x| x.parse::<_>().unwrap_or(0));
-    si.wtime = options.get("wtime").map(|x| x.parse::<_>().unwrap_or(0));
-    si.btime = options.get("btime").map(|x| x.parse::<_>().unwrap_or(0));
-    si.winc = options.get("winc").map(|x| x.parse::<_>().unwrap_or(0));
-    si.binc = options.get("binc").map(|x| x.parse::<_>().unwrap_or(0));
-    si.movestogo = options.get("movestogo").map(|x| x.parse::<_>().unwrap_or(0));
-
-
-    s.send(EngineMsg::Search(si)).unwrap();
-}
-
-fn cmd_stop(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    s.send(EngineMsg::Stop).unwrap();
-}
-
-fn cmd_newgame(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    s.send(EngineMsg::NewGame).unwrap();
-}
-
-fn cmd_quit(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, _s: &Sender<EngineMsg>) {
-    exit(0);
-}
-
-
-
-

+ 1 - 1
src/magic/mod.rs

@@ -1,4 +1,4 @@
-use crate::{bitboard::*, movegen::ROOK};
+use crate::bitboard::*;
 
 mod tables;
 

+ 7 - 11
src/main.rs

@@ -1,9 +1,9 @@
 #![feature(stdsimd)]
-pub mod interface;
+pub mod uci;
 pub mod bitboard;
 pub mod movegen;
 pub mod engine;
-pub mod game;
+pub mod board;
 pub mod evaluate;
 pub mod search;
 pub mod zobrist;
@@ -16,14 +16,7 @@ extern crate simplelog;
 extern crate rand;
 extern crate static_assertions;
 
-use std::sync::{mpsc};
-use std::{thread, fs::File};
 use engine::Engine;
-use log::*;
-use simplelog::*;
-use static_assertions::const_assert_eq;
-
-use crate::movegen::Move;
 
 
 fn main() {
@@ -37,7 +30,7 @@ fn main() {
 
     //let logfile = File::create("C:\\Users\\Nicolas\\debug.log").unwrap();
     //simplelog::WriteLogger::init(LevelFilter::Info, Config::default(), logfile).unwrap();
-    const_assert_eq!(std::mem::size_of::<Move>(), 4);
+    /*const_assert_eq!(std::mem::size_of::<Move>(), 4);
 
     let (esend, erecv) = mpsc::channel();
     let (isend, irecv) = mpsc::channel();
@@ -48,7 +41,10 @@ fn main() {
         engine.run();
     });
 
-    interface::run(irecv, esend);
+    interface::run(irecv, esend);*/
+
+    let mut engine = Engine::new();
+    engine.run(std::io::stdin());
 }
 
 

+ 39 - 35
src/movegen.rs

@@ -1,7 +1,7 @@
 use bitboard::Bitboard;
 use bitboard::Square;
 use bitboard::*;
-use game::Game;
+use board::Board;
 use static_assertions::const_assert_eq;
 use ttable::{Cache, EntryType};
 use std::iter::Iterator;
@@ -122,6 +122,9 @@ impl Move {
     }
 
     pub fn to_string(&self) -> String {
+        if self.is_null() {
+            return "0000".to_owned();
+        }
         match self {
             Move::Default{ mov, pc: _} => {
                 Self::square_to_string(mov.from) + &Self::square_to_string(mov.to)
@@ -183,10 +186,10 @@ impl Move {
             Move::Promotion{ mov: _, pc } => {
                 pc.piece_type()
             },
-            Move::Castling{ mov, side: _, left: _ } => {
+            Move::Castling{ mov: _, side: _, left: _ } => {
                 KING
             },
-            Move::EnPassant{ mov, beaten: _ } => {
+            Move::EnPassant{ mov: _, beaten: _ } => {
                 PAWN
             },
         }
@@ -248,7 +251,7 @@ pub struct MoveGenerator {
 }
 
 impl MoveGenerator {
-    pub fn generate_legal_moves(game: &mut Game, ce: Option<&CacheEntry>, killers: &[Move], counter: Option<Move>, side: Side) -> Self {
+    pub fn generate_legal_moves(game: &mut Board, ce: Option<&CacheEntry>, killers: &[Move], counter: Option<Move>, side: Side) -> Self {
         MoveGenerator {
             moves: generate_legal_moves(game, side, false),
             captures: Vec::with_capacity(32),
@@ -350,7 +353,7 @@ impl Iterator for MoveGenerator {
     }
 }
 
-pub fn generate_moves(game: &Game, side: Side) -> Vec<Move> {
+pub fn generate_moves(game: &Board, side: Side) -> Vec<Move> {
 
     let mut moves: Vec<Move> = Vec::with_capacity(128);
     generate_pawn_pushes(game, side, &mut moves);
@@ -371,7 +374,7 @@ pub fn generate_moves(game: &Game, side: Side) -> Vec<Move> {
  * 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> {
+pub fn generate_possattacking_moves(game: &Board, side: Side) -> Vec<Move> {
 
     let mut moves: Vec<Move> = Vec::with_capacity(32);
     generate_pawn_captures(game, side, &mut moves);
@@ -384,7 +387,7 @@ pub fn generate_possattacking_moves(game: &Game, side: Side) -> Vec<Move> {
     return moves;
 }
 
-pub fn generate_legal_moves_old(game: &mut Game, side: Side) -> Vec<Move> {
+pub fn generate_legal_moves_old(game: &mut Board, side: Side) -> Vec<Move> {
     let moves = generate_moves(game, side);
 
     let check_legality = |mov: &Move| {
@@ -433,7 +436,7 @@ fn pinned_rays(king: Bitboard, friends: Bitboard, diag_enemies: Bitboard, straig
 }
 
 
-pub fn generate_legal_moves(game: &mut Game, side: Side, captures_only: bool) -> Vec<Move> {
+pub fn generate_legal_moves(game: &mut Board, side: Side, captures_only: bool) -> Vec<Move> {
     let moves =
         if captures_only {
             generate_attacking_moves(game, side)
@@ -486,7 +489,7 @@ pub fn generate_legal_moves(game: &mut Game, side: Side, captures_only: bool) ->
 }
 
 
-pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, captures_only: bool, side: Side) -> Vec<Move> {
+pub fn generate_legal_sorted_moves(game: &mut Board, _hash: &mut Cache, killers: &[Move], ce: Option<CacheEntry>, captures_only: bool, side: Side) -> Vec<Move> {
     let mut moves = generate_legal_moves(game, side, captures_only);
 
     let mov_val= |mov: &Move| {
@@ -509,7 +512,7 @@ pub fn generate_legal_sorted_moves(game: &mut Game, _hash: &mut Cache, killers:
 }
 
 
-pub fn generate_attacking_moves(game: &Game, side: Side) -> Vec<Move> {
+pub fn generate_attacking_moves(game: &Board, 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);
@@ -521,7 +524,7 @@ pub fn generate_attacking_moves(game: &Game, side: Side) -> Vec<Move> {
 }
 
 
-pub fn sort_moves(game: &mut Game, hash: &mut Cache, killers: &[Move], move_list: &mut Vec<Move>) {
+pub fn sort_moves(game: &mut Board, hash: &mut Cache, killers: &[Move], move_list: &mut Vec<Move>) {
     move_list.sort_by_cached_key(|mov| {
         let undo = game.apply(*mov);
         if let Some(e) = hash.lookup(game) {
@@ -576,11 +579,11 @@ fn mvv_lva_score(mov: &Move) -> i32 {
     }
 }
 
-pub fn sort_moves_least_valuable_attacker(_game: &mut Game, move_list: &mut Vec<Move>) {
+pub fn sort_moves_least_valuable_attacker(_game: &mut Board, move_list: &mut Vec<Move>) {
     move_list.sort_by_cached_key(mvv_lva_score)
 }
 
-pub fn sort_moves_no_hash(game: &mut Game, move_list: &mut Vec<Move>) {
+pub fn sort_moves_no_hash(game: &mut Board, move_list: &mut Vec<Move>) {
     move_list.sort_by_cached_key(|mov| {
         let undo = game.apply(*mov);
         let our_material = crate::evaluate::material_value(game, game.turn);
@@ -592,7 +595,7 @@ pub fn sort_moves_no_hash(game: &mut Game, move_list: &mut Vec<Move>) {
 }
 
 
-fn generate_legal_pawn_pushes<const SIDE: Side>(game: &Game, move_list: &mut Vec<Move>) {
+fn generate_legal_pawn_pushes<const SIDE: Side>(game: &Board, move_list: &mut Vec<Move>) {
     let pawns = game.pawns(SIDE);
     let adv_pieces= game.get_all_side(!SIDE);
     let own_pieces = game.get_all_side(SIDE);
@@ -615,7 +618,7 @@ fn generate_legal_pawn_pushes<const SIDE: Side>(game: &Game, move_list: &mut Vec
 }
 
 
-fn generate_pawn_pushes(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_pawn_pushes(game: &Board, 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);
@@ -635,7 +638,7 @@ 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>) {
+fn generate_pawn_doublepushes(game: &Board, 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 pieces = game.get_all_side(side);
@@ -658,7 +661,7 @@ fn generate_pawn_doublepushes(game: &Game, side: Side, move_list: &mut Vec<Move>
     }
 }
 
-fn generate_pawn_captures(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_pawn_captures(game: &Board, side: Side, move_list: &mut Vec<Move>) {
     let pawns = game.pawns(side);
     let others = game.get_all_side(!side);
     let promotion_mask = ROW_1 | ROW_8;
@@ -692,7 +695,7 @@ fn generate_pawn_captures(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     }
 }
 
-fn generate_promotions(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_promotions(game: &Board, 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);
@@ -719,7 +722,7 @@ fn generate_promotions(game: &Game, side: Side, move_list: &mut Vec<Move>) {
 }
 
 
-fn generate_en_passant(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_en_passant(game: &Board, side: Side, move_list: &mut Vec<Move>) {
     if let Some(ep) = game.en_passant {
         let pawncolumn = FILE_A >> ep;
 
@@ -753,7 +756,7 @@ fn generate_en_passant(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     }
 }
 
-fn generate_knight_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
+fn generate_knight_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);
 
@@ -797,21 +800,21 @@ pub fn get_knight_targets(square: Square) -> Bitboard {
     TARGETS[square as usize]
 }
 
-fn generate_bishop_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
+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);
     
     generate_sliding_moves(game, 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>, captures_only: bool) {
+fn generate_rook_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);
     
     generate_sliding_moves(game, 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>, captures_only: bool) {
+fn generate_queen_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);
     
@@ -832,7 +835,7 @@ pub fn count_sliding_move_bitboard(friends: Bitboard, others: Bitboard, pieces:
     return sum;
 }
 
-fn generate_sliding_moves(game: &Game, friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
+fn generate_sliding_moves(game: &Board, friends: Bitboard, others: Bitboard, pieces: Bitboard, piece_type: PieceType,
                           straight: bool, diagonal: bool, move_list: &mut Vec<Move>, captures_only: bool) {
     let pieces_iter = BitboardIterator(pieces);
 
@@ -884,7 +887,7 @@ fn generate_direction(piece: Bitboard, direction: fn(Bitboard) -> Bitboard, occu
 }
 
 
-fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captures_only: bool) {
+fn generate_king_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);
 
@@ -914,7 +917,7 @@ fn generate_king_moves(game: &Game, side: Side, move_list: &mut Vec<Move>, captu
 }
 
 
-fn generate_castling_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
+fn generate_castling_moves(game: &Board, side: Side, move_list: &mut Vec<Move>) {
     let king = game.kings(side);
     let king_sq = square(king);
     let c1 = Move::Castling{ mov: SimpleMove { from: king_sq, to: king_sq - 2 }, side: side, left: false };
@@ -951,7 +954,7 @@ fn generate_castling_moves(game: &Game, side: Side, move_list: &mut Vec<Move>) {
     }
 }
 
-pub fn is_check(game: &Game, side: Side) -> bool {
+pub fn is_check(game: &Board, side: Side) -> bool {
     let king = game.get_piece(KING, side);
     let king_square = square(king);
     let friends = game.get_all_side(side);
@@ -1007,7 +1010,7 @@ pub fn is_check(game: &Game, side: Side) -> bool {
 ///
 /// checks wether the square sq is attacked by the specified side
 /// 
-pub fn is_attacked(game: &Game, sq: Square, side: Side) -> bool {
+pub fn is_attacked(game: &Board, sq: Square, side: Side) -> bool {
     let others = game.get_all_side(!side);
     let friends = game.get_all_side(side);
 
@@ -1058,9 +1061,11 @@ pub fn is_attacked(game: &Game, sq: Square, side: Side) -> bool {
 
 #[cfg(test)]
 mod tests {
+    use std::sync::mpsc;
+
     use movegen::*;
 
-    use crate::{search::{perft, SearchControl}, ttable::RepetitionTable};
+    use crate::{search::SearchControl, engine::SearchMessage};
 
     ///
     /// tests the move generation from some positions to depth 4 and checks wether the correct
@@ -1079,11 +1084,10 @@ mod tests {
     }
 
     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;
+        let board: Board = Board::from_fen_str(fen).unwrap();
+        let (_, stop_check) = mpsc::channel::<SearchMessage>();
+        let mut pc = SearchControl::new_perft(&board, stop_check, depth);
+        pc.perft(depth);
+        return pc.nodes;
     }
 }

+ 236 - 54
src/search.rs

@@ -1,21 +1,32 @@
-use std::clone;
+use std::{clone, sync::{Arc, RwLock, mpsc}, cell::RefCell, time::{Instant, Duration}, ops::Add};
 
 use movegen::*;
-use game::Game;
+use board::Board;
 use evaluate::*;
 use log::info;
 use rand::prelude::*;
 use ttable::*;
 
+use crate::engine::SearchMessage;
 
 
+pub struct SearchCommon {
+    pub hash: Cache,
+}
+
 ///
 /// struct to contain data for a search
 /// 
-pub struct SearchControl<'a> {
+pub struct SearchControl {
     /// node counter
     pub nodes: usize,
 
+    pub board: Board,
+    pub hash: Arc<RwLock<Cache>>,
+
+    /// depth the current iteration was started at 
+    initial_depth: i32,
+
     pub killer_moves: Vec<[Move; 2]>,
 
     pub last_move: Option<Move>,
@@ -24,35 +35,46 @@ pub struct SearchControl<'a> {
     /// current halfmove clock for discarding old hash entries
     pub halfmove_age: u16,
 
-    /// function to check if the search should be exited
-    pub check: &'a mut dyn FnMut() -> bool,
-    pub stopping: bool,
-    pub move_history: &'a mut RepetitionTable,
+    /// channel which is probed from time to time to check whether the
+    /// search should be aborted
+    stop_channel: mpsc::Receiver<SearchMessage>,
 
-    /// depth the search was started at
-    initial_depth: i32,
+    /// whether the search is currently being terminated (after receiving Stop on stop_channel)
+    stopping: bool,
+
+    search_started: Instant,
+    pub movetime: Option<Duration>,
+    pub remaining_time: Option<Duration>,
 
     nullmoves_enabled: bool,
 }
 
+
+
 pub enum SearchResult {
     Finished(Move, PosValue),
+    NoMove(PosValue),
     Cancelled(Option<(Move, PosValue)>),
+    PerftFinished,
     Invalid
 }
 
-impl<'a> SearchControl<'a> {
-    pub fn new(check: &'a mut dyn FnMut() -> bool, move_history: &'a mut RepetitionTable, depth: i32) -> Self {
+impl SearchControl {
+    pub fn new(board: &Board, stop_channel: mpsc::Receiver<SearchMessage>, hash: Arc<RwLock<Cache>>) -> Self {
         SearchControl {
             nodes: 0,
-            killer_moves: vec![[Move::nullmove(); 2]; depth as usize],
+            board: board.clone(),
+            hash,
+            killer_moves: Vec::new(),
             last_move: None,
             countermoves: [[Move::nullmove(); 64]; 64],
             halfmove_age: 0,
-            check,
+            stop_channel,
             stopping: false,
-            move_history,
-            initial_depth: depth,
+            search_started: Instant::now(),
+            movetime: None,
+            remaining_time: None,
+            initial_depth: 0,
             nullmoves_enabled: true
         }
     }
@@ -62,7 +84,7 @@ impl<'a> SearchControl<'a> {
         self.killer_moves = vec![[Move::nullmove(); 2]; depth as usize];
     }
 
-    pub fn insert_killer(&mut self, ply_depth: usize, killer: Move) {
+    fn insert_killer(&mut self, ply_depth: usize, killer: Move) {
         if self.is_killer(ply_depth, &killer) {
             return;
         }
@@ -73,7 +95,7 @@ impl<'a> SearchControl<'a> {
         self.killer_moves[ply_depth][nkm - 1] = killer;
     }
 
-    pub fn is_killer(&self, ply_depth: usize, killer: &Move) -> bool {
+    fn is_killer(&self, ply_depth: usize, killer: &Move) -> bool {
         self.killer_moves[ply_depth].contains(killer)
     }
 
@@ -83,6 +105,129 @@ impl<'a> SearchControl<'a> {
         self.countermoves[from as usize][to as usize]
     }
 
+    fn reconstruct_pv(&self) -> Vec<Move> {
+        let mut pv: Vec<Move> = Vec::new();
+        let mut g = self.board.clone();
+
+        // max 100 to avoid infinite loops
+        for _ in 0..100 {
+            let mce = self.hash.read().unwrap().lookup(&g).map(CacheEntry::clone);
+            if let Some(ce) = mce {
+                if ce.mov == (SimpleMove{ from: 0, to: 0 }) {
+                    break;
+                }
+                let movs = generate_moves(&g, g.turn);
+                let bm = movs.iter().find(|m| (**m).to_simple() == ce.mov);
+                if let Some(found) = bm {
+                    g.apply(*found);
+                    pv.push(*found);
+                }
+                else {
+                    break;
+                }
+            }
+            else {
+                break;
+            }
+        }
+        return pv;
+    }
+
+    /// is called every 1024 nodes to check if the search will be stopped
+    fn check_stop(&self) -> bool {
+        if let Some(mt) = self.movetime {
+            if self.search_started.elapsed() >= mt {
+                return true;
+            }
+        }
+
+        if let Some(rt) = self.remaining_time {
+            if self.search_started.elapsed() >= (rt / 17) {
+                return true;
+            }
+        }
+
+        if let Ok(SearchMessage::Stop) = self.stop_channel.try_recv() {
+            return true;
+        }
+
+        return false;
+    }
+
+    pub fn iterative_deepening(&mut self, max_depth: Option<i32>) -> bool {
+        let mut alpha = MIN_VALUE;
+        let mut beta = MAX_VALUE;
+
+        let mut best_move: Option<Move> = None;
+        let mut best_val: Option<PosValue> = None;
+
+        self.search_started = Instant::now();
+        self.halfmove_age = self.board.ply_number() as _;
+
+        for depth in 1.. {
+            let board = self.board.clone();
+            let hash = self.hash.clone();
+
+            self.set_depth(depth);
+
+            let result = search(board, self, hash, alpha, beta, depth);
+
+            match result {
+                SearchResult::Finished(mov, val) => {
+                    best_move = Some(mov);
+                    best_val = Some(val);
+
+                    let elapsed = self.search_started.elapsed();
+                    let nodes = self.nodes;
+                    let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64;
+                    let hashfull = self.hash.read().unwrap().fullness_permill();
+                    let pv_array = self.reconstruct_pv();
+                    let pv = &pv_array.iter().filter(|&m| !m.is_null()).map(|m| m.to_string()).fold(String::new(), |a, b| a + " " + &b)[0..];
+
+
+                    if let Some(plies) = crate::evaluate::is_mate_in_p1(val) {
+                        //println!("plies = {}", plies);
+                        let turns = (plies + if plies > 0 { 1 } else { -1 }) / 2;
+                        let infostring = format!("info depth {} score mate {} time {} nodes {} nps {} hashfull {} pv{}", depth, turns, elapsed.as_millis(), nodes, nps, hashfull, pv);
+                        println!("{}", infostring);
+                        info!("{}", infostring);
+                        if max_depth.is_none() && plies.abs() * 2 <= depth {
+                            break;
+                        }
+                    }
+                    else {
+                        let infostring = format!("info depth {} score cp {} time {} nodes {} nps {} hashfull {} pv{}", depth, val, elapsed.as_millis(), nodes, nps, hashfull, pv);
+                        println!("{}", infostring);
+                        info!("{}", infostring);
+                    }
+                },
+                SearchResult::NoMove(val) => {
+                    let infostring = format!("info depth {} score cp {}", depth, val);
+                    println!("{}", infostring);
+                    info!("{}", infostring);
+                    break;
+                }
+                _ => {
+                    break;
+                }
+            }
+
+            if Some(depth) == max_depth {
+                break;
+            }
+        }
+
+        if let (Some(bm), Some(bv)) = (best_move, best_val) {
+            info!("bestmove {}", bm.to_string());
+            println!("bestmove {}", bm.to_string());
+        }
+        else {
+            info!("bestmove 0000");
+            println!("bestmove 0000");
+        }
+        return true;
+    }
+
 }
 
 
@@ -90,15 +235,17 @@ impl<'a> SearchControl<'a> {
 /**
  * searches for moves and returns the best move found plus its value
  */
-pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> SearchResult {
+pub fn search(mut board: Board, sc: &mut SearchControl, hashs: Arc<RwLock<Cache>>, mut alpha: PosValue, beta: PosValue, depth: i32) -> SearchResult {
+    let mut hash = hashs.write().unwrap();
     if depth == 0 {
         return SearchResult::Invalid;
     }
 
-    let cache_entry = hash.lookup(game);
+    let cache_entry = hash.lookup(&board).map(CacheEntry::clone);
 
     let ply_depth = (sc.initial_depth - depth) as usize;
-    let moves = generate_legal_sorted_moves(game, hash, &sc.killer_moves[ply_depth], cache_entry.map(CacheEntry::clone), false, game.turn);
+    let turn = board.turn;
+    let moves = generate_legal_sorted_moves(&mut board, &mut hash, &sc.killer_moves[ply_depth], cache_entry, false, turn);
     //let mut moves = generate_legal_moves(game, game.turn);
     //sort_moves(game, hash, &sc.killer_moves[ply_depth], &mut moves);
     
@@ -111,14 +258,23 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alp
     let mut valued_moves: Vec<(Move, PosValue)> = Vec::with_capacity(moves.len());
     let mut cancelled = false;
 
+    if moves.is_empty() {
+        if is_check(&board, board.turn) {
+            return SearchResult::NoMove(checkmated());
+        }
+        else {
+            return SearchResult::NoMove(0);
+        }
+    }
+
     for mov in moves {
-        let undo = game.apply(mov);
+        let undo = board.apply(mov);
 
-        let val = -negamax(game, sc, hash, decrease_mate_in(-beta), decrease_mate_in(-alpha), depth - 1);
+        let val = -negamax(&mut board, sc, &mut hash, decrease_mate_in(-beta), decrease_mate_in(-alpha), depth - 1);
 
         //info!("moveval {} -> {}\n", mov.to_string(), val);
         
-        game.undo_move(undo);
+        board.undo_move(undo);
 
         if sc.stopping {
             cancelled = true;
@@ -143,7 +299,7 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alp
             return SearchResult::Cancelled(Some((chosen_mov.0, -chosen_mov.1)));
         }
         else {
-            hash.cache(game, CacheEntry::new_value(depth as _, sc.halfmove_age as _, chosen_mov.0.to_simple(), chosen_mov.1 as _));
+            hash.cache(&board, CacheEntry::new_value(depth as _, sc.halfmove_age as _, chosen_mov.0.to_simple(), chosen_mov.1 as _));
             return SearchResult::Finished(chosen_mov.0, -chosen_mov.1);
         }
     }
@@ -152,7 +308,7 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alp
     }
 }
 
-pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, mut depth: i32) -> PosValue {
+pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, mut depth: i32) -> PosValue {
 
     let last_move = sc.last_move;
 
@@ -184,7 +340,7 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
 
     sc.nodes += 1;
     if sc.nodes % 1024 == 0 {
-        if (sc.check)() {
+        if sc.check_stop() {
             sc.stopping = true;
             return 0;
         }
@@ -297,11 +453,11 @@ pub fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut al
     return alpha;
 }
 
-fn quiescence_search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> PosValue {
+fn quiescence_search(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut alpha: PosValue, beta: PosValue, depth: i32) -> PosValue {
 
     sc.nodes += 1;
     if sc.nodes % 1024 == 0 {
-        if (sc.check)() {
+        if sc.check_stop() {
             sc.stopping = true;
             return 0;
         }
@@ -347,37 +503,64 @@ fn quiescence_search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache,
 }
 
 
-pub fn perft(game: &mut Game, sc: &mut SearchControl, depth: i32) -> bool {
-    let moves = generate_legal_moves(game, game.turn, false);
+impl SearchControl {
+    pub fn new_perft(board: &Board, stop_channel: mpsc::Receiver<SearchMessage>, depth: i32) -> Self {
+        SearchControl {
+            nodes: 0,
+            board: board.clone(),
+            hash: Arc::new(RwLock::new(Cache::new(0))),
+            initial_depth: depth,
+            killer_moves: Vec::new(),
+            last_move: None,
+            countermoves: [[Move::nullmove(); 64]; 64],
+            halfmove_age: board.ply_number() as _,
+            stop_channel,
+            stopping: false,
+            search_started: Instant::now(),
+            movetime: None,
+            remaining_time: None,
+            nullmoves_enabled: false
+        }
+    }
+
+    pub fn perft(&mut self, depth: i32) -> bool {
+        let board = &mut self.board;
+        let moves = generate_legal_moves(board, board.turn, false);
+
+        if depth <= 1 {
+            self.nodes += moves.len();
+            if self.nodes % 1024 < moves.len() {
+                if self.check_stop() {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+
+        for mov in moves {
+            let nodes_before = self.nodes;
+            let undo = self.board.apply(mov);
+            let do_return = self.perft(depth - 1);
+            self.board.undo_move(undo);
+
+            if depth >= self.initial_depth {
+                println!("{}: {}", mov.to_string(), self.nodes - nodes_before);
+            }
 
-    if depth <= 1 {
-        sc.nodes += moves.len();
-        if sc.nodes % 1024 < moves.len() {
-            if (sc.check)() {
+            if do_return {
                 return true;
             }
         }
+
         return false;
     }
 
+}
 
-    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;
-        }
-    }
 
-    return false;
-}
 
 
 #[cfg(test)]
@@ -396,14 +579,13 @@ mod tests {
         ];
 
         for (i, &position) in positions.iter().enumerate() {
-            let mut game = Game::from_fen_str(position).unwrap();
+            let board = Board::from_fen_str(position).unwrap();
             for (j, &p_res) in perft_results[i].iter().enumerate() {
                 let depth = j + 1;
-                let mut check_fn = || false;
-                let mut rt = RepetitionTable::new();
-                let mut sc = SearchControl::new(&mut check_fn, &mut rt, depth as _);
-                perft(&mut game, &mut sc, depth as _);
-                assert_eq!(sc.nodes, p_res);
+                let (_, stop_check) = mpsc::channel::<SearchMessage>();
+                let mut pc = SearchControl::new_perft(&board, stop_check, depth as _);
+                pc.perft(depth as _);
+                assert_eq!(pc.nodes, p_res);
             }
         }
     }

+ 0 - 2
src/settings.rs

@@ -1,8 +1,6 @@
 use std::collections::BTreeMap;
 
 
-
-
 pub enum SettingType {
     Slider{ min: isize, max: isize },
     String,

+ 3 - 3
src/ttable.rs

@@ -1,4 +1,4 @@
-use game::Game;
+use board::Board;
 use evaluate::PosValue;
 use static_assertions::const_assert_eq;
 use std::collections::{HashMap};
@@ -102,7 +102,7 @@ impl Cache {
         (hash % (self.table.len() as u64)) as usize
     }
 
-    pub fn lookup<'a>(&'a self, board: &Game) -> Option<&'a CacheEntry> {
+    pub fn lookup<'a>(&'a self, board: &Board) -> Option<&'a CacheEntry> {
         if board.zobrist.is_none() {
             info!("invalid zobrist");
         }
@@ -143,7 +143,7 @@ impl Cache {
         return false;
     }
     
-    pub fn cache(&mut self, game_pos: &Game, ce: CacheEntry) {
+    pub fn cache(&mut self, game_pos: &Board, ce: CacheEntry) {
         if game_pos.zobrist.is_none() {
             info!("invalid zobrist");
         }