2 clients can play and see each other moves.

This commit is contained in:
2020-01-03 22:54:52 +01:00
parent ae632df144
commit 0cff0e13da
5 changed files with 297 additions and 119 deletions

View File

@ -1,4 +1,8 @@
use clichess::UserRole;
use shakmaty::fen::Fen;
use shakmaty::{Chess, Color, Setup};
use std::io;
use std::io::{BufRead, BufReader};
use std::os::unix::net::UnixStream;
fn main() {
@ -10,10 +14,58 @@ fn main() {
}
};
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);
let (client, chess) = get_connection_info_from_stream(&stream);
//First prompt when connecting to the server
println!(
"Hello, anonymous !\n\r You're playing with the {} pieces",
client.role.to_string()
);
//then we get the initial role of the connected client.
let mut current_position = chess;
loop {
println!(
"{}",
clichess::board_representation(&current_position, clichess::to_color(client.side))
);
if clichess::is_player_turn(client, current_position.turn()) {
//it's the user turn, taking user input
io::stdin().read_line(&mut buffer).unwrap();
println!("trying to play {}", buffer);
clichess::write_to_stream(&mut stream, String::from(buffer.trim()));
buffer.clear();
}
//update position after playing.
current_position = get_current_position(&stream);
//come back at the beginning of the loop to wait for incoming moves.
}
}
//wait for next position from server, then return the current board.
fn get_current_position(stream: &UnixStream) -> Chess {
let response = clichess::read_line_from_stream(&stream);
parse_position(&response)
}
fn parse_position(string: &str) -> Chess {
let setup: Fen = string.trim().parse().expect("Invalid message from server.");
let position: Chess = setup.position().expect("Invalid message from server.");
position
}
fn get_connection_info_from_stream(stream: &UnixStream) -> (clichess::Client, Chess) {
println!("Read lines from stream...");
let mut buf = String::new();
let mut reader = BufReader::new(stream);
//first, client as json.
reader
.read_line(&mut buf)
.expect("Server closed connection.");
let client = serde_json::from_str(&(buf.trim())).unwrap();
buf.clear();
reader
.read_line(&mut buf)
.expect("Server closed connection.");
let chess = parse_position(buf.trim());
(client, chess)
}

View File

@ -1,19 +1,25 @@
use clichess;
use shakmaty::{Chess, Color};
use clichess::Client;
use clichess::UserRole;
use shakmaty::{fen, Chess, Color, Setup};
use std::collections::HashMap;
use std::fs;
use std::io::prelude::*;
use std::os::unix::net::{UnixListener, UnixStream};
use std::str;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
#[derive(Clone)]
struct Server {
id: usize,
chess_position: Arc<Mutex<Chess>>,
clients: Arc<Mutex<HashMap<usize, (Client, Arc<(Mutex<String>, Condvar)>)>>>,
receiving_buffer: Arc<(Mutex<String>, Condvar)>,
}
fn main() {
let chess = Arc::new(Mutex::new(Chess::default()));
let clients = Arc::new(Mutex::new(HashMap::new()));
let mut counter = 0;
fs::remove_file("/tmp/clichess.socket");
@ -23,12 +29,13 @@ fn main() {
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let chess = Arc::clone(&chess);
/* connection succeeded */
let server = Server {
id: counter,
chess_position: chess,
chess_position: chess.clone(),
clients: clients.clone(),
receiving_buffer: Arc::new((Mutex::new(String::new()), Condvar::new())),
};
/* connection succeeded */
thread::spawn(|| handle_client(stream, server));
counter += 1;
}
@ -41,24 +48,81 @@ fn main() {
}
fn handle_client(mut stream: UnixStream, server: Server) {
//create client
let client = create_client(&server);
//send its information to the client
println!(
"server {}, send first information to the client...",
server.id
);
clichess::write_to_stream(&mut stream, serde_json::to_string(&client).unwrap());
println!("server {}, information to the client sent", server.id);
let mut player_turn: Color;
//send current position to the client
println!(
"server {}, send current position to the client...",
server.id
);
{
let chess = server.chess_position.lock().unwrap();
clichess::write_to_stream(
&mut stream,
clichess::board_representation(&chess, Color::White),
);
player_turn = chess.turn();
clichess::write_to_stream(&mut stream, fen::fen(&*chess));
}
//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),
);
println!("server {}, current position to the client sent", server.id);
loop {
if clichess::is_player_turn(client, player_turn) {
//let go of the lock while waiting for user input.
println!("server {}, waiting for client move..", server.id);
let input = clichess::read_line_from_stream(&stream);
{
let mut chess = server.chess_position.lock().unwrap();
let clients = server.clients.lock().unwrap();
println!("User tried to play {}", input);
match clichess::try_to_play_move(&chess, input) {
Ok(played_chess) => *chess = played_chess,
Err(e) => println!("Error: {}", e),
};
let chessfen = fen::fen(&*chess);
for (id, (_, cvar_buffer)) in clients.iter() {
if server.id != *id {
cvar_buffer.0.lock().unwrap().push_str(&chessfen);
cvar_buffer.1.notify_one();
}
}
clichess::write_to_stream(&mut stream, chessfen);
player_turn = chess.turn();
}
} else {
{
let (buffer_mutex, cvar) = &*server.receiving_buffer;
let mut buffer = buffer_mutex.lock().unwrap();
println!("server id: {}, waiting for move to send...", server.id);
while buffer.is_empty() {
buffer = cvar.wait(buffer).unwrap();
}
println!("server id: {}, sending {}", server.id, buffer);
clichess::write_to_stream(&mut stream, buffer.clone());
buffer.clear();
let chess = server.chess_position.lock().unwrap();
player_turn = chess.turn();
}
}
}
}
fn create_client(server: &Server) -> Client {
println!("Creating client {}...", server.id);
let role = match server.id {
0 => UserRole::White,
1 => UserRole::Black,
_ => UserRole::Spectator,
};
let mut clients = server.clients.lock().unwrap();
let client = Client {
role,
side: clichess::from_color(clichess::get_default_side(role)),
};
clients.insert(server.id, (client, server.receiving_buffer.clone()));
println!("Created client {}", server.id);
client
}

