chess_game 0.2.0

Simple Chess game
Documentation
use std::{thread, time::Duration};
use chess::{ChessMove, Board, MoveGen, Square, Piece, EMPTY, BoardStatus, File};
use method_shorthands::methods::*;
use rand::prelude::*;

use crate::{piece::Value, square::IsBackRank};

use super::Player;

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct EvalBot;

impl Player for EvalBot {
    fn get_move(board: Board, num_moves: usize, bot_wait_ms: u64) -> Result<ChessMove, ()> {
        if bot_wait_ms > 0 { thread::sleep(Duration::from_millis(bot_wait_ms)) };

        let move_gen = MoveGen::new_legal(&board);

        // King's Pawn opener
        match num_moves {
            0 => return Ok(ChessMove::new(Square::E2, Square::E4, None)),
            1 => return Ok(ChessMove::new(Square::E7, Square::E5, None)),
            _ => ()
        };

        // evaluation function
        let mut best_move = (ChessMove::default(), -1E10);
        for j in move_gen {
            let source = j.get_source();
            let dest = j.get_dest();
            let source_piece = board.piece_on(source).uw();

            let temp_board = board.make_move_new(j);
            let null_board = board.null_move();
            let null_temp_board = board.make_move_new(j).null_move();

            if temp_board.status() == BoardStatus::Checkmate {
                return Ok(j);
            }

            let mut score = 0.0;
            // develop pieces
            if null_temp_board.is_some() {
                let move_options = MoveGen::new_legal(&board).filter(|x| x.get_source() == source).count();
                let new_move_options = MoveGen::new_legal(&null_temp_board.uw()).filter(|x| x.get_source() == dest).count();

                score += new_move_options as f32 - move_options as f32 + if source_piece == Piece::Pawn { 4.0 } else { 0.0 };
            };
            // defend pieces
            if null_board.is_some() {
                let threat_count = MoveGen::new_legal(&board.null_move().uw()).filter(|x| x.get_dest() == source).count();
                let new_threat_count = MoveGen::new_legal(&temp_board).filter(|x| x.get_dest() == dest).count();

                score += (threat_count as f32 - new_threat_count as f32) * source_piece.value();
            };
            // castle
            if board.piece_on(source).uw() == Piece::King && source.get_file() == File::E {
                if dest.get_file() == File::C || dest.get_file() == File::G {
                    score += 5.0;
                }
            }
            // check
            if *temp_board.checkers() != EMPTY {
                score += 2.0;
            }
            // take pieces
            if let Some(piece) = board.piece_on(dest) {
                score += 2.0 * piece.value();
            }
            // promote
            if source_piece == Piece::Pawn && dest.is_back_rank() {
                score += source_piece.value();
            }
            score += rand::thread_rng().gen_range(-0.55..0.55);

            if score > best_move.1 {
                best_move = (j, score);
            }
        }

        Ok(best_move.0)
    }
}