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; } impl GridAxisIO for board::Rank { fn parse(c: char) -> Result { 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 { 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 { 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 { match c { 'P' => Ok(board::PieceType::Pawn), _ => Err(()), } } } impl board::Color { fn parse(c: char) -> Result { match c { 'B' => Ok(board::Color::Black), 'W' => Ok(board::Color::White), _ => Err(()), } } } impl board::Piece { fn parse(s: &str) -> Result { 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 { 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 { 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 { 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 { 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 { 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 { if let [color_str] = args { if let [color_char] = color_str.chars().collect::>()[..] { 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 { 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 { 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), }, }, } } } }