///|
/// Synchronous, transport-independent UCCI command session.
pub struct Engine {
mut game : Game
mut closed : Bool
} derive(Debug)
///|
pub fn Engine::new() -> Engine {
{ game: Game::new(initial()), closed: false, }
}
///|
pub fn Engine::fen(self : Engine) -> String {
self.game.board().fen()
}
///|
pub fn Engine::command(self : Engine, line : String) -> String raise ChessError {
if self.closed {
raise Invalid("engine closed")
}
let args = line
.trim()
.split(" ")
.filter(x => !x.is_empty())
.map(x => x.to_owned())
.collect()
if args.is_empty() {
raise Invalid("empty UCCI command")
}
match args[0] {
"ucci" => "id name MoonBit Xiangqi Local\nucciok"
"isready" => "readyok"
"ucinewgame" => {
self.game = Game::new(initial())
""
}
"quit" => {
self.closed = true
""
}
"stop" => ""
"position" => {
if args.length() < 2 {
raise Invalid("position arguments")
}
let mut end = args.length()
for i in 2.. {
if args.length() != 3 || args[1] != "depth" {
raise Invalid("synchronous core supports go depth 1..64")
}
let mut depth = 0
if args[2].is_empty() || args[2].length() > 2 {
raise Invalid("depth range")
}
for c in args[2] {
if c < '0' || c > '9' {
raise Invalid("depth range")
}
depth = depth * 10 + c.to_int() - 48
}
let result = self.game.search(depth, node_limit=50000)
if result.stopped {
raise Invalid("search node budget")
}
"bestmove " +
(match result.best {
Some(m) => m.coordinate()
None => "0000"
})
}
_ => raise Invalid("unsupported UCCI command")
}
}
///|
pub fn Engine::status(self : Engine) -> String {
self.game.status()
}
///|
pub fn Engine::search(
self : Engine,
depth : Int,
node_limit? : Int = 100000,
should_stop? : () -> Bool = () => false,
on_iteration? : (SearchResult) -> Unit = _ => (),
) -> SearchResult raise ChessError {
if self.closed {
raise Invalid("engine closed")
}
self.game.search(depth, node_limit~, should_stop~, on_iteration~)
}