Server and client communicates through unix socket
This commit is contained in:
parent
fcc5d5727a
commit
ae632df144
19
src/bin/client.rs
Normal file
19
src/bin/client.rs
Normal 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
64
src/bin/server.rs
Normal 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),
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
257
src/lib.rs
257
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<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();
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
use clichess;
|
||||
|
||||
fn main() {
|
||||
clichess::play();
|
||||
}
|
Loading…
Reference in New Issue
Block a user