//! UCI is a simple library to allow people to ignore the lower-level protocol needed to create chess engines. //! The engine is fully generic. By specifying a valid reader and writing you can send the messages to STDIN //! and STDOUT, per the standard - or to memory for testing. mod commands; use std::io::{BufRead, Write}; #[derive(Debug)] pub struct Engine<'a, R, W> { pub name: &'a str, pub author: &'a str, pub reader: R, pub writer: W, } /// Notes to delete later: /// /// /// A way to make this comfortable for users to use is this provides easy access to commands as well as a /// loop and stuff they can use. This way they don't have to override anything - they just include this /// in their code for their engine to make the communication portion easy. /// /// Think of this in the manner of a game engine. They don't provide the loop and everything, but they do /// provide convenience functions and stuff to make _building_ that loop easier. /// /// In other words, the engine writer still needs to know how the standard works. They just don't need to /// implement all the nasty details. /// For example, a start up function calls the correct "boot up" code for the engine so they only have to /// worry about calling "boot up" prior to their main engine loop. /// /// Additionally this code should have a parser. You call it with the reader supplied and it waits for a command /// and parses it into a tuple of some kind for the user. impl<'a, R, W> Engine<'a, R, W> where R: BufRead, W: Write, { pub fn new(name: &'a str, author: &'a str, reader: R, writer: W) -> Engine<'a, R, W> { // TODO: This should also take an EngineOptions thing that indicates which options are supported // so that when called it can send them easily. // Engine options are all optional and appeared to be fixed by the UCI standard. Engine { name: name, author: author, reader: reader, writer: writer, } } /// Sends identification messages to the writer for the GUI to pick up pub fn identify(&mut self) { let name_id: String = format!("{} {} {}\n", commands::ID, commands::NAME, self.name); let author_id: String = format!("{} {} {}\n", commands::ID, commands::AUTHOR, self.author); // For these two writes we can panic - there's no possibility of recovery if the engine fails at this stage write!(&mut self.writer, "{}", name_id).expect("failed to send name identification to writer"); write!(&mut self.writer, "{}", author_id).expect("failed to send author identification to writer"); } /// Sends all available options in the options configuration and a final UCIOK meaning we are reading to go. pub fn send_available_options(&mut self) { // NOTE: Options can be represented as a hashmap with keys that are constant strings in the options.rs // file and values are the associated options. // This function will construct the proper string from the objects. // How can you make this as easy as possible for the user...give it some thought. The value has to be // some sort of object that can store various optiobns that change on the option. // Since options are fixed it would be nice to type check the key values. Maybe use a trait or something // that is actually a wrapped string, but typecheckable so not just any string can be put in. // TODO: This needs to be tested majorly // Again this command must complete before we can say the engine is connected, so panicking at this stage is ok write!(&mut self.writer, "{}", commands::UCIOK).expect("failed to send `uciok` command"); } }