From ae632df1448b59f6597453d15cb3b73d2c0d5423 Mon Sep 17 00:00:00 2001 From: Artlef Date: Tue, 17 Dec 2019 01:09:17 +0100 Subject: [PATCH] Server and client communicates through unix socket --- src/bin/client.rs | 19 ++++ src/bin/server.rs | 64 ++++++++++++ src/chessboard.rs | 222 --------------------------------------- src/lib.rs | 257 +++++++++++++++++++++++++++++++++++++++++++--- src/main.rs | 5 - 5 files changed, 326 insertions(+), 241 deletions(-) create mode 100644 src/bin/client.rs create mode 100644 src/bin/server.rs delete mode 100644 src/chessboard.rs delete mode 100644 src/main.rs diff --git a/src/bin/client.rs b/src/bin/client.rs new file mode 100644 index 0000000..2c0364e --- /dev/null +++ b/src/bin/client.rs @@ -0,0 +1,19 @@ +use std::io; +use std::os::unix::net::UnixStream; + +fn main() { + let mut stream = match UnixStream::connect("/tmp/clichess.socket") { + Ok(sock) => sock, + Err(_) => { + println!("clichess daemon is not running."); + return; + } + }; + let mut buffer = String::new(); + let input = clichess::read_from_stream(&stream); + println!("{}", input); + io::stdin().read_line(&mut buffer).unwrap(); + clichess::write_to_stream(&mut stream, String::from(buffer.trim())); + let response = clichess::read_from_stream(&stream); + println!("{}", response); +} diff --git a/src/bin/server.rs b/src/bin/server.rs new file mode 100644 index 0000000..2733af8 --- /dev/null +++ b/src/bin/server.rs @@ -0,0 +1,64 @@ +use clichess; +use shakmaty::{Chess, Color}; +use std::fs; +use std::io::prelude::*; +use std::os::unix::net::{UnixListener, UnixStream}; +use std::str; +use std::sync::{Arc, Mutex}; +use std::thread; + +struct Server { + id: usize, + chess_position: Arc>, +} + +fn main() { + let chess = Arc::new(Mutex::new(Chess::default())); + let mut counter = 0; + fs::remove_file("/tmp/clichess.socket"); + + let listener = UnixListener::bind("/tmp/clichess.socket").unwrap(); + + // accept connections and process them, spawning a new thread for each one + for stream in listener.incoming() { + match stream { + Ok(stream) => { + let chess = Arc::clone(&chess); + /* connection succeeded */ + let server = Server { + id: counter, + chess_position: chess, + }; + thread::spawn(|| handle_client(stream, server)); + counter += 1; + } + Err(err) => { + println!("Could not create a connection: {}", err); + break; + } + } + } +} + +fn handle_client(mut stream: UnixStream, server: Server) { + { + let chess = server.chess_position.lock().unwrap(); + clichess::write_to_stream( + &mut stream, + clichess::board_representation(&chess, Color::White), + ); + } + //let go of the lock while waiting for user input. + let input = clichess::read_from_stream(&stream); + { + let mut chess = server.chess_position.lock().unwrap(); + match clichess::try_to_play_move(&chess, input) { + Ok(played_chess) => *chess = played_chess, + Err(e) => println!("Error: {}", e), + }; + clichess::write_to_stream( + &mut stream, + clichess::board_representation(&chess, Color::White), + ); + } +} diff --git a/src/chessboard.rs b/src/chessboard.rs deleted file mode 100644 index f782d59..0000000 --- a/src/chessboard.rs +++ /dev/null @@ -1,222 +0,0 @@ -use colored::Colorize; -use shakmaty::fen; -use shakmaty::fen::Fen; -use shakmaty::fen::ParseFenError; -use shakmaty::san::ParseSanError; -use shakmaty::san::San; -use shakmaty::san::SanError; -use shakmaty::{Chess, Color, IllegalMoveError, Position, PositionError, Setup, Square}; -use std::fmt; -use std::fs::File; -use std::io; -use std::io::{ErrorKind, Read, Write}; - -struct SquareToPrint { - color: String, - background_color: String, - square_representation: String, -} - -#[derive(Debug)] -pub enum MoveInputError { - SanSanError(SanError), - SanParseSanError(ParseSanError), - ShakmatyIllegalMoveError(IllegalMoveError), -} - -impl From for MoveInputError { - fn from(error: SanError) -> Self { - MoveInputError::SanSanError(error) - } -} - -impl From for MoveInputError { - fn from(error: ParseSanError) -> Self { - MoveInputError::SanParseSanError(error) - } -} - -impl From for MoveInputError { - fn from(error: IllegalMoveError) -> Self { - MoveInputError::ShakmatyIllegalMoveError(error) - } -} - -impl fmt::Display for MoveInputError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - MoveInputError::SanSanError(san_error) => san_error.fmt(f), - MoveInputError::SanParseSanError(parse_san_error) => parse_san_error.fmt(f), - MoveInputError::ShakmatyIllegalMoveError(illegal_move_error) => { - illegal_move_error.fmt(f) - } - } - } -} - -#[derive(Debug)] -pub enum ReadingFileError { - ReadParseFenError(ParseFenError), - ReadFileError(io::Error), - ReadPositionError(PositionError), -} - -impl From for ReadingFileError { - fn from(error: ParseFenError) -> Self { - ReadingFileError::ReadParseFenError(error) - } -} - -impl From for ReadingFileError { - fn from(error: io::Error) -> Self { - ReadingFileError::ReadFileError(error) - } -} - -impl From for ReadingFileError { - fn from(error: PositionError) -> Self { - ReadingFileError::ReadPositionError(error) - } -} - -impl fmt::Display for ReadingFileError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ReadingFileError::ReadParseFenError(error) => error.fmt(f), - ReadingFileError::ReadFileError(error) => error.fmt(f), - ReadingFileError::ReadPositionError(error) => error.fmt(f), - } - } -} - -pub fn retrieve_chess_from_file_or_create(filename: &str) -> Result { - match retrieve_chess_from_file(filename) { - Ok(chess) => Ok(chess), - Err(ReadingFileError::ReadFileError(error)) => { - if error.kind() == ErrorKind::NotFound { - create_default_fen_in_file(filename) - } else { - Err(ReadingFileError::ReadFileError(error)) - } - } - other_error => other_error, - } -} - -pub fn save_chess_position(chess: &Chess, filename: &str) -> std::io::Result<()> { - let mut file = File::create(filename)?; - file.write_all(fen::fen(chess).as_bytes())?; - Ok(()) -} - -fn retrieve_chess_from_file(filename: &str) -> Result { - let mut file = File::open(filename)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - let setup: Fen = contents.trim().parse()?; - let position: Chess = setup.position()?; - Ok(position) -} - -fn create_default_fen_in_file(filename: &str) -> Result { - let chess = Chess::default(); - save_chess_position(&Chess::default(), filename)?; - Ok(chess) -} - -pub fn board_representation(chess: &Chess, side: Color) -> String { - let mut s = String::from(""); - let mut rank_numbers; - if side == Color::White { - rank_numbers = 8; - } else { - rank_numbers = 1; - } - for v in board_string_representation(chess, side) { - for square_to_print in v { - s.push_str(&format!( - "{}", - square_to_print - .square_representation - .color(square_to_print.color) - .on_color(square_to_print.background_color) - )); - } - s.push_str(&format!("{}\n\r", rank_numbers)); - if side == Color::White { - rank_numbers -= 1; - } else { - rank_numbers += 1; - } - } - let mut files = vec!["a", "b", "c", "d", "e", "f", "g", "h"]; - if side == Color::Black { - files.reverse(); - } - for file in files { - s.push_str(&format!("{} ", file)); - } - s -} - -fn board_string_representation(chess: &Chess, side: Color) -> Vec> { - let mut full_board_to_print = Vec::new(); - for _ in 0..8 { - full_board_to_print.push(Vec::new()) - } - for i in 0..8 { - for j in 0..8 { - let square = Square::new(i + 8 * j); - let square_to_print = SquareToPrint { - color: "black".to_string(), - background_color: if square.is_light() { - "white".to_string() - } else { - "green".to_string() - }, - square_representation: get_square_representation(&square, chess), - }; - full_board_to_print[j as usize].push(square_to_print); - } - } - if side == Color::White { - full_board_to_print.reverse(); - } else { - for j in 0..8 { - full_board_to_print[j as usize].reverse(); - } - } - full_board_to_print -} - -pub fn try_to_play_move(chess: &Chess, movestr: String) -> Result { - let san: San = movestr.parse()?; - let mv = san.to_move(chess)?; - Ok((*chess).clone().play(&mv)?) -} - -fn get_square_representation(square: &Square, chess: &Chess) -> String { - let board = (*chess).board(); - match board.piece_at(*square) { - Some(piece) => format!("{} ", piece_char_to_utf8(piece.char())), - None => " ".to_string(), - } -} - -fn piece_char_to_utf8(piece: char) -> char { - match piece { - 'P' => '♙', - 'p' => '♟', - 'N' => '♘', - 'n' => '♞', - 'B' => '♗', - 'b' => '♝', - 'R' => '♖', - 'r' => '♜', - 'Q' => '♕', - 'q' => '♛', - 'K' => '♔', - 'k' => '♚', - c => c, - } -} diff --git a/src/lib.rs b/src/lib.rs index 3d0616e..5a04ec1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,246 @@ -use std::io; -pub mod chessboard; -use shakmaty::{Chess, Color}; +use colored::Colorize; +use shakmaty::fen; +use shakmaty::fen::Fen; +use shakmaty::fen::ParseFenError; +use shakmaty::san::ParseSanError; +use shakmaty::san::San; +use shakmaty::san::SanError; +use shakmaty::{Chess, Color, IllegalMoveError, Position, PositionError, Setup, Square}; +use std::fmt; +use std::fs::File; +use std::io::prelude::*; +use std::io::{self, BufReader, ErrorKind, Read, Write}; +use std::os::unix::net::UnixStream; +use std::str; -pub fn play() { - let mut chess = Chess::default(); - loop { - println!("{}", chessboard::board_representation(&chess, Color::White)); - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); - print!("try to play: {}", input); - match chessboard::try_to_play_move(&chess, String::from(input.trim())) { - Ok(played_chess) => chess = played_chess, - Err(e) => println!("Error: {}", e), - }; +struct SquareToPrint { + color: String, + background_color: String, + square_representation: String, +} + +#[derive(Debug)] +pub enum MoveInputError { + SanSanError(SanError), + SanParseSanError(ParseSanError), + ShakmatyIllegalMoveError(IllegalMoveError), +} + +impl From for MoveInputError { + fn from(error: SanError) -> Self { + MoveInputError::SanSanError(error) } } + +impl From for MoveInputError { + fn from(error: ParseSanError) -> Self { + MoveInputError::SanParseSanError(error) + } +} + +impl From for MoveInputError { + fn from(error: IllegalMoveError) -> Self { + MoveInputError::ShakmatyIllegalMoveError(error) + } +} + +impl fmt::Display for MoveInputError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MoveInputError::SanSanError(san_error) => san_error.fmt(f), + MoveInputError::SanParseSanError(parse_san_error) => parse_san_error.fmt(f), + MoveInputError::ShakmatyIllegalMoveError(illegal_move_error) => { + illegal_move_error.fmt(f) + } + } + } +} + +#[derive(Debug)] +pub enum ReadingFileError { + ReadParseFenError(ParseFenError), + ReadFileError(io::Error), + ReadPositionError(PositionError), +} + +impl From for ReadingFileError { + fn from(error: ParseFenError) -> Self { + ReadingFileError::ReadParseFenError(error) + } +} + +impl From for ReadingFileError { + fn from(error: io::Error) -> Self { + ReadingFileError::ReadFileError(error) + } +} + +impl From for ReadingFileError { + fn from(error: PositionError) -> Self { + ReadingFileError::ReadPositionError(error) + } +} + +impl fmt::Display for ReadingFileError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ReadingFileError::ReadParseFenError(error) => error.fmt(f), + ReadingFileError::ReadFileError(error) => error.fmt(f), + ReadingFileError::ReadPositionError(error) => error.fmt(f), + } + } +} + +pub fn retrieve_chess_from_file_or_create(filename: &str) -> Result { + match retrieve_chess_from_file(filename) { + Ok(chess) => Ok(chess), + Err(ReadingFileError::ReadFileError(error)) => { + if error.kind() == ErrorKind::NotFound { + create_default_fen_in_file(filename) + } else { + Err(ReadingFileError::ReadFileError(error)) + } + } + other_error => other_error, + } +} + +pub fn save_chess_position(chess: &Chess, filename: &str) -> std::io::Result<()> { + let mut file = File::create(filename)?; + file.write_all(fen::fen(chess).as_bytes())?; + Ok(()) +} + +fn retrieve_chess_from_file(filename: &str) -> Result { + let mut file = File::open(filename)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + let setup: Fen = contents.trim().parse()?; + let position: Chess = setup.position()?; + Ok(position) +} + +fn create_default_fen_in_file(filename: &str) -> Result { + let chess = Chess::default(); + save_chess_position(&Chess::default(), filename)?; + Ok(chess) +} + +pub fn board_representation(chess: &Chess, side: Color) -> String { + let mut s = String::from(""); + let mut rank_numbers; + if side == Color::White { + rank_numbers = 8; + } else { + rank_numbers = 1; + } + for v in board_string_representation(chess, side) { + for square_to_print in v { + s.push_str(&format!( + "{}", + square_to_print + .square_representation + .color(square_to_print.color) + .on_color(square_to_print.background_color) + )); + } + s.push_str(&format!("{}\n\r", rank_numbers)); + if side == Color::White { + rank_numbers -= 1; + } else { + rank_numbers += 1; + } + } + let mut files = vec!["a", "b", "c", "d", "e", "f", "g", "h"]; + if side == Color::Black { + files.reverse(); + } + for file in files { + s.push_str(&format!("{} ", file)); + } + s.push_str("\n\r"); + s +} + +fn board_string_representation(chess: &Chess, side: Color) -> Vec> { + let mut full_board_to_print = Vec::new(); + for _ in 0..8 { + full_board_to_print.push(Vec::new()) + } + for i in 0..8 { + for j in 0..8 { + let square = Square::new(i + 8 * j); + let square_to_print = SquareToPrint { + color: "black".to_string(), + background_color: if square.is_light() { + "white".to_string() + } else { + "green".to_string() + }, + square_representation: get_square_representation(&square, chess), + }; + full_board_to_print[j as usize].push(square_to_print); + } + } + if side == Color::White { + full_board_to_print.reverse(); + } else { + for j in 0..8 { + full_board_to_print[j as usize].reverse(); + } + } + full_board_to_print +} + +pub fn try_to_play_move(chess: &Chess, movestr: String) -> Result { + let san: San = movestr.parse()?; + let mv = san.to_move(chess)?; + Ok((*chess).clone().play(&mv)?) +} + +fn get_square_representation(square: &Square, chess: &Chess) -> String { + let board = (*chess).board(); + match board.piece_at(*square) { + Some(piece) => format!("{} ", piece_char_to_utf8(piece.char())), + None => " ".to_string(), + } +} + +fn piece_char_to_utf8(piece: char) -> char { + match piece { + 'P' => '♙', + 'p' => '♟', + 'N' => '♘', + 'n' => '♞', + 'B' => '♗', + 'b' => '♝', + 'R' => '♖', + 'r' => '♜', + 'Q' => '♕', + 'q' => '♛', + 'K' => '♔', + 'k' => '♚', + c => c, + } +} + +const DELIMITER_CHAR: u8 = 4; + +pub fn read_from_stream(stream: &UnixStream) -> String { + let mut result = Vec::new(); + let mut reader = BufReader::new(stream); + + reader.read_until(DELIMITER_CHAR, &mut result).unwrap(); + if let Some((_, msg)) = result.split_last() { + String::from(str::from_utf8(msg).unwrap()) + } else { + String::new() + } +} + +pub fn write_to_stream(stream: &mut UnixStream, msg: String) { + let mut to_send = Vec::new(); + to_send.append(&mut msg.into_bytes()); + to_send.push(DELIMITER_CHAR); + stream.write_all(&to_send).unwrap(); +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 0fe36f4..0000000 --- a/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -use clichess; - -fn main() { - clichess::play(); -}