diff --git a/server/src/main.rs b/server/src/main.rs index 2ceaaa6..17c9b5d 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -66,7 +66,7 @@ enum RoomEvent { PlayerJoined(Player), PlayerLeft(Player), AIJoined(Difficulty), - //AILeft(Difficulty), + AILeft { index: usize }, } #[derive(Clone, Serialize, Debug)] @@ -74,6 +74,7 @@ enum RoomEvent { enum ServerToClientMessage { RoomChange { event: RoomEvent, info: PartyInfo }, GameEvent { state: ApiState }, + Invalid { reason: String }, } #[derive(Deserialize, Debug)] @@ -84,6 +85,7 @@ enum ClientToServerMessage { StartGame, GameMove, AddAI { difficulty: Difficulty }, + RemoveAI { index: usize }, } #[derive(Clone, Debug)] @@ -99,6 +101,7 @@ async fn incoming_message_handler( sender: &Sender, player: &Player, room: &Arc>, + stream: &mut DuplexStream, ) -> bool { match message { None => { @@ -184,6 +187,23 @@ async fn incoming_message_handler( }; sender.send(InnerRoomMessage::PassThrough(event)).unwrap(); } + ClientToServerMessage::RemoveAI { index } => { + let mut room = room.write().await; + if index < room.party_info.ais.len() { + room.party_info.ais.remove(index); + let event = ServerToClientMessage::RoomChange { + event: RoomEvent::AILeft { index }, + info: room.party_info.clone(), + }; + sender.send(InnerRoomMessage::PassThrough(event)).unwrap(); + } else { + let event = ServerToClientMessage::Invalid { + reason: format!("{index} is out of bounds"), + }; + let event = serde_json::to_string(&event).unwrap(); + let _ = stream.send(event.into()).await; + } + } } } Err(e) => { @@ -204,6 +224,7 @@ async fn outgoing_message_handler( stream: &mut DuplexStream, ) -> bool { let message = message.unwrap(); + println!("Inner room message - {:#?}", message); let message = match message { InnerRoomMessage::PassThrough(event) => serde_json::to_string(&event).unwrap(), InnerRoomMessage::GameEvent => { @@ -290,7 +311,7 @@ async fn chat( // Rust formatter can't reach into this macro, hence we broke out the logic // into sub-functions message = incoming_message => { - if incoming_message_handler(message, &sender, &player, &room).await { + if incoming_message_handler(message, &sender, &player, &room, &mut stream).await { return Ok(()) } }, @@ -304,56 +325,6 @@ async fn chat( } }) }) - - // - // ws.channel(move |mut stream| Box::pin(async move { - // let mut interval = interval(Duration::from_secs(10)); - // while !stream.is_terminated(){ // always seems to return true? - // let ws_incoming = stream.next(); - // let other_incoming = receiver.recv(); - // let ping_tick = interval.tick(); - // - // // pin_mut!(ws_incoming, other_incoming); // no clue what this does - // - // select! { - // message = ws_incoming => { - // if message.is_none() { - // println!("Websocket closed"); - // return Ok(()) - // } - // - // println!("websocket received a websocket message"); - // let message = message.unwrap()?; - // - // if let ws::Message::Close(close_frame) = &message { - // println!("Received close message"); - // println!("{close_frame:?}") - // } else if let ws::Message::Text(text) = &message { - // println!("Received text {text:?}"); - // sender.send(text.to_string()).unwrap(); - // } else { - // println!("Received non-text message: {message:?}") - // } - // }, - // message = other_incoming => { - // let message = message.unwrap(); - // println!("Sending message \"{message}\" via websocket"); - // - // - // - // let _ = stream.send(message.into()).await; // always seems to return Ok(()), even after a disconnection - // //println!("Message sent: {blat:?}"); - // } - // _ = ping_tick => { - // println!("ping_tick"); - // let message = Message::Ping(Vec::new()); - // let _ = stream.send(message.into()).await; - // } - // } - // } - // Ok(()) - // - // })) } #[launch] diff --git a/ui/src/multiplayer.tsx b/ui/src/multiplayer.tsx index fb65bb9..523bfaa 100644 --- a/ui/src/multiplayer.tsx +++ b/ui/src/multiplayer.tsx @@ -18,6 +18,16 @@ interface PartyInfo { players: Player[] } +const LOGBASE = 10000; +function unprocessAIRandomness(processedAIRandomness: number): string { + const x = 100*(LOGBASE**processedAIRandomness -1) / (LOGBASE-1) + return x.toFixed(0) +} + +function unprocessedAIProportion(processedProportion: number): string { + return (100 * (1-processedProportion)).toFixed(0); +} + export function Menu(): React.JSX.Element { const [roomName, setRoomName] = useState(""); @@ -31,8 +41,7 @@ export function Menu(): React.JSX.Element { // Can change log scale to control shape of curve using following equation: // aiRandomness = log(1 + x*(n-1))/log(n) when x, the user input, ranges between 0 and 1 - const logBase: number = 10000; - const processedAIRandomness = Math.log(1 + (logBase - 1)*aiRandomness/100) / Math.log(logBase); + const processedAIRandomness = Math.log(1 + (LOGBASE - 1)*aiRandomness/100) / Math.log(LOGBASE); const processedProportionDictionary = 1.0 - proportionDictionary / 100; if(socket != null && partyInfo == null) { @@ -42,8 +51,19 @@ export function Menu(): React.JSX.Element { return
  • {x.name}
  • ; }); const ais = partyInfo.ais.map((x, i) => { - return
  • Proportion: {x.proportion} / Randomness: {x.randomness}
  • - }) + return
  • + Proportion: {unprocessedAIProportion(x.proportion)} / Randomness: {unprocessAIRandomness(x.randomness)} + +
  • + }); return

    Connected to {roomName}

    diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index bfdc766..0faa7dd 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -26,19 +26,19 @@ impl WasmAPI { pub fn exchange(&mut self, selection: JsValue) -> JsValue { let selection: Vec = serde_wasm_bindgen::from_value(selection).unwrap(); - let result = self.0.exchange("Player".to_string(), selection); + let result = self.0.exchange("Player", selection); serde_wasm_bindgen::to_value(&result).unwrap() } pub fn pass(&mut self) -> JsValue { - let result = self.0.pass("Player".to_string()); + let result = self.0.pass("Player"); serde_wasm_bindgen::to_value(&result).unwrap() } pub fn load(&mut self) -> JsValue { - let result = self.0.load("Player".to_string()); + let result = self.0.load("Player"); serde_wasm_bindgen::to_value(&result).unwrap() } diff --git a/wordgrid/src/api.rs b/wordgrid/src/api.rs index 12966e7..efb62dd 100644 --- a/wordgrid/src/api.rs +++ b/wordgrid/src/api.rs @@ -128,13 +128,13 @@ impl APIGame { } } - pub fn exchange(&mut self, player: String, tray_tiles: Vec) -> Result { - self.player_exists_and_turn(&player)?; + pub fn exchange(&mut self, player: &str, tray_tiles: Vec) -> Result { + self.player_exists_and_turn(player)?; let (tray, turn_action, game_state) = self.0.exchange_tiles(tray_tiles)?; let update = Update { r#type: turn_action, - player, + player: player.to_string(), }; self.1.push(update.clone()); @@ -142,14 +142,14 @@ impl APIGame { Ok(self.build_result(tray, Some(game_state), Some(update))) } - pub fn pass(&mut self, player: String) -> Result { - self.player_exists_and_turn(&player)?; + pub fn pass(&mut self, player: &str) -> Result { + self.player_exists_and_turn(player)?; let game_state = self.0.pass()?; - let tray = self.0.player_states.get_tray(&player).unwrap().clone(); + let tray = self.0.player_states.get_tray(player).unwrap().clone(); let update = Update { r#type: TurnAction::Pass, - player, + player: player.to_string(), }; self.1.push(update.clone()); diff --git a/wordgrid/src/game.rs b/wordgrid/src/game.rs index 59f848c..2da2277 100644 --- a/wordgrid/src/game.rs +++ b/wordgrid/src/game.rs @@ -8,7 +8,7 @@ use crate::player_interaction::ai::{Difficulty, AI}; use crate::player_interaction::Tray; use rand::prelude::SliceRandom; use rand::rngs::SmallRng; -use rand::{Rng, SeedableRng}; +use rand::SeedableRng; use serde::{Deserialize, Serialize, Serializer}; pub enum Player { @@ -204,7 +204,6 @@ pub struct Game { } impl Game { - pub fn new_specific( mut rng: SmallRng, dictionary: DictionaryImpl,