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)
}
/// 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();

View file

@ -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);
}
}
}

View file

@ -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
}

View file

@ -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();

View file

@ -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,21 +248,35 @@ 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] {
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[1..7]) {
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)),
}
i += 1;
}
UciCmd::Position(subcommands)
}