timecat 1.52.0

A NNUE-based chess engine that implements the Negamax algorithm and can be integrated into any project as a library. It features move generation, advanced position evaluation through NNUE, and move searching capabilities.
Documentation
use super::*;

#[derive(Default)]
pub struct TimecatBuilder<T: ChessEngine> {
    user_commands: Vec<UserCommand>,
    engine: Option<T>,
}

impl<T: ChessEngine<IoReader = IoReader> + Default> TimecatBuilder<T> {
    pub fn build(self) -> Timecat<T> {
        let io_reader = IoReader::default();
        Timecat {
            user_commands: self.user_commands,
            engine: self
                .engine
                .unwrap_or_default()
                .with_io_reader(io_reader.clone()),
            io_reader,
            uci_state_manager: UCIStateManager::default(),
        }
    }
}

impl<T: ChessEngine> TimecatBuilder<T> {
    pub fn parse_args(mut self, args: &[&str]) -> Self {
        if args.contains(&"--uci") {
            self.user_commands
                .push(UserCommand::ChangeToUCIMode { verbose: false });
        }
        #[cfg(feature = "colored")]
        if args.contains(&"--no-color") {
            self.user_commands.push(UserCommand::SetColor(false));
        }
        if args.contains(&"--threads") {
            let num_threads = args
                .iter()
                .skip_while(|&arg| !arg.starts_with("--threads"))
                .nth(1)
                .unwrap_or(&"")
                .parse()
                .unwrap_or(TIMECAT_DEFAULTS.num_threads);
            self.user_commands.push(UserCommand::SetUCIOption {
                user_input: format!("setoption name Threads value {}", num_threads).into(),
            });
        }
        if args.contains(&"--help") {
            self.user_commands.push(UserCommand::Help);
            self.user_commands.push(UserCommand::TerminateEngine);
            return self;
        }
        if args.contains(&"--version") {
            self.user_commands.push(UserCommand::EngineVersion);
            self.user_commands.push(UserCommand::TerminateEngine);
            return self;
        }
        #[cfg(feature = "debug")]
        if args.contains(&"--test") {
            self.user_commands.push(UserCommand::RunTest);
            self.user_commands.push(UserCommand::TerminateEngine);
            return self;
        }
        if args.contains(&"-c") || args.contains(&"--command") {
            let command_string = args
                .iter()
                .skip_while(|&arg| !["-c", "--command"].contains(arg))
                .skip(1)
                .take_while(|&&arg| !arg.starts_with("--"))
                .join(" ");

            let parser_result = if command_string.is_empty() {
                Err(TimecatError::NoInput)
            } else {
                Parser::parse_command(&command_string)
            };

            match parser_result {
                Ok(user_commands) => self.user_commands.extend(user_commands),
                Err(error) => println_wasm!(
                    "{}",
                    error
                        .stringify_with_optional_raw_input(Some(&command_string))
                        .colorize(ERROR_MESSAGE_STYLE)
                ),
            }

            self.user_commands.push(UserCommand::TerminateEngine);
            return self;
        }
        self
    }
}

pub struct Timecat<T: ChessEngine> {
    user_commands: Vec<UserCommand>,
    engine: T,
    io_reader: IoReader,
    uci_state_manager: UCIStateManager<T>,
}

impl<T: ChessEngine> Timecat<T> {
    pub fn run(&mut self) {
        self.io_reader.start_reader_in_parallel();
        for user_command in self.user_commands.iter() {
            user_command
                .run_command(&mut self.engine, &self.uci_state_manager)
                .unwrap_or_else(|error| {
                    println_wasm!("{}", error.stringify().colorize(ERROR_MESSAGE_STYLE))
                });
        }
        if self.engine.terminate() {
            return;
        }
        self.engine.print_info();
        Self::main_loop.run_and_print_time(self);
    }

    pub fn run_uci_command(&mut self, raw_input: &str) -> Result<()> {
        for user_command in Parser::parse_command(raw_input)? {
            user_command.run_command(&mut self.engine, &self.uci_state_manager)?;
        }
        Ok(())
    }

    pub fn main_loop(&mut self) {
        loop {
            if self.engine.terminate() {
                if GLOBAL_TIMECAT_STATE.is_in_console_mode() {
                    println_wasm!(
                        "{}",
                        "Program ended successfully!".colorize(SUCCESS_MESSAGE_STYLE)
                    );
                }
                break;
            }
            let raw_input = if GLOBAL_TIMECAT_STATE.is_in_console_mode() {
                println_wasm!();
                let raw_input = get_input(
                    "Enter Command: ".colorize(INPUT_MESSAGE_STYLE),
                    &self.io_reader,
                );
                println_wasm!();
                raw_input
            } else {
                get_input("", &self.io_reader)
            };
            self.run_uci_command(&raw_input).unwrap_or_else(|error| {
                println_wasm!(
                    "{}",
                    error
                        .stringify_with_optional_raw_input(Some(raw_input.as_str()))
                        .colorize(ERROR_MESSAGE_STYLE)
                )
            });
        }
    }

    pub fn uci_loop(&mut self) {
        GLOBAL_TIMECAT_STATE.set_to_uci_mode();
        self.run();
    }
}