Compare commits

...

43 Commits

Author SHA1 Message Date
Taylor Bockman 4c12c857ff lol 6 years ago
Taylor Bockman 4f8a5ef694 fix some errors, first test 6 years ago
Taylor Bockman fef0bc8e44 lol 6 years ago
Taylor Bockman 0ce59d6169 Some work on the parser 6 years ago
Taylor Bockman e049b59eb3 yep 6 years ago
Taylor Bockman 8ca26fe648 lol 6 years ago
Taylor Bockman 9b9e466cdb some parsy stuff 6 years ago
Taylor Bockman a73dae7568 onto the parser 6 years ago
Taylor Bockman d8577ee142 lol 6 years ago
Taylor Bockman 428da563dd SOme issue with backslash escaping 6 years ago
Taylor Bockman a91331d10f Generating option string finally done, now on to sending it... 6 years ago
Taylor Bockman 587fa6a4a6 fix idea 6 years ago
Taylor Bockman a1dae653fa String inconsistencies.... out of order???? 6 years ago
Taylor Bockman 02638cd05a lol 6 years ago
Taylor Bockman 5c9d13de42 lol 6 years ago
Taylor Bockman 47438cf5ea everything is fuk 6 years ago
Taylor Bockman c176d390bd first pass at option_string 6 years ago
Taylor Bockman 219032db9d trash talking Go 6 years ago
Taylor Bockman bf87428887 well...almost done with engine options 6 years ago
Taylor Bockman 7c5c3338cd Test in place for engine option string 6 years ago
Taylor Bockman 54cab71bd7 Engine option equality done...finally 6 years ago
Taylor Bockman cb4b8bbced some compiler errors, fix em and it will pass. Lots of equality issues 6 years ago
Taylor Bockman be2ca9fc75 Nearly there 6 years ago
Taylor Bockman 72433e7c73 lol 6 years ago
Taylor Bockman 039d1601ce Constants for options.rs 6 years ago
Taylor Bockman 74822a9f2d type documentation 6 years ago
Taylor Bockman 215af79b1e lol 6 years ago
Taylor Bockman 9c24d8c7c3 implement and test ready function 6 years ago
Taylor Bockman 759e5bbe7e lol 6 years ago
Taylor Bockman 64f4a7a89d lol 6 years ago
Taylor Bockman 969aed2734 Some small work on engine options 6 years ago
Taylor Bockman 3eca0caaa4 yep 6 years ago
Taylor Bockman bee54e617c headway on options 6 years ago
Taylor Bockman 4ed87916a5 Identity properly tested 6 years ago
Taylor Bockman 3eb729653a lol 6 years ago
Taylor Bockman 3e2fbfa216 fix flaw 6 years ago
Taylor Bockman 2fcacc815d lol 6 years ago
Taylor Bockman bae58bb10f some notes 6 years ago
Taylor Bockman 42f366b2aa lol 6 years ago
Taylor Bockman 78e23831e0 yep 6 years ago
Taylor Bockman a1d7ca7e64 instantiating engine works fine 6 years ago
Taylor Bockman 1875853925 enumerate basic commands 6 years ago
Taylor Bockman d462ce8d11 Some sales pitch things 6 years ago
  1. 7
      .travis.yml
  2. 19
      CONTRIBUTING.md
  3. 13
      Cargo.toml
  4. 139
      README.md
  5. 57
      src/commands.rs
  6. 87
      src/lib.rs
  7. 143
      src/options.rs
  8. 92
      src/parser.rs
  9. 82
      tests/lib.rs
  10. 51
      tests/options.rs
  11. 71
      tests/parser.rs

7
.travis.yml

@ -0,0 +1,7 @@
language: rust
rust:
- stable
- nightly
matrix:
allow_failures:
- rust: stable

19
CONTRIBUTING.md

