use engine::gameend::GameEnd; use engine::{boardsquare::BoardSquare, chessmove::ChessMove, piecetype::PieceType}; use futures_util::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; use std::io::{self, Write}; use tokio_tungstenite::{connect_async, tungstenite::Message}; use url::Url; use uuid::Uuid; #[derive(Serialize, Deserialize, Debug)] struct Step { from: String, to: String, } #[derive(Serialize, Deserialize)] #[serde(tag = "type")] enum ClientMessage { Join { username: String }, FindMatch, Move { step: ChessMove, fen: String }, Resign, Chat { text: String }, RequestLegalMoves { fen: String }, } #[derive(Serialize, Deserialize, Debug)] struct ServerMessage { #[serde(rename = "type")] message_type: String, player_id: Option, match_id: Option, opponent: Option, color: Option, reason: Option, } #[derive(Serialize, Deserialize)] pub enum ServerMessage2 { GameEnd { winner: GameEnd, }, UIUpdate { fen: String, }, MatchFound { match_id: Uuid, color: String, opponent_name: String, }, } #[tokio::main] async fn main() -> Result<(), Box> { println!("Knightly Chess Client"); println!("========================"); // Get server address from user print!("Enter server address [ws://127.0.0.1:9001]: "); io::stdout().flush()?; let mut server_addr = String::new(); io::stdin().read_line(&mut server_addr)?; let server_addr = server_addr.trim(); let server_addr = if server_addr.is_empty() { "ws://127.0.0.1:9001".to_string() } else { server_addr.to_string() }; // Connect to server println!("Connecting to {}...", server_addr); let url = Url::parse(&server_addr)?; let (ws_stream, _) = connect_async(url).await?; println!("Connected to server!"); let (mut write, mut read) = ws_stream.split(); // Spawn a task to handle incoming messages let read_handle = tokio::spawn(async move { while let Some(message) = read.next().await { match message { Ok(msg) => { if msg.is_text() { let text = msg.to_text().unwrap(); println!("\nServer: {}", text); // Try to parse as structured message if let Ok(parsed) = serde_json::from_str::(text) { match parsed { ServerMessage2::MatchFound { match_id, color, opponent_name, } => { println!( "opponent: {}, match_id: {}, color: {}", opponent_name, match_id, color ); } _ => { println!("cucc"); } } } } } Err(e) => { eprintln!("Error receiving message: {}", e); break; } } } }); // Main loop for sending messages println!("\nAvailable commands:"); println!(" join - Join the server"); println!(" findmatch - Find a match"); println!(" move - Make a move (e.g., move e2 e4)"); println!(" chat - Send chat message"); println!(" resign - Resign from current game"); println!(" quit - Exit client"); println!(); loop { print!("āž”ļø Enter command: "); io::stdout().flush()?; let mut input = String::new(); io::stdin().read_line(&mut input)?; let input = input.trim(); if input.is_empty() { continue; } let parts: Vec<&str> = input.split_whitespace().collect(); let command = parts[0].to_lowercase(); match command.as_str() { "quit" | "exit" => { println!("šŸ‘‹ Goodbye!"); break; } "join" => { if parts.len() >= 2 { let username = parts[1..].join(" "); let message = ClientMessage::Join { username }; send_message(&mut write, &message).await?; } else { println!("Usage: join "); } } "findmatch" | "find" => { let message = ClientMessage::FindMatch; send_message(&mut write, &message).await?; println!("šŸ” Searching for a match..."); } "move" => { if parts.len() >= 3 { //let from = parts[1].to_string(); //let to = parts[2].to_string(); let fen = parts[1].to_string(); let step = ChessMove::quiet( engine::piecetype::PieceType::WhiteBishop, BoardSquare::new(), BoardSquare { x: 1, y: 1 }, None, ); let message = ClientMessage::Move { step, fen }; send_message(&mut write, &message).await?; //println!("ā™Ÿļø Sent move: {} -> {}", parts[1], parts[2]); } else { println!("Usage: move (e.g., move e2 e4)"); } } "chat" => { if parts.len() >= 2 { let text = parts[1..].join(" "); let message = ClientMessage::Chat { text }; send_message(&mut write, &message).await?; } else { println!("Usage: chat "); } } "resign" => { let message = ClientMessage::Resign; send_message(&mut write, &message).await?; println!("Resigned from current game"); } "help" => { print_help(); } "requestmoves" => { if parts.len() >= 2 { let fen = parts[1..].join(" "); let message = ClientMessage::RequestLegalMoves { fen }; send_message(&mut write, &message).await?; } } _ => { println!( "Unknown command: {}. Type 'help' for available commands.", command ); } } } // Cleanup read_handle.abort(); Ok(()) } async fn send_message( write: &mut futures_util::stream::SplitSink< tokio_tungstenite::WebSocketStream< tokio_tungstenite::MaybeTlsStream, >, Message, >, message: &ClientMessage, ) -> Result<(), Box> { let json = serde_json::to_string(message)?; write.send(Message::Text(json)).await?; Ok(()) } fn print_help() { println!("\nšŸ“– Available Commands:"); println!(" join - Register with a username"); println!(" findmatch - Enter matchmaking queue"); println!(" move - Make a chess move"); println!(" chat - Send chat to opponent"); println!(" resign - Resign from current game"); println!(" help - Show this help"); println!(" quit - Exit the client"); println!(" requestmoves - Request the legal moves"); println!(); }