Server and client communicates through unix socket

This commit is contained in:
Artlef 2019-12-17 01:09:17 +01:00
parent fcc5d5727a
commit ae632df144
5 changed files with 326 additions and 241 deletions

19
src/bin/client.rs Normal file
View File

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

64
src/bin/server.rs Normal file
View File

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

View File

@ -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<SanError> for MoveInputError {
fn from(error: SanError) -> Self {
MoveInputError::SanSanError(error)
}
}
impl From<ParseSanError> for MoveInputError {
fn from(error: ParseSanError) -> Self {
MoveInputError::SanParseSanError(error)
}
}
impl From<IllegalMoveError> 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<ParseFenError> for ReadingFileError {
fn from(error: ParseFenError) -> Self {
ReadingFileError::ReadParseFenError(error)
}
}
impl From<io::Error> for ReadingFileError {
fn from(error: io::Error) -> Self {
ReadingFileError::ReadFileError(error)
}
}
impl From<PositionError> 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<Chess, ReadingFileError> {
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<Chess, ReadingFileError> {
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<Chess, ReadingFileError> {
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<Vec<SquareToPrint>> {
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<Chess, MoveInputError> {
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,
}
}

View File

@ -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<SanError> for MoveInputError {
fn from(error: SanError) -> Self {
MoveInputError::SanSanError(error)
}
}
impl From<ParseSanError> for MoveInputError {
fn from(error: ParseSanError) -> Self {
MoveInputError::SanParseSanError(error)
}
}
impl From<IllegalMoveError> 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<ParseFenError> for ReadingFileError {
fn from(error: ParseFenError) -> Self {
ReadingFileError::ReadParseFenError(error)
}
}
impl From<io::Error> for ReadingFileError {
fn from(error: io::Error) -> Self {
ReadingFileError::ReadFileError(error)
}
}
impl From<PositionError> 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<Chess, ReadingFileError> {
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<Chess, ReadingFileError> {
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<Chess, ReadingFileError> {
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<Vec<SquareToPrint>> {
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<Chess, MoveInputError> {
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();
}

View File

@ -1,5 +0,0 @@
use clichess;
fn main() {
clichess::play();
}