engine: cleaning
This commit is contained in:
parent
5d61db1fba
commit
5771930e97
16
src/board.rs
16
src/board.rs
|
@ -69,6 +69,14 @@ pub fn pos(s: &str) -> Pos {
|
|||
((chars[0] - 0x61) as i8, (chars[1] - 0x31) as i8)
|
||||
}
|
||||
|
||||
/// Return string coordinates from Pos.
|
||||
pub fn pos_string(p: &Pos) -> String {
|
||||
let mut bytes = [0u8; 2];
|
||||
bytes[0] = (p.0 + 0x61) as u8;
|
||||
bytes[1] = (p.1 + 0x31) as u8;
|
||||
String::from_utf8_lossy(&bytes).to_string()
|
||||
}
|
||||
|
||||
/// Bitboard representation of a chess board.
|
||||
///
|
||||
/// 64 squares, from A1, A2 to H7, H8. A square is an u8, with bits
|
||||
|
@ -229,6 +237,14 @@ mod tests {
|
|||
assert_eq!(pos("h8"), (7, 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pos_string() {
|
||||
assert_eq!(pos_string(&(0, 0)), "a1");
|
||||
assert_eq!(pos_string(&(0, 1)), "a2");
|
||||
assert_eq!(pos_string(&(0, 7)), "a8");
|
||||
assert_eq!(pos_string(&(7, 7)), "h8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_from_fen() {
|
||||
let b1 = new();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Vatu engine.
|
||||
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
|
||||
use rand::seq::IteratorRandom;
|
||||
|
||||
|
@ -35,7 +37,7 @@ pub enum Cmd {
|
|||
UciGo(Vec<uci::GoArgs>), // UCI "go" command.
|
||||
|
||||
// Commands that can be sent by the engine.
|
||||
BestMove(board::Move),
|
||||
BestMove(Option<board::Move>),
|
||||
}
|
||||
|
||||
pub const CASTLING_WH_K: u8 = 0b00000001;
|
||||
|
@ -138,15 +140,25 @@ impl Engine {
|
|||
self.fullmove = fullmove.parse::<i32>().ok().unwrap();
|
||||
}
|
||||
|
||||
fn apply_moves(&mut self, moves: &Vec<board::Move>) {
|
||||
moves.iter().for_each(|m| self.apply_move(m));
|
||||
}
|
||||
|
||||
fn apply_move(&mut self, m: &board::Move) {
|
||||
board::apply_into(&mut self.board, m);
|
||||
}
|
||||
|
||||
/// Start working on board, returning the best move found.
|
||||
///
|
||||
/// Stop working after `movetime` ms, or go on forever if it's -1.
|
||||
pub fn work(&mut self, _movetime: i32) -> board::Move {
|
||||
pub fn work(&mut self, movetime: i32) -> Option<board::Move> {
|
||||
// Stupid engine! Return a random move.
|
||||
let moves = rules::get_player_legal_moves(&self.board, self.color);
|
||||
let mut rng = rand::thread_rng();
|
||||
let best_move = moves.iter().choose(&mut rng).unwrap();
|
||||
*best_move
|
||||
let best_move = moves.iter().choose(&mut rng).and_then(|m| Some(*m));
|
||||
// board::draw(&self.board);
|
||||
thread::sleep(time::Duration::from_millis(movetime as u64));
|
||||
best_move
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,6 +192,9 @@ impl Engine {
|
|||
uci::PositionArgs::Startpos => {
|
||||
let fen = notation::parse_fen(notation::FEN_START).unwrap();
|
||||
self.apply_fen(&fen);
|
||||
},
|
||||
uci::PositionArgs::Moves(moves) => {
|
||||
self.apply_moves(&moves);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ fn cmd_cli(args: &ArgMatches) -> i32 {
|
|||
}
|
||||
|
||||
fn cmd_uci(args: &ArgMatches) -> i32 {
|
||||
let output = args.value_of("output");
|
||||
let output = args.value_of("log_file");
|
||||
uci::Uci::start(output);
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,5 +1,20 @@
|
|||
//! Functions using various notations.
|
||||
|
||||
use crate::board;
|
||||
|
||||
pub const NULL_MOVE: &str = "0000";
|
||||
|
||||
pub fn move_to_string(m: &board::Move) -> String {
|
||||
let mut move_string = String::new();
|
||||
move_string.push_str(&board::pos_string(&m.0));
|
||||
move_string.push_str(&board::pos_string(&m.1));
|
||||
move_string
|
||||
}
|
||||
|
||||
pub fn parse_move(m_str: &str) -> board::Move {
|
||||
(board::pos(&m_str[0..2]), board::pos(&m_str[2..4]))
|
||||
}
|
||||
|
||||
pub const FEN_START: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/// FEN notation for positions, split into fields.
|
||||
|
@ -36,6 +51,17 @@ pub fn parse_fen_fields(fields: &[&str]) -> Option<Fen> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_move_to_string() {
|
||||
assert_eq!(move_to_string(&((0, 0), (3, 3))), "a1d4");
|
||||
assert_eq!(move_to_string(&((7, 7), (0, 7))), "h8a8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_move() {
|
||||
assert_eq!(parse_move("a1d4"), ((0, 0), (3, 3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fen() {
|
||||
let fen_start = parse_fen(FEN_START).unwrap();
|
||||
|
|
55
src/uci.rs
55
src/uci.rs
|
@ -57,6 +57,7 @@ pub enum UciCmd {
|
|||
pub enum PositionArgs {
|
||||
Startpos,
|
||||
Fen(notation::Fen),
|
||||
Moves(Vec<board::Move>),
|
||||
}
|
||||
|
||||
/// Arguments for the go remote commands.
|
||||
|
@ -112,11 +113,9 @@ impl Uci {
|
|||
}
|
||||
|
||||
fn log(&mut self, s: String) {
|
||||
match self.logfile.as_ref() {
|
||||
Some(mut f) => {
|
||||
f.write_all(s.as_bytes()).unwrap();
|
||||
f.write_all("\n".as_bytes()).unwrap();
|
||||
f.flush().unwrap();
|
||||
match &mut self.logfile {
|
||||
Some(f) => {
|
||||
writeln!(f, "{}", s).unwrap();
|
||||
}
|
||||
None => {
|
||||
eprintln!("{}", s);
|
||||
|
@ -135,6 +134,7 @@ impl Uci {
|
|||
pub fn read_stdin(tx: mpsc::Sender<Cmd>) {
|
||||
let mut s = String::new();
|
||||
loop {
|
||||
s.clear();
|
||||
match io::stdin().read_line(&mut s) {
|
||||
Ok(_) => {
|
||||
let s = s.trim();
|
||||
|
@ -147,7 +147,6 @@ impl Uci {
|
|||
eprintln!("Failed to read input: {:?}", e);
|
||||
}
|
||||
}
|
||||
s.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,8 +216,12 @@ impl Uci {
|
|||
}
|
||||
|
||||
/// Send best move.
|
||||
fn send_bestmove(&mut self, m: &board::Move) {
|
||||
self.send(&format!("bestmove {:?}", m)); // TODO notation conversion
|
||||
fn send_bestmove(&mut self, m: &Option<board::Move>) {
|
||||
let move_str = match m {
|
||||
Some(m) => notation::move_to_string(m),
|
||||
None => notation::NULL_MOVE.to_string(),
|
||||
};
|
||||
self.send(&format!("bestmove {}", move_str));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,20 +248,34 @@ fn parse_command(s: &str) -> UciCmd {
|
|||
|
||||
/// Parse an UCI "position" command.
|
||||
fn parse_position_command(fields: &[&str]) -> UciCmd {
|
||||
// Currently we only match the first subcommand; moves are not supported.
|
||||
let num_fields = fields.len();
|
||||
let mut i = 0;
|
||||
let mut subcommands = vec!();
|
||||
match fields[0] {
|
||||
// Subcommand "fen" is followed by a FEN string.
|
||||
"fen" => {
|
||||
if let Some(fen) = notation::parse_fen_fields(&fields[1..7]) {
|
||||
subcommands.push(PositionArgs::Fen(fen))
|
||||
} else {
|
||||
return UciCmd::Unknown(format!("Bad format for position fen"))
|
||||
while i < num_fields {
|
||||
match fields[i] {
|
||||
// Subcommand "fen" is followed by a FEN string.
|
||||
"fen" => {
|
||||
if let Some(fen) = notation::parse_fen_fields(&fields[i + 1 .. i + 7]) {
|
||||
subcommands.push(PositionArgs::Fen(fen))
|
||||
} else {
|
||||
return UciCmd::Unknown(format!("Bad format for position fen"))
|
||||
}
|
||||
i += 6;
|
||||
}
|
||||
// Subcommand "startpos" assumes the board is a new game.
|
||||
"startpos" => subcommands.push(PositionArgs::Startpos),
|
||||
// Subcommand "moves" is followed by moves until the end of the command.
|
||||
"moves" => {
|
||||
let mut moves = vec!();
|
||||
while i + 1 < num_fields {
|
||||
moves.push(notation::parse_move(fields[i + 1]));
|
||||
i += 1;
|
||||
}
|
||||
subcommands.push(PositionArgs::Moves(moves));
|
||||
},
|
||||
f => return UciCmd::Unknown(format!("Unknown position subcommand: {}", f)),
|
||||
}
|
||||
// Subcommand "startpos" assumes the board is a new game.
|
||||
"startpos" => subcommands.push(PositionArgs::Startpos),
|
||||
f => return UciCmd::Unknown(format!("Unknown position subcommand: {}", f)),
|
||||
i += 1;
|
||||
}
|
||||
UciCmd::Position(subcommands)
|
||||
}
|
||||
|
|
Reference in a new issue