View File

@ -1,18 +1,48 @@
use colored::Colorize;
use shakmaty::fen;
use shakmaty::fen::Fen;
use shakmaty::fen::ParseFenError;
use serde::{Deserialize, Serialize};
use shakmaty::san::ParseSanError;
use shakmaty::san::San;
use shakmaty::san::SanError;
use shakmaty::{Chess, Color, IllegalMoveError, Position, PositionError, Setup, Square};
use shakmaty::{Chess, Color, IllegalMoveError, Position, Setup, Square};
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader, ErrorKind, Read, Write};
use std::io::{BufReader, Write};
use std::os::unix::net::UnixStream;
use std::str;
#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct Client {
pub role: UserRole,
pub side: BoardSide,
}
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum BoardSide {
White,
Black,
}
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum UserRole {
White,
Black,
Spectator,
}
impl fmt::Display for UserRole {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
UserRole::White => "white",
UserRole::Black => "black",
UserRole::Spectator => "spectator",
}
)
}
}
struct SquareToPrint {
color: String,
background_color: String,
@ -56,76 +86,6 @@ impl fmt::Display for MoveInputError {
}
}
#[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;
@ -224,23 +184,43 @@ fn piece_char_to_utf8(piece: char) -> char {
}
}
const DELIMITER_CHAR: u8 = 4;
pub fn read_from_stream(stream: &UnixStream) -> String {
let mut result = Vec::new();
pub fn read_line_from_stream(stream: &UnixStream) -> String {
let mut result = String::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()
}
reader.read_line(&mut result).unwrap();
String::from(result.trim_end())
}
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();
stream.write_all(&(msg + "\n").as_bytes()).unwrap();
}
pub fn get_default_side(role: UserRole) -> Color {
if role == UserRole::Black {
Color::Black
} else {
Color::White
}
}
pub fn to_color(board_side: BoardSide) -> Color {
if board_side == BoardSide::White {
Color::White
} else {
Color::Black
}
}
pub fn from_color(color: Color) -> BoardSide {
if color == Color::White {
BoardSide::White
} else {
BoardSide::Black
}
}
pub fn is_player_turn(client: Client, playing_side: Color) -> bool {
(playing_side == Color::White && client.role == UserRole::White)
|| (playing_side == Color::Black && client.role == UserRole::Black)
}