engine: cleaning

This commit is contained in:
dece 2020-06-03 22:32:30 +02:00
parent 5d61db1fba
commit 5771930e97
5 changed files with 98 additions and 24 deletions

View file

@ -69,6 +69,14 @@ pub fn pos(s: &str) -> Pos {
((chars[0] - 0x61) as i8, (chars[1] - 0x31) as i8) ((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. /// Bitboard representation of a chess board.
/// ///
/// 64 squares, from A1, A2 to H7, H8. A square is an u8, with bits /// 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)); 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] #[test]
fn test_new_from_fen() { fn test_new_from_fen() {
let b1 = new(); let b1 = new();

View file

@ -1,6 +1,8 @@
//! Vatu engine. //! Vatu engine.
use std::sync::mpsc; use std::sync::mpsc;
use std::thread;
use std::time;
use rand::seq::IteratorRandom; use rand::seq::IteratorRandom;
@ -35,7 +37,7 @@ pub enum Cmd {
UciGo(Vec<uci::GoArgs>), // UCI "go" command. UciGo(Vec<uci::GoArgs>), // UCI "go" command.
// Commands that can be sent by the engine. // Commands that can be sent by the engine.
BestMove(board::Move), BestMove(Option<board::Move>),
} }
pub const CASTLING_WH_K: u8 = 0b00000001; pub const CASTLING_WH_K: u8 = 0b00000001;
@ -138,15 +140,25 @@ impl Engine {
self.fullmove = fullmove.parse::<i32>().ok().unwrap(); 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. /// Start working on board, returning the best move found.
/// ///
/// Stop working after `movetime` ms, or go on forever if it's -1. /// 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. // Stupid engine! Return a random move.
let moves = rules::get_player_legal_moves(&self.board, self.color); let moves = rules::get_player_legal_moves(&self.board, self.color);
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let best_move = moves.iter().choose(&mut rng).unwrap(); let best_move = moves.iter().choose(&mut rng).and_then(|m| Some(*m));
*best_move // board::draw(&self.board);
thread::sleep(time::Duration::from_millis(movetime as u64));
best_move
} }
} }
@ -180,6 +192,9 @@ impl Engine {
uci::PositionArgs::Startpos => { uci::PositionArgs::Startpos => {
let fen = notation::parse_fen(notation::FEN_START).unwrap(); let fen = notation::parse_fen(notation::FEN_START).unwrap();
self.apply_fen(&fen); self.apply_fen(&fen);
},
uci::PositionArgs::Moves(moves) => {
self.apply_moves(&moves);
} }
} }
} }

View file

@ -50,7 +50,7 @@ fn cmd_cli(args: &ArgMatches) -> i32 {
} }
fn cmd_uci(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); uci::Uci::start(output);
0 0
} }

View file

@ -1,5 +1,20 @@
//! Functions using various notations. //! 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"; pub const FEN_START: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
/// FEN notation for positions, split into fields. /// FEN notation for positions, split into fields.
@ -36,6 +51,17 @@ pub fn parse_fen_fields(fields: &[&str]) -> Option<Fen> {
mod tests { mod tests {
use super::*; 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] #[test]
fn test_parse_fen() { fn test_parse_fen() {
let fen_start = parse_fen(FEN_START).unwrap(); let fen_start = parse_fen(FEN_START).unwrap();

View file

@ -57,6 +57,7 @@ pub enum UciCmd {
pub enum PositionArgs { pub enum PositionArgs {
Startpos, Startpos,
Fen(notation::Fen), Fen(notation::Fen),
Moves(Vec<board::Move>),
} }
/// Arguments for the go remote commands. /// Arguments for the go remote commands.
@ -112,11 +113,9 @@ impl Uci {
} }
fn log(&mut self, s: String) { fn log(&mut self, s: String) {
match self.logfile.as_ref() { match &mut self.logfile {
Some(mut f) => { Some(f) => {
f.write_all(s.as_bytes()).unwrap(); writeln!(f, "{}", s).unwrap();
f.write_all("\n".as_bytes()).unwrap();
f.flush().unwrap();
} }
None => { None => {
eprintln!("{}", s); eprintln!("{}", s);
@ -135,6 +134,7 @@ impl Uci {
pub fn read_stdin(tx: mpsc::Sender<Cmd>) { pub fn read_stdin(tx: mpsc::Sender<Cmd>) {
let mut s = String::new(); let mut s = String::new();
loop { loop {
s.clear();
match io::stdin().read_line(&mut s) { match io::stdin().read_line(&mut s) {
Ok(_) => { Ok(_) => {
let s = s.trim(); let s = s.trim();
@ -147,7 +147,6 @@ impl Uci {
eprintln!("Failed to read input: {:?}", e); eprintln!("Failed to read input: {:?}", e);
} }
} }
s.clear();
} }
} }
@ -217,8 +216,12 @@ impl Uci {
} }
/// Send best move. /// Send best move.
fn send_bestmove(&mut self, m: &board::Move) { fn send_bestmove(&mut self, m: &Option<board::Move>) {
self.send(&format!("bestmove {:?}", m)); // TODO notation conversion 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. /// Parse an UCI "position" command.
fn parse_position_command(fields: &[&str]) -> UciCmd { 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!(); let mut subcommands = vec!();
match fields[0] { while i < num_fields {
// Subcommand "fen" is followed by a FEN string. match fields[i] {
"fen" => { // Subcommand "fen" is followed by a FEN string.
if let Some(fen) = notation::parse_fen_fields(&fields[1..7]) { "fen" => {
subcommands.push(PositionArgs::Fen(fen)) if let Some(fen) = notation::parse_fen_fields(&fields[i + 1 .. i + 7]) {
} else { subcommands.push(PositionArgs::Fen(fen))
return UciCmd::Unknown(format!("Bad format for position 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. i += 1;
"startpos" => subcommands.push(PositionArgs::Startpos),
f => return UciCmd::Unknown(format!("Unknown position subcommand: {}", f)),
} }
UciCmd::Position(subcommands) UciCmd::Position(subcommands)
} }