315 lines
9.7 KiB
Rust
315 lines
9.7 KiB
Rust
use crate::board;
|
|
use crate::board::GridAxis;
|
|
use crate::engine;
|
|
use std::io::BufRead;
|
|
|
|
impl std::fmt::Display for board::Color {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self {
|
|
board::Color::White => write!(f, "W"),
|
|
board::Color::Black => write!(f, "B"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for board::PieceType {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self {
|
|
board::PieceType::Pawn => write!(f, "P"),
|
|
board::PieceType::Knight => write!(f, "N"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for board::Piece {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
write!(f, "{}{}", self.piece_type, self.color)
|
|
}
|
|
}
|
|
|
|
trait GridAxisIO: std::fmt::Display
|
|
where
|
|
Self: GridAxis,
|
|
{
|
|
fn parse(c: char) -> Result<Self, ()>;
|
|
}
|
|
|
|
impl GridAxisIO for board::Rank {
|
|
fn parse(c: char) -> Result<Self, ()> {
|
|
match c {
|
|
'1' => Ok(board::Rank::_1),
|
|
'2' => Ok(board::Rank::_2),
|
|
'3' => Ok(board::Rank::_3),
|
|
'4' => Ok(board::Rank::_4),
|
|
'5' => Ok(board::Rank::_5),
|
|
'6' => Ok(board::Rank::_6),
|
|
'7' => Ok(board::Rank::_7),
|
|
'8' => Ok(board::Rank::_8),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for board::Rank {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
let c = match self {
|
|
board::Rank::_1 => '1',
|
|
board::Rank::_2 => '2',
|
|
board::Rank::_3 => '3',
|
|
board::Rank::_4 => '4',
|
|
board::Rank::_5 => '5',
|
|
board::Rank::_6 => '6',
|
|
board::Rank::_7 => '7',
|
|
board::Rank::_8 => '8',
|
|
};
|
|
write!(f, "{}", c)
|
|
}
|
|
}
|
|
|
|
impl GridAxisIO for board::File {
|
|
fn parse(c: char) -> Result<Self, ()> {
|
|
match c {
|
|
'a' => Ok(board::File::A),
|
|
'b' => Ok(board::File::B),
|
|
'c' => Ok(board::File::C),
|
|
'd' => Ok(board::File::D),
|
|
'e' => Ok(board::File::E),
|
|
'f' => Ok(board::File::F),
|
|
'g' => Ok(board::File::G),
|
|
'h' => Ok(board::File::H),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for board::File {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
let c = match self {
|
|
board::File::A => 'a',
|
|
board::File::B => 'b',
|
|
board::File::C => 'c',
|
|
board::File::D => 'd',
|
|
board::File::E => 'e',
|
|
board::File::F => 'f',
|
|
board::File::G => 'g',
|
|
board::File::H => 'h',
|
|
};
|
|
write!(f, "{}", c)
|
|
}
|
|
}
|
|
|
|
impl board::Position {
|
|
fn parse(s: &str) -> Result<Self, ()> {
|
|
let mut chars = s.chars();
|
|
let file = board::File::parse(chars.next().ok_or(())?)?;
|
|
let rank = board::Rank::parse(chars.next().ok_or(())?)?;
|
|
if chars.next().is_none() {
|
|
Ok(board::Position::new(file, rank))
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl board::PieceType {
|
|
fn parse(c: char) -> Result<Self, ()> {
|
|
match c {
|
|
'P' => Ok(board::PieceType::Pawn),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl board::Color {
|
|
fn parse(c: char) -> Result<Self, ()> {
|
|
match c {
|
|
'B' => Ok(board::Color::Black),
|
|
'W' => Ok(board::Color::White),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl board::Piece {
|
|
fn parse(s: &str) -> Result<Self, ()> {
|
|
let mut chars = s.chars();
|
|
let piece_type = board::PieceType::parse(chars.next().ok_or(())?)?;
|
|
let color = board::Color::parse(chars.next().ok_or(())?)?;
|
|
if chars.next().is_none() {
|
|
Ok(board::Piece {
|
|
piece_type,
|
|
color,
|
|
})
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for board::Position {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
write!(f, "{}{}", self.file, self.rank)
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for board::Board {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
for (position, piece) in self.iter() {
|
|
write!(f, "{}{}", piece, position)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl board::Board {
|
|
fn parse(s: &str) -> Result<Self, ()> {
|
|
if s.chars().all(|c| char::is_ascii_alphanumeric(&c)) {
|
|
let mut board = board::Board::new();
|
|
for piece_pos in s.as_bytes().chunks(4) {
|
|
let position = board::Position::parse(
|
|
std::str::from_utf8(&piece_pos[2..]).unwrap(),
|
|
)?;
|
|
let piece = board::Piece::parse(
|
|
std::str::from_utf8(&piece_pos[..2]).unwrap(),
|
|
)?;
|
|
board.set_at(position, Some(piece));
|
|
}
|
|
Ok(board)
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Ui {
|
|
engine: engine::Engine,
|
|
}
|
|
|
|
impl Ui {
|
|
pub fn new() -> Self {
|
|
Ui {
|
|
engine: engine::Engine::new(),
|
|
}
|
|
}
|
|
fn get_state(&self, args: &[&str]) -> Result<String, String> {
|
|
if let [] = args {
|
|
let result = format!("{}", self.engine.get_state());
|
|
Ok(result)
|
|
} else {
|
|
Err("get_state takes no args".to_owned())
|
|
}
|
|
}
|
|
fn set_state(&mut self, args: &[&str]) -> Result<String, String> {
|
|
if let [state_str] = args {
|
|
self.engine.set_state(
|
|
board::Board::parse(state_str).map_err(|_| {
|
|
format!("Error parsing state {}", state_str)
|
|
})?,
|
|
);
|
|
let result = format!("{}", self.engine.get_state());
|
|
Ok(result)
|
|
} else {
|
|
Err("set_state takes 1 arg".to_owned())
|
|
}
|
|
}
|
|
fn get_moves(&self, args: &[&str]) -> Result<String, String> {
|
|
if let [position_str] = args {
|
|
let position =
|
|
board::Position::parse(position_str).map_err(|_| {
|
|
format!("Error parsing position {}", position_str)
|
|
})?;
|
|
match self.engine.get_legal_moves(&position) {
|
|
Err(_) => {
|
|
let error = format!("No moves possible from {}", position);
|
|
Err(error)
|
|
}
|
|
Ok(positions) => {
|
|
Ok(positions.map(|pos| format!("{}", pos)).collect())
|
|
}
|
|
}
|
|
} else {
|
|
Err("get_moves takes 1 arg".to_owned())
|
|
}
|
|
}
|
|
fn make_move(&mut self, args: &[&str]) -> Result<String, String> {
|
|
if let [source_str, target_str] = args {
|
|
let source = board::Position::parse(source_str)
|
|
.map_err(|_| format!("Error parsing source {}", source_str))?;
|
|
let target = board::Position::parse(target_str)
|
|
.map_err(|_| format!("Error parsing target {}", target_str))?;
|
|
match self.engine.make_move(&source, target) {
|
|
Err(()) => Err(format!(
|
|
"Move not possible {}-{}",
|
|
source_str, target_str
|
|
)),
|
|
Ok(()) => Ok(format!("{}", self.engine.get_state())),
|
|
}
|
|
} else {
|
|
Err("make_move takes 2 args".to_owned())
|
|
}
|
|
}
|
|
fn choose_move(&self, args: &[&str]) -> Result<String, String> {
|
|
if let [color_str] = args {
|
|
if let [color_char] = color_str.chars().collect::<Vec<char>>()[..]
|
|
{
|
|
match self.engine.choose_move(
|
|
&board::Color::parse(color_char).map_err(|_| {
|
|
format!("Couldn't parse color code {}", color_char)
|
|
})?,
|
|
) {
|
|
Some((source, target)) => {
|
|
Ok(format!("{},{}", source, target))
|
|
}
|
|
None => {
|
|
Err(format!("No move possible for {}", color_char))
|
|
}
|
|
}
|
|
} else {
|
|
Err(format!("Invalid color string {}", color_str))
|
|
}
|
|
} else {
|
|
Err("choose_move takes 1 arg".to_owned())
|
|
}
|
|
}
|
|
fn reset(&mut self, args: &[&str]) -> Result<String, String> {
|
|
if let [] = args {
|
|
self.engine.reset();
|
|
Ok(format!("{}", self.engine.get_state()))
|
|
} else {
|
|
Err("reset doesn't take args".to_owned())
|
|
}
|
|
}
|
|
fn handle_command(&mut self, s: &str) -> Result<String, String> {
|
|
let mut cmd = s.split(',');
|
|
// There will be at least an empty string => otherwise panic
|
|
let cmd_type = cmd.next().unwrap();
|
|
let args: Vec<&str> = cmd.collect();
|
|
match cmd_type {
|
|
"get_state" => self.get_state(&args),
|
|
"get_moves" => self.get_moves(&args),
|
|
"make_move" => self.make_move(&args),
|
|
"set_state" => self.set_state(&args),
|
|
"choose_move" => self.choose_move(&args),
|
|
"reset" => self.reset(&args),
|
|
"" => Err("No command given".to_owned()),
|
|
_ => Err("Invalid command".to_owned()),
|
|
}
|
|
}
|
|
pub fn run(&mut self) -> Result<(), ()> {
|
|
let stdin = std::io::stdin();
|
|
let mut input_lines = stdin.lock().lines();
|
|
loop {
|
|
match input_lines.next() {
|
|
None => break Ok(()),
|
|
Some(line) => match line {
|
|
Err(_) => break Err(()),
|
|
Ok(line) => match self.handle_command(&line) {
|
|
Ok(result) => println!("ok,{}", result),
|
|
Err(reason) => println!("err,{}", reason),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|