@ -0,0 +1,19 @@
# Contributing to UCI
TODO
# Styleguide
We try to make use of the styleguide found [here](https://github.com/rust-lang-nursery/fmt-rfcs/blob/master/guide/guide.md).
## Character Limit
We try to make each line less than or equal to 120 charcters.
## Testing
Prefer seperating out your tests into discrete modules where possible. In general:
1. Engine "global" tests go into `tests/lib.rs`
2. Everything else gets it's own module inside `tests/`

13
Cargo.toml

@ -0,0 +1,13 @@
[package]
name = "uci"
version = "0.1.0"
authors = ["Taylor Bockman <angrygoats@protonmail.com>"]
description = "An implementation of the UCI specification in Rust"
repository = "https://github.com/angrygoats/uci"
readme = "README.md"
keywords = ["uci", "universal chess interface", "chess", "engine", "chess engine"]
categories = ["games", "game-engines", "api-bindings", "config", "development-tools"]
license = "GPL-3.0"
[dependencies]
"either" = "1.4.0"

139
README.md

@ -1,2 +1,137 @@
# uci
An implementation of the Universal Chess Interface in Rust
# UCI
UCI is an acronym for **U**niversal **C**hess **I**nterface. It is a standard for communication that competes with
XBoard/Winboard. [http://wbec-ridderkerk.nl/html/UCIProtocol.html](UCI) makes communication a little easier, but
it seems there's a nearly religious debate on which is better.
Here are some benefits to using UCI:
* It works with Chessbase 14
* Simple to use
* Fewer bugs in code
* Built from scratch rather than ad-hoc
* Flexible time controls
* Additional search information can be displayed
and to be fair, some downsides:
* Stateless
* Delegates some important decisions to the GUI
* Difficult to add new features on top of it
* Sends the whole move list each turn
Overall, UCI seems to be fairly popular and is worth considering for your next engine.
## Why
People shouldn't waste their time implementing protocols that really should be libraries. With this, you can
include it in your project, build your engine on top of it, and be able to focus on what matters - beating Stockfish.
### Yeah, but why Rust?
Originally I had written part of this library in C++. However, considering how prone people are to errors in C++, I
decided to choose a language that provides a little more safety to the user. Currently
[Rust is beating Go](http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=rust&lang2=go) in the Computer
Language Benchmark Game, so it was the winner. People who write chess engines care about performance. If you're not
writing C/C++, Rust seems like the next best thing.
## Will You Implement Winboard?
Yeah, probably.
## Why GPL 3.0?
I have noticed that there are no good _free_ solutions so that anyone can build a chess engine. Since UCI is
a common format, and something all engines (should) implement, making this chunk of it free makes _total_ sense.
## Usage
UCI is designed to complement your chess engine rather than be a one-stop shop for engine development.
As such, you will be responsible for maintaining the game loop. What UCI provides is a series of functions
that will help you send data to, and receive data from the GUI.
### First Steps
Your engine will first need to wait on STDIN for a command `uci` from the GUI. This indicates that your engine should
switch to UCI mode. You can use `commands::UCI` to make sure the command text you are waiting for is correct.
Next, you'll need to create a copy of `Engine` by calling `Engine::new`. Once this is setup, you can call
`Engine::identify` to send identification information to the GUI.
Once identification is done, you need to send your configuration options. This is dependent on your engine. Refer to
the UCI standard for the available options. Here is an example of an option configuration:
```rust
let o1 = EngineOption {
name: constants::HASH,
option_type: EngineOptionType::Spin,
option_data: [(EngineOptionDataType::DefaultVal, EngineOptionData::Int(1)),
(EngineOptionDataType::Min, EngineOptionData::Int(1)),
(EngineOptionDataType::Max, EngineOptionData::Int(128))
].iter().cloned().collect();
};
```
As nice as it would be to have fully typechecked options you will need to be careful a little here. The available
options your engine uses must be passed to `Engine::new` as an array of `Options::EngineOption<T>`
from `options.rs`. Every `Options::EngineOption<T>` has a `name`, which you can use any of the
`Options::*` constants to represent, a `type`, which can be an `Options::EngineOptionType`, and a hashmap
of `Options::EngineOptionDataType` to `Options::EngineOptionDataValue<T>`. Refer to the standard for more
information on option configurations. The `T` type parameters represents the type of that particular
`Options::EngineOption<T>` and allows us to at least fix the type in the HashMap to a single type. You can still
hurt yourself, but doing this makes it much harder. Keep this list of options close, the GUI may send back
information after call `Engine::send_available_options` to reconfigure default settings.
You must configure this before calling `Engine::new` so the rest of this guide assumes you've done that already.
To send your configuration options simply call `Engine::send_available_options`. Once this finishes `uciok` will
also be sent, indicating to the GUI your engine is ready to roll. At this point you need to set up two threads, one
to calculate with your engine, and one to read STDIN.
```
EXAMPLE HERE WITH LOOPS IN THREADS AND BLOCKING AND WHATEVER
```
Notice how in our STDIN thread we are calling `Engine::parse` and the handling the output using a match statement
depending on what kind of token it was. You are responsible for obeying the commands from this thread, the UCI library
just makes it convenient to work with. Here is an example of handling a parsed command:
```
EXAMPLE OF HANDLING A PARSED COMMAND AND SENDING BACK SOMETHING
```
At this point the engine will send a series of `setoption` commands to you in your STDIN thread. We can use the
`Engine::parse` function to get these:
```
EXAMPLE HERE
```
Once the options are extracted you will be responsible for configuring your engine with the values. The GUI will send
an `isready` command which you will be responsible for replying to by using `Engine::ready`.
**TODO: MORE STUFF WITH EXAMPLES**
**THINGS LIKE SENDINB BEST MOVE AFTER CALCULATING, ETC**
**RECEIVING SETOPTION COMMANDS AND PROCESSING THEM**
### Other Options
**TODO: Talk about the additional helpers available in the UCI library and what-not**.
* Copy protection checking
* Registration checking
** TODO: Put an example engine under `/examples` that does nothing but talks to the GUI and receives commands.
** Document it here and mention it can be used for guidance.
### Other Libraries in the works
In the future more libraries will be available to help chess engine developers get started. I will be writing
a library to give some standard implementations of [Zobrist Hashing](https://en.wikipedia.org/wiki/Zobrist_hashing),
among other useful tools that are commonly re-implemented a thousand times for every engine.

57
src/commands.rs

@ -0,0 +1,57 @@
//! Commands contains the command constants expected during the UCI lifecycle
//! It is useful to note many of these commands do not end with '\n'. This is because they expect arguments.
///////// Engine to GUI /////////
/// ID is used to signal the engine name and author to the GUI
pub const ID: &'static str = "id";
/// NAME is used to signal the engine name
pub const NAME: &'static str = "name";
/// AUTHOR is used to signal the engine author
pub const AUTHOR: &'static str = "author";
/// UCIOK is sent after the ID and optional options to tell the GUI that the engine has sent all infos and is ready
/// in UCI mode
pub const UCIOK: &'static str = "uciok\n";
/// READYOK is sent when the engine has received an "isready" command and has processed all input and is ready to
/// accept new commands now
pub const READYOK: &'static str = "readyok\n";
/// BESTMOVE is sent when the engine has stopped searching and found the move best in this position
pub const BESTMOVE: &'static str = "bestmove";
/// COPYPROTECTIONCHECKING tells the GUI that the engine is checking the copy protection
pub const COPYPROTECTIONCHECKING: &'static str = "copyprotection checking\n";
/// COPYPROTECTIONOK tells the GUI that the engine has verified the copy protection
pub const COPYPROTECTIONOK: &'static str = "copyprotection ok\n";
/// COPYPROTECTIONERROR tells the GUI that the engine has rejected the copy protection
pub const COPYPROTECTIONERROR: &'static str = "copyprotection error\n";
/// REGISTRATIONCHECKING tells the GUI that the engine is checking the registration
pub const REGISTRATIONCHECKING: &'static str = "registration checking\n";
/// REGISTRATIONOK tells the GUI that the engine the registration has been verified
pub const REGISTRATIONOK: &'static str = "registration ok\n";
/// REGISTRATIONERROR tells the GUI that the engine has rejected the registration
pub const REGISTRATIONERROR: &'static str = "registration error\n";
/// INFO tells the GUI the engine wants to sent infos to the GUI. This should be done whenever the info has changed
pub const INFO: &'static str = "info";
/// OPTION tells the GUI which parameters can be changed in the engine
pub const OPTION: &'static str = "option";
/// OPTIONNAME tells the GUI which option name is being sent
pub const OPTIONNAME: &'static str = "name";
/// TYPE tells the GUI the type of the option
pub const TYPE: &'static str = "type";

87
src/lib.rs

@ -0,0 +1,87 @@
//! 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.
// BIG TODO: Change pub struct members to private and use getters/setters. DO THIS FOR EVERYTHING AFTER YOU
// IMPLEMENT OPTION SENDING.
// ALSO CHANGE COMMANDS TO HAVE A CONSTANTS SUBMODULE LIKE OPTIONS
extern crate either;
pub mod commands;
pub mod options;
pub mod parser;
use std::io::{BufRead, Write};
use std::vec;
#[derive(Debug)]
pub struct Engine<'a, R, W> {
pub name: &'a str,
pub author: &'a str,
pub reader: R,
pub writer: W,
pub engine_options: Vec<options::EngineOption>,
}
/// 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.
///
///
/// TODO: THE NEXT THING - THE PARSER! (after putting in getters but _no_ setters and taking away pub fields
/// for all structs in the app if that is idiomatic)
///
/// 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 <token, value>. For example it would parse `setoption` into an option constant
/// and the value to set it to. This should be super generic and parsing results should just be of a type
/// ParseResult. The tuple implements ParseResult which represents a tuple of a token and value.
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_options: Vec<options::EngineOption>) -> Engine<'a, R, W> {
Engine { name, author, reader, writer, engine_options,
}
}
/// 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) {
for eo in &self.engine_options {
write!(&mut self.writer, "{}", eo.to_string()).expect(&format!("failed to send `{}`", eo.to_string()));
}
// 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");
}
/// Sends `readyok` to the GUI
pub fn ready(&mut self) {
write!(&mut self.writer, "{}", commands::READYOK).expect("failed to send `readyok` command");
}
}

