Nicolas Winkler 2 rokov pred
rodič
commit
04a94c75d7
2 zmenil súbory, kde vykonal 1127 pridanie a 0 odobranie
  1. 948 0
      src/board.rs
  2. 179 0
      src/uci.rs

+ 948 - 0
src/board.rs

@@ -0,0 +1,948 @@
+use bitboard::*;
+use movegen::*;
+use log::info;
+use std::sync::Arc;
+use zobrist::{ZobristTable, Hash};
+use crate::movegen;
+
+#[derive(Clone)]
+pub struct Board
+{
+    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>, Hash)>
+}
+
+impl Default for Board {
+    fn default () -> Board {
+        Board {
+            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 Board {
+    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 Board {
+    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 Board {
+}
+
+
+impl Board {
+    pub fn empty() -> Board {
+        Board {
+            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<Board> {
+        let mut game: Board = Board::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<Board> {
+        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).unwrap_or(Err(())?);
+                //println!("from: {}", from);
+
+                let target = self.get_square(to);
+                let captured = if let Some((cap, _side)) = target { Some(cap) } else { None };
+
+                if let Some((_cap, cs)) = target {
+                    if cs == side {
+                        // cannot capture own 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) -> Option<(PieceType, Side)> {
+        let square_mask = 1 << square;
+        for i in 0..6 {
+            if self.pieces[i] & square_mask != 0 {
+                return Some((i as PieceType, WHITE));
+            }
+        }
+        for i in 0..6 {
+            if self.pieces[i + 6] & square_mask != 0 {
+                return Some((i as PieceType, BLACK));
+            }
+        }
+        return None;
+    }
+
+    /**
+     * @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 {
+                if let Some((pt, side)) = self.get_square(square_from_indices(i, 7 - row)) {
+                    let fig_index = pt as usize + if side == BLACK { 6 } else { 0 };
+                    line.push(figures[fig_index]);
+                }
+                else {
+                    line.push(' ');
+                }
+                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: Hash) {
+        if let Some((ref _zt, ref mut zh)) = self.zobrist {
+            *zh ^= h;
+        }
+    }
+
+    pub fn calculate_zobrist(&self, zt: &ZobristTable) -> Hash {
+        let mut hash: 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 {
+                        if let Some((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) == Some((pt, side)) {
+                        v.push(1f64);
+                    }
+                    else {
+                        v.push(0f64);
+                    }
+                }
+            }
+        }
+        v
+    }
+
+    pub fn ply_number(&self) -> i32 {
+        self.turn_number * 2 + if self.turn == BLACK { 1 } else { 0 }
+    }
+}

+ 179 - 0
src/uci.rs

@@ -0,0 +1,179 @@
+use std::io::{self, BufRead};
+use std::collections::BTreeMap;
+use std::sync::mpsc::{Receiver, Sender};
+
+use std::process::exit;
+
+use engine::{Command, SearchInfo};
+use board::Board;
+
+use log::info;
+
+use crate::evaluate::PosValue;
+
+
+pub fn parse_command(command: &str) -> Result<Command, String> {
+    let parts = command.split_whitespace().collect::<Vec<&str>>();
+    if parts.len() > 0 {
+        let command = parts[0];
+        let args: Vec<&str> = parts.into_iter().skip(1).collect();
+        match command {
+            "uci" => Ok(Command::Uci),
+            "isready" => Ok(Command::IsReady),
+            "position" => parse_position(&args),
+            "go" => parse_go(&args),
+            "stop" => Ok(Command::Stop),
+            "quit" | "exit" => Ok(Command::Quit),
+            /*"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 => { Err("unknown command".to_owned()) }
+        }
+    }
+    else {
+        Err("no command".to_owned())
+    }
+}
+
+fn parse_position(args: &Vec<&str>) -> Result<Command, String> {
+    //let position = args[0];
+    //args.drain(0..1);
+    let mut arg_iter = args.iter();
+    let &position = arg_iter.next().ok_or("no position type argument")?;
+
+    let mut board = Board::default();
+
+    if position == "startpos" {
+    }
+    else if position == "fen" {
+        let fen_parts: Vec<&str> = arg_iter.by_ref().take(6).map(|p| *p).collect::<Vec<&str>>();
+        if fen_parts.len() != 6 {
+            return Err("invalid fen arguments".to_owned());
+        }
+
+        board = Board::from_fen(fen_parts.as_slice()).ok_or("invalid fen")?;
+    }
+    else {
+        return Err("invalid position type".to_owned());
+    }
+
+    let pmov = arg_iter.next();
+    
+    
+    let moves =
+        if let Some(&moves) = pmov {
+            if moves != "moves" {
+                return Err("expected nothing or 'moves'".to_owned());
+            }
+
+            arg_iter.map(|m| String::from(*m)).collect::<Vec<String>>()
+        }
+        else {
+            Vec::new()
+        };
+
+    Ok(Command::Position{ pos: board, moves })
+}
+
+
+/*fn run_command(mut cmd: Vec<&str>, r: &Receiver<InterfaceMsg>, s: &Sender<Command>) {
+    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_isready(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<Command>) {
+    s.send(Command::IsReady).unwrap();
+}
+
+fn cmd_position(mut args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<Command>) {
+    let position = args[0];
+    args.drain(0..1);
+
+    let mut game = Board::default();
+
+    if position == "startpos" {
+    }
+    else if position == "fen" {
+        let fen_parts: Vec<&str> = Vec::from(&args[0..6]).into_iter().collect::<Vec<&str>>();
+        game = Board::from_fen(fen_parts.as_slice()).unwrap_or_else(|| {
+            info!("invalid fen");
+            println!("invalid fen");
+            Board::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(Command::Position{ pos: game, moves }).unwrap();
+}*/
+
+
+pub fn parse_go(args: &Vec<&str>) -> Result<Command, String> {
+    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));
+
+
+    Ok(Command::Go(si))
+}
+
+
+
+
+