hexe/engine/
uci.rs

1use super::*;
2
3use std::io::{self, BufRead};
4use std::mem;
5use std::str;
6
7
8use scoped_threadpool::Scope;
9
10use core::color::Color;
11use core::mv::Move;
12use util::MutRef;
13
14const WHITE: usize = Color::White as usize;
15const BLACK: usize = Color::Black as usize;
16
17macro_rules! name { () => { "Hexe" } }
18
19macro_rules! authors { () => { "Nikolai Vazquez" } }
20
21macro_rules! id {
22    ($mac:ident) => {
23        concat!("id ", stringify!($mac), " ", $mac!())
24    }
25}
26
27struct Limits {
28    ponder: bool,
29    infinite: bool,
30    moves_to_go: u32,
31    time: [u32; 2],
32    inc: [u32; 2],
33    depth: u32,
34    nodes: u32,
35    mate: u32,
36    move_time: u32,
37}
38
39impl Default for Limits {
40    fn default() -> Limits {
41        // Safe because `bool` uses 0 to represent `false`
42        unsafe { mem::zeroed() }
43    }
44}
45
46type UciIter<'a> = str::SplitWhitespace<'a>;
47
48/// Runs the engine via the [Universal Chess Interface][uci] (UCI) protocol.
49///
50/// [uci]: http://wbec-ridderkerk.nl/html/UCIProtocol.html
51pub struct Uci<'a>(MutRef<'a, Engine>);
52
53impl<'a> From<&'a mut Engine> for Uci<'a> {
54    #[inline]
55    fn from(engine: &'a mut Engine) -> Uci<'a> {
56        Uci(MutRef::Borrowed(engine))
57    }
58}
59
60impl<'a> From<Box<Engine>> for Uci<'a> {
61    #[inline]
62    fn from(engine: Box<Engine>) -> Uci<'a> {
63        Uci(MutRef::Owned(engine))
64    }
65}
66
67impl<'a> Default for Uci<'a> {
68    fn default() -> Uci<'a> {
69        Uci(MutRef::Owned(Box::default()))
70    }
71}
72
73impl<'a> Uci<'a> {
74    /// Returns a reference to the underlying engine over which `self` iterates.
75    #[inline]
76    pub fn engine(&self) -> &Engine { &self.0 }
77
78    /// Returns a mutable reference to the underlying engine over which `self`
79    /// iterates.
80    #[inline]
81    pub fn engine_mut(&mut self) -> &mut Engine { &mut self.0 }
82
83    /// Runs the UCI loop, feeding commands from `stdin`.
84    ///
85    /// This method retains a lock on `stdin` until it exits. To feed commands
86    /// differently, use [`start_with`](#method.start_with).
87    ///
88    /// # Examples
89    ///
90    /// Basic usage:
91    ///
92    /// ```rust,norun
93    /// use hexe::engine::Engine;
94    ///
95    /// let mut engine = Engine::default();
96    /// engine.uci().start();
97    /// ```
98    pub fn start(&mut self) {
99        let Engine { ref mut pool, ref mut engine } = *self.0;
100        pool.scoped(|scope| {
101            let stdin = io::stdin();
102            let lines = stdin.lock()
103                             .lines()
104                             .filter_map(Result::ok);
105            for line in lines {
106                if !engine.run_uci_line(scope, &line) {
107                    break;
108                }
109            }
110        });
111    }
112
113    /// Runs the UCI loop, feeding commands from an iterator.
114    ///
115    /// # Examples
116    ///
117    /// The UCI can be fed command line arguments.
118    ///
119    /// ```rust,norun
120    /// use hexe::engine::Engine;
121    /// use std::env;
122    ///
123    /// let mut args = env::args();
124    /// args.next(); // discard program name
125    ///
126    /// let mut engine = Engine::default();
127    /// engine.uci().start_with(args);
128    /// ```
129    pub fn start_with<I>(&mut self, commands: I)
130        where I: IntoIterator,
131              I::Item: AsRef<str>,
132    {
133        let Engine { ref mut pool, ref mut engine } = *self.0;
134        pool.scoped(|scope| {
135            for line in commands {
136                engine.run_uci(scope, line.as_ref());
137            }
138        });
139    }
140
141    /// Runs a single UCI command or multiple if newlines are found.
142    #[inline]
143    pub fn run(&mut self, command: &str) {
144        let Engine { ref mut pool, ref mut engine } = *self.0;
145        pool.scoped(|scope| {
146            engine.run_uci(scope, command);
147        });
148    }
149}
150
151macro_rules! unknown_command {
152    ($cmd:expr) => { println!("Unknown command: {}", $cmd) }
153}
154
155impl EngineInner {
156    fn run_uci(&mut self, scope: &Scope, command: &str) {
157        if command.is_empty() {
158            unknown_command!(command);
159        } else {
160            for line in command.lines() {
161                if !self.run_uci_line(scope, line) {
162                    break;
163                }
164            }
165        }
166    }
167
168    fn run_uci_line(&mut self, scope: &Scope, line: &str) -> bool {
169        let mut split = line.split_whitespace();
170        match split.next().unwrap_or("") {
171            "quit"       => return false,
172            "uci"        => self.cmd_uci(),
173            "stop"       => self.cmd_stop(),
174            "ponderhit"  => self.cmd_ponder_hit(),
175            "position"   => self.cmd_position(split),
176            "setoption"  => self.cmd_set_option(split),
177            "ucinewgame" => self.cmd_new_game(),
178            "go"         => self.cmd_go(split),
179            "isready"    => println!("readyok"),
180            _            => unknown_command!(line),
181        }
182        true
183    }
184
185    fn cmd_uci(&self) {
186        println!(id!(name));
187        println!(id!(authors));
188        self.options.report();
189        println!("uciok");
190    }
191
192    fn cmd_stop(&mut self) {
193
194    }
195
196    fn cmd_ponder_hit(&mut self) {
197
198    }
199
200    fn cmd_position(&mut self, _: UciIter) {
201
202    }
203
204    fn cmd_set_option(&mut self, mut iter: UciIter) {
205        iter.next(); // consume "name"
206
207        let mut name  = String::new();
208        let mut value = String::new();
209
210        while let Some(next) = iter.next() {
211            if next == "value" {
212                break;
213            }
214            if !name.is_empty() {
215                name.push(' ');
216            }
217            name.push_str(next);
218        }
219
220        for next in iter {
221            if !value.is_empty() {
222                value.push(' ');
223            }
224            value.push_str(next);
225        }
226
227        if !self.options.set(&name, &value) {
228            println!("No such option: {}", name);
229        }
230    }
231
232    fn cmd_new_game(&mut self) {
233
234    }
235
236    fn cmd_go(&mut self, mut iter: UciIter) {
237        let mut limits = Limits::default();
238        let mut moves  = Vec::<Move>::new();
239
240        macro_rules! update {
241            ($val:expr) => {
242                if let Some(Ok(val)) = iter.next().map(str::parse) {
243                    $val = val
244                }
245            }
246        }
247
248        while let Some(next) = iter.next() {
249            match next {
250                "searchmoves" => while let Some(m) = iter.next() {
251                    if let Some(mv) = self.cmd_read_move(m) {
252                        moves.push(mv);
253                    }
254                },
255                "ponder"    => limits.ponder = true,
256                "infinite"  => limits.infinite = true,
257                "wtime"     => update!(limits.time[WHITE]),
258                "btime"     => update!(limits.time[BLACK]),
259                "winc"      => update!(limits.inc[WHITE]),
260                "binc"      => update!(limits.inc[BLACK]),
261                "movestogo" => update!(limits.moves_to_go),
262                "depth"     => update!(limits.depth),
263                "nodes"     => update!(limits.nodes),
264                "mate"      => update!(limits.mate),
265                "movetime"  => update!(limits.move_time),
266                _ => continue,
267            }
268        }
269
270        self.cmd_start_thinking(&limits, &moves);
271    }
272
273    fn cmd_read_move(&self, s: &str) -> Option<Move> {
274        None
275    }
276
277    fn cmd_start_thinking(&mut self, limits: &Limits, moves: &[Move]) {
278
279    }
280}