143
src/options.rs

@ -0,0 +1,143 @@
//! Options contains everything related to engine options. The idea behind this is to take as much
//! advantage of the typechecker as possible. As a result, engine option names are constant static strings,
//! and EngineOption tries to be flexible so it can be reused for each option, which also maintaining some
//! of the nicer parts of typechecking.
use std::collections::HashMap;
use commands;
/// These constants can be used for naming options easily. Option name is fairly flexible.
pub mod constants {
/// Represents the hash option
pub const HASH: &'static str = "Hash";
/// Represents the Nalimov Path option
pub const NALIMOVPATH: &'static str = "NalimovPath";
/// Represents the Nalimov Cache option
pub const NALIMOVCACHE: &'static str = "NalimovCache";
/// Represents the ponder option
pub const PONDER: &'static str = "Ponder";
/// Represents the OwnBook option
pub const OWNBOOK: &'static str = "OwnBook";
/// Represents the MultiPV option
pub const MULTIPV: &'static str = "MultiPV";
/// Represents the UCI_ShowCurrLine option
pub const UCISHOWCURRLINE: &'static str = "UCI_ShowCurrLine";
/// Represents the UCI_Refutations option
pub const UCISHOWREFUTATIONS: &'static str = "UCI_ShowRefutations";
/// Represents the UCI_LimitStrength option
pub const UCILIMITSTRENGTH: &'static str = "UCI_LimitStrength";
/// Represents the UCI_Elo option
pub const UCIELO: &'static str = "UCI_Elo";
/// Represents the UCI_AnalysisMode option
pub const UCIANALYSISMODE: &'static str = "UCI_AnalysisMode";
/// Represents the UCI_Opponent option
pub const UCIOPPONENT: &'static str = "UCI_Opponent";
}
/// The `EngineOptionType` type used to indicate what type of option the GUI should display
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum EngineOptionType {
Check,
Spin,
Combo,
Button,
TypeString, // `String` is a reserved word so `TypeString` is substituted
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
/// The `EngineOptionData` makes the data type generic so one `EngineOption` can represent everything
/// This would be set to the type of the engine option (ex. i32)
pub enum EngineOptionData {
Int(i32),
Float(f64),
Text(String),
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
/// The `EngineOptionDataType` type used to indicate the type of the `EngineOption` setting
pub enum EngineOptionDataType {
DefaultVal, // `Default` is reserved so `DefaultVal` is used
Min,
Max,
Var,
}
#[derive(Debug, PartialEq)]
/// The `EngineOption` type is the overarching type representing a single configurable engine option
pub struct EngineOption {
pub name: &'static str,
pub option_type: EngineOptionType,
pub option_data: HashMap<EngineOptionDataType, EngineOptionData>,
}
impl EngineOption {
/// Constructs a new EngineOption of type T
pub fn new(name: &'static str, option_type: EngineOptionType,
option_data: HashMap<EngineOptionDataType, EngineOptionData>) -> EngineOption {
EngineOption { name, option_type, option_data, }
}
fn engine_option_data_string(d: &EngineOptionData) -> String {
match d {
&EngineOptionData::Int(v) => v.to_string(),
&EngineOptionData::Float(v) => v.to_string(),
&EngineOptionData::Text(ref v) => v.clone(),
}
}
/// Turns the EngineOption into a string that can be sent to the GUI
pub fn to_string(&self) -> String {
let mut option_data_string: String = String::new();
// NOTE: The user is left to understand what option takes which of these data type values. There's some
// work that can be done later switching based on the option type, but for now it's reasonable to
// expect the user understands the standard well enough to know which options take what kind
// of settings.
if self.option_data.contains_key(&EngineOptionDataType::DefaultVal) {
let data = self.option_data.get(&EngineOptionDataType::DefaultVal);
option_data_string.push_str(&format!(" {} {}", "default",
EngineOption::engine_option_data_string(&data.unwrap())));
}
if self.option_data.contains_key(&EngineOptionDataType::Min) {
let data = self.option_data.get(&EngineOptionDataType::Min);
option_data_string.push_str(&format!(" {} {}", "min", EngineOption::engine_option_data_string(&data.unwrap())));
}
if self.option_data.contains_key(&EngineOptionDataType::Max) {
let data = self.option_data.get(&EngineOptionDataType::Max);
option_data_string.push_str(&format!(" {} {}", "max", EngineOption::engine_option_data_string(&data.unwrap())));
}
if self.option_data.contains_key(&EngineOptionDataType::Var) {
let data = self.option_data.get(&EngineOptionDataType::Var);
option_data_string.push_str(&format!(" {} {}", "var", EngineOption::engine_option_data_string(&data.unwrap())));
}
let ots = match self.option_type {
EngineOptionType::Check => "check",
EngineOptionType::Spin => "spin",
EngineOptionType::Combo => "combo",
EngineOptionType::Button => "button",
EngineOptionType::TypeString => "string",
};
format!("{} {} {} {} {}{}\n", commands::OPTION, commands::OPTIONNAME, self.name,
commands::TYPE, ots, option_data_string)
}
}

92
src/parser.rs

@ -0,0 +1,92 @@
//! Parser contains the command parser for handling receiving commands from the GUI
use std::collections::HashMap;
use commands;
use either::Either;
pub use either::Left;
pub use either::Right;
/// Token represents a parsable token in the string sent via STDIN from the GUI
#[derive(Debug, Eq, PartialEq)]
pub enum Token {
UCI,
DEBUG,
ISREADY,
SETOPTION,
REGISTER,
UCINEWGAME,
POSITION,
GO,
STOP,
PONDERHIT,
QUIT,
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum CommandValue {
Int(i32),
Float(f64),
Text(String),
Boolean(bool),
}
pub struct Parser {}
/// TokenResult is a convenient way to package an entire command string. It is identified by it's associated
/// Token, and the arguments.
///
/// Example:
///
/// setoption name Hash value 32
/// --------- ---- ---- ----- --
/// Token arg val arg val
///
/// debug on
/// ----- --
/// Token val
///
///
/// The user is still responsible for knowing what to look for in the hashmap but this structure makes it far
/// easier to work with commands. Some commands don't have arguments, in that case the args will be None.
#[derive(Debug, PartialEq)]
pub struct TokenResult {
token: Token,
args: Either<Option<HashMap<&'static str, CommandValue>>, &'static str>,
}
impl TokenResult {
/// Intantiates a new TokenResult
pub fn new(token: Token, args: Either<Option<HashMap<&'static str, CommandValue>>, &'static str>>) -> TokenResult {
TokenResult { token, args }
}
}
impl Parser {
/// Instantiates a new Parser
pub fn new() -> Parser {
Parser{}
}
/// Parses a single string from the GUI and turns it into a neatly packaged TokenResult for processing
/// by the engine.
pub fn parse(&self, s: &str) -> Result<TokenResult, &'static str> {
// TODO: Remove the /n from the end
let tokens: Vec<&str> = s.split_whitespace().collect::<Vec<&str>>();
// TODO: Parse things based on the token, probably need a special function for each token based on a switch
// of tokens[0].
// remove this when it's working
Ok(TokenResult::new(Token::SETOPTION, Left(Some(HashMap::new()))))
}
}

82
tests/lib.rs

@ -0,0 +1,82 @@
extern crate uci;
use uci::Engine;
use uci::options::constants;
use uci::options::{ EngineOption, EngineOptionType, EngineOptionDataType, EngineOptionData };
use std::str;
#[test]
fn instantiate_new_engine() {
let input = b"UNUSED";
let mut output = Vec::new();
let e = Engine::new("test_name", "test", &input[..], &mut output, vec!());
assert_eq!(e.name, "test_name");
assert_eq!(e.author, "test");
}
#[test]
fn send_identification_data() {
let input = b"UNUSED";
let mut output = Vec::new();
// We need to scope this so that the mutable borrow ends and we can test it safely
{
let mut e = Engine::new("test_name", "test", &input[..], &mut output, vec!());
e.identify();
}
assert_eq!(str::from_utf8(&output).unwrap_or("Unwrapping output failed in send_identification_data"),
"id name test_name\nid author test\n");
}
#[test]
fn send_readyok() {
let input = b"UNUSED";
let mut output = Vec::new();
{
let mut e = Engine::new("test_name", "test", &input[..], &mut output, vec!());
e.ready();
}
assert_eq!(str::from_utf8(&output).unwrap_or("Unwrapping output failed in send_readyok"),
"readyok\n");
}
#[test]
fn send_available_engine_options() {
let input = b"UNUSED";
let mut output = Vec::new();
let o1 = EngineOption {
name: constants::HASH,
option_type: EngineOptionType::Spin,
option_data: [(EngineOptionDataType::DefaultVal, EngineOptionData::Int(1)),
(EngineOptionDataType::Min, EngineOptionData::Int(1)),
(EngineOptionDataType::Max, EngineOptionData::Int(128))
].iter().cloned().collect(),
};
let o2 = EngineOption {
name: constants::NALIMOVPATH,
option_type: EngineOptionType::TypeString,
option_data: [(EngineOptionDataType::DefaultVal, EngineOptionData::Text(String::from(r"c:\"))),
].iter().cloned().collect(),
};
let o3 = EngineOption {
name: "Clear Hash",
option_type: EngineOptionType::Button,
option_data: [].iter().cloned().collect(),
};
{
let mut e = Engine::new("test_name", "test", &input[..], &mut output, vec!(o1, o2, o3));
e.send_available_options();
}
assert_eq!(str::from_utf8(&output).unwrap_or("Unwrapping output failed in send_identification_data"),
"option name Hash type spin default 1 min 1 max 128\n\
option name NalimovPath type string default c:\\\n\
option name Clear Hash type button\n\
uciok\n");
}

51
tests/options.rs

@ -0,0 +1,51 @@
extern crate uci;
use uci::options::constants;
use uci::options::{ EngineOption, EngineOptionType, EngineOptionDataType, EngineOptionData };
#[test]
fn engine_option_equality() {
let name = constants::HASH;
let option_type = EngineOptionType::Spin;
let option_data1 =
[(EngineOptionDataType::DefaultVal, EngineOptionData::Int(1)),
(EngineOptionDataType::Min, EngineOptionData::Int(1)),
(EngineOptionDataType::Max, EngineOptionData::Int(128))
].iter().cloned().collect();
let option_data2 =
[(EngineOptionDataType::DefaultVal, EngineOptionData::Int(1)),
(EngineOptionDataType::Min, EngineOptionData::Int(1)),
(EngineOptionDataType::Max, EngineOptionData::Int(128))
].iter().cloned().collect();
let o1 = EngineOption {
name: name,
option_type: option_type,
option_data: option_data1,
};
let o2 = EngineOption {
name: name,
option_type: option_type,
option_data: option_data2,
};
assert_eq!(o1, o2);
}
#[test]
fn engine_option_string() {
let name = constants::HASH;
let option_type = EngineOptionType::Spin;
let option_data =
[(EngineOptionDataType::DefaultVal, EngineOptionData::Int(1)),
(EngineOptionDataType::Min, EngineOptionData::Int(1)),
(EngineOptionDataType::Max, EngineOptionData::Int(128))
].iter().cloned().collect();
let o = EngineOption { name, option_type, option_data, };
let expected = "option name Hash type spin default 1 min 1 max 128\n";
assert_eq!(o.to_string(), expected);
}

71
tests/parser.rs

@ -0,0 +1,71 @@
// TODO: Write parser tests before implementing the parser.
// Start with setoption and branch out from there. The commands are finite so you should be able
// to write a test for each one. Take the time to do this right so that the rest of this project is
// a cakewalk
//
// // Also remember to fix the constant thing noted in the lib.rs for uci.... lol
//
extern crate uci;
use uci::parser;
#[test]
fn parse_setoption() {
let s = "setoption name Hash value 32\n";
let expected = parser::TokenResult::new(
parser::Token::SETOPTION,
parser::Left(Some(
[
("name", parser::CommandValue::Text(String::from("Hash"))),
("value", parser::CommandValue::Int(32)),
].iter().cloned().collect())
));
let p = parser::Parser::new();
match p.parse(s) {
Ok(r) => {
assert_eq!(r, expected);
}
Err(_) => panic!("failed to parse setoption as expected")
}
}
#[test]
fn parse_uci() {
let s = "uci\n";
let expected = parser::TokenResult::new(
parser::Token::UCI, parser::Left(None);
let p = parser::Parser::new();
match p.parse(s) {
Ok(r) => {
assert_eq!(r, expected);
}
Err(_) => panic!("failed to parse uci as expected")
}
}
#[test]
fn parse_debug() {
let s = "debug on\n";
let expected = parser::TokenResult::new(
parser::Token::DEBUG, parser::Right("on")
);
let p = parser::Parser::new();
match p.parse(s) {
Ok(r) => {
assert_eq!(r, expected);
}
Err(_) => panic!("failed to parse uci as expected")
}
}
#[test]
fn bad_parse() {
assert_eq!(true, false);
}
Loading…
Cancel
Save