]> git.seodisparate.com - EN605.607.81.SP22_ASDM_Project/commitdiff
Impl match players via phrase
authorStephen Seo <seo.disparate@gmail.com>
Wed, 27 Apr 2022 03:47:45 +0000 (12:47 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 27 Apr 2022 03:47:45 +0000 (12:47 +0900)
Front-end now has option to input phrase on game start.
Fixed back-end accepting empty strings (will treat empty strings as if
no phrase was given).

back_end/src/json_handlers.rs
front_end/index.html
front_end/src/state.rs
front_end/src/yew_components.rs

index 0f4c1df7d864646160e09cebb98488c5659b2243..b3c20b678c601e53f226a97bb90b9368737ad671 100644 (file)
@@ -41,7 +41,9 @@ fn handle_pairing_request(root: Value, tx: SyncSender<DBHandlerRequest>) -> Resu
     let mut phrase: Option<String> = None;
     if let Some(phrase_text) = root.get("phrase") {
         if let Some(phrase_str) = phrase_text.as_str() {
-            phrase = Some(phrase_str.to_owned());
+            if !phrase_str.is_empty() {
+                phrase = Some(phrase_str.to_owned());
+            }
         }
     }
     if tx
index eebcb7fd9b28c78f8c6e1c8cea2456a5c0df2e66..bf0034c883843b6079e1d02badbf40899065b6a4 100644 (file)
         grid-row: 2;
         grid-column: 3;
       }
-      button.menuMultiplayer {
+      div.multiplayerMenu {
         grid-row: 2;
         grid-column: 4;
+
+        display: grid;
+      }
+      button.networkedMultiplayer {
+        grid-row: 1;
+        grid-column: 1;
+      }
+      button.NMPhrase {
+        grid-row: 2;
+        grid-column: 1;
+      }
+      input.NMPhrase {
+        grid-row: 3;
+        grid-column: 1;
       }
       b.menuText {
         color: #FFF;
index ff5af2d14df0189ee86137d1f1694ba175b42c5b..794775f8bbabd43576613a9d08941503d080cbba 100644 (file)
@@ -12,12 +12,12 @@ use crate::game_logic::{check_win_draw, WinType};
 
 use serde::{Deserialize, Serialize};
 
-use std::cell::Cell;
+use std::cell::{Cell, RefCell};
 use std::collections::hash_set::HashSet;
 use std::fmt::Display;
 use std::rc::Rc;
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum GameState {
     MainMenu,
     SinglePlayer(Turn, AIDifficulty),
@@ -26,18 +26,20 @@ pub enum GameState {
         paired: bool,
         current_side: Option<Turn>,
         current_turn: Turn,
+        phrase: Option<String>,
     },
     PostGameResults(BoardState),
 }
 
 impl GameState {
-    pub fn is_networked_multiplayer(self) -> bool {
+    pub fn is_networked_multiplayer(&self) -> bool {
         matches!(
-            self,
+            *self,
             GameState::NetworkedMultiplayer {
                 paired: _,
                 current_side: _,
-                current_turn: _
+                current_turn: _,
+                phrase: _,
             }
         )
     }
@@ -47,6 +49,7 @@ impl GameState {
             ref mut paired,
             current_side: _,
             current_turn: _,
+            phrase: _,
         } = self
         {
             *paired = true;
@@ -58,6 +61,7 @@ impl GameState {
             paired: _,
             current_side,
             current_turn: _,
+            phrase: _,
         } = *self
         {
             current_side
@@ -71,6 +75,7 @@ impl GameState {
             paired: _,
             ref mut current_side,
             current_turn: _,
+            phrase: _,
         } = self
         {
             *current_side = side;
@@ -84,6 +89,7 @@ impl GameState {
             paired: _,
             current_side: _,
             current_turn,
+            phrase: _,
         } = *self
         {
             current_turn
@@ -97,6 +103,7 @@ impl GameState {
             paired: _,
             current_side,
             current_turn: _,
+            phrase: _,
         } = *self
         {
             current_side
@@ -110,11 +117,26 @@ impl GameState {
             paired: _,
             current_side: _,
             ref mut current_turn,
+            phrase: _,
         } = self
         {
             *current_turn = turn;
         }
     }
+
+    pub fn get_phrase(&self) -> Option<String> {
+        if let GameState::NetworkedMultiplayer {
+            paired: _,
+            current_side: _,
+            current_turn: _,
+            phrase,
+        } = self
+        {
+            phrase.clone()
+        } else {
+            None
+        }
+    }
 }
 
 impl Default for GameState {
@@ -128,10 +150,11 @@ impl From<MainMenuMessage> for GameState {
         match msg {
             MainMenuMessage::SinglePlayer(t, ai) => GameState::SinglePlayer(t, ai),
             MainMenuMessage::LocalMultiplayer => GameState::LocalMultiplayer,
-            MainMenuMessage::NetworkedMultiplayer => GameState::NetworkedMultiplayer {
+            MainMenuMessage::NetworkedMultiplayer(phrase) => GameState::NetworkedMultiplayer {
                 paired: false,
                 current_side: None,
                 current_turn: Turn::CyanPlayer,
+                phrase,
             },
         }
     }
@@ -380,7 +403,7 @@ pub fn new_placed() -> PlacedType {
 #[derive(Clone, Debug, PartialEq)]
 pub struct SharedState {
     pub board: BoardType,
-    pub game_state: Rc<Cell<GameState>>,
+    pub game_state: Rc<RefCell<GameState>>,
     pub turn: Rc<Cell<Turn>>,
     pub placed: PlacedType,
 }
@@ -390,7 +413,7 @@ impl Default for SharedState {
         Self {
             // cannot use [<type>; 56] because Rc does not impl Copy
             board: new_empty_board(),
-            game_state: Rc::new(Cell::new(GameState::default())),
+            game_state: Rc::new(RefCell::new(GameState::default())),
             turn: Rc::new(Cell::new(Turn::CyanPlayer)),
             placed: new_placed(),
         }
@@ -399,11 +422,11 @@ impl Default for SharedState {
 
 // This enum moved from yew_components module so that this module would have no
 // dependencies on the yew_components module
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum MainMenuMessage {
     SinglePlayer(Turn, AIDifficulty),
     LocalMultiplayer,
-    NetworkedMultiplayer,
+    NetworkedMultiplayer(Option<String>),
 }
 
 pub fn new_string_board() -> String {
@@ -582,12 +605,15 @@ mod tests {
             paired: false,
             current_side: None,
             current_turn: Turn::CyanPlayer,
+            phrase: None,
         };
         assert!(state.is_networked_multiplayer());
         let state = GameState::NetworkedMultiplayer {
             paired: true,
             current_side: Some(Turn::CyanPlayer),
             current_turn: Turn::MagentaPlayer,
+            phrase: None,
         };
+        assert!(state.is_networked_multiplayer());
     }
 }
index ba8a73aea3229dc4a0fd03d757c718af40189c3d..fd37e72597312e3bb4710888b8d425f37c8ab30d 100644 (file)
@@ -13,12 +13,12 @@ use crate::constants::{
 };
 use crate::game_logic::{check_win_draw, WinType};
 use crate::html_helper::{
-    append_to_info_text, create_json_request, element_append_class, element_has_class,
-    element_remove_class, get_window_document, send_to_backend,
+    append_to_info_text, element_append_class, element_has_class, element_remove_class,
+    get_window_document, send_to_backend,
 };
 use crate::random_helper::get_seeded_random;
 use crate::state::{
-    board_from_string, BoardState, BoardType, GameState, GameStateResponse, MainMenuMessage,
+    board_from_string, BoardState, GameState, GameStateResponse, MainMenuMessage,
     NetworkedGameState, PairingRequestResponse, PairingStatusResponse, PlaceTokenResponse,
     PlacedEnum, SharedState, Turn,
 };
@@ -28,10 +28,7 @@ use std::collections::HashMap;
 use std::rc::Rc;
 
 use js_sys::{Function, Promise};
-use wasm_bindgen::JsCast;
-use web_sys::{AddEventListenerOptions, Document, Response};
-
-use serde_json::Value as SerdeJSONValue;
+use web_sys::{AddEventListenerOptions, Document, HtmlInputElement};
 
 use wasm_bindgen_futures::JsFuture;
 
@@ -80,9 +77,9 @@ impl Component for MainMenu {
 
         let onclick_networked_multiplayer = ctx
             .link()
-            .callback(|_| MainMenuMessage::NetworkedMultiplayer);
+            .callback(|_| MainMenuMessage::NetworkedMultiplayer(None));
 
-        let menu_class = if shared.game_state.get() == GameState::MainMenu {
+        let menu_class = if shared.game_state.borrow().eq(&GameState::MainMenu) {
             "menu"
         } else {
             "hidden_menu"
@@ -104,9 +101,19 @@ impl Component for MainMenu {
                 <button class={"menuLocalMultiplayer"} onclick={onclick_local_multiplayer}>
                     {"Local Multiplayer"}
                 </button>
-                <button class={"menuMultiplayer"} onclick={onclick_networked_multiplayer}>
-                    {"Networked Multiplayer"}
-                </button>
+                <div class={"multiplayerMenu"}>
+                    <button class={"networkedMultiplayer"} onclick={onclick_networked_multiplayer}>
+                        {"Networked Multiplayer"}
+                    </button>
+                    <button class={"NMPhrase"} onclick={ctx.link().callback(|_| {
+                        let (_window, document) = get_window_document().expect("Should be able to get window/document");
+                        let input = HtmlInputElement::from(wasm_bindgen::JsValue::from(document.get_element_by_id("NMPhraseText").expect("Should be able to get NMPhrase input element")));
+                        MainMenuMessage::NetworkedMultiplayer(Some(input.value()))
+                    })}>
+                        {"NMultiplayer with Phrase"}
+                    </button>
+                    <input class={"NMPhrase"} id={"NMPhraseText"} placeholder={"input phrase here"} autofocus=true />
+                </div>
             </div>
         }
     }
@@ -120,13 +127,13 @@ impl Component for MainMenu {
         let document = window.document().expect("window should have a document");
 
         shared.game_state.replace(msg.into());
-        if shared.game_state.get() != GameState::MainMenu {
+        if !shared.game_state.borrow().eq(&GameState::MainMenu) {
             let mainmenu = document
                 .get_element_by_id("mainmenu")
                 .expect("mainmenu should exist");
             mainmenu.set_class_name("hidden_menu");
 
-            match shared.game_state.get() {
+            match shared.game_state.borrow().clone() {
                 GameState::SinglePlayer(turn, _) => {
                     if shared.turn.get() == turn {
                         append_to_info_text(
@@ -157,6 +164,7 @@ impl Component for MainMenu {
                     paired: _,
                     current_side: _,
                     current_turn: _,
+                    phrase: _,
                 } => {
                     append_to_info_text(
                         &document,
@@ -265,13 +273,14 @@ impl Component for Slot {
             .context::<SharedState>(Callback::noop())
             .expect("state to be set");
 
-        match shared.game_state.get() {
+        match shared.game_state.borrow().clone() {
             GameState::MainMenu => return false,
             GameState::SinglePlayer(_, _) | GameState::LocalMultiplayer => (),
             GameState::NetworkedMultiplayer {
                 paired,
                 current_side,
                 current_turn,
+                phrase: _,
             } => {
                 if paired && current_side.is_some() {
                     if current_side.as_ref().unwrap() == &current_turn {
@@ -289,7 +298,7 @@ impl Component for Slot {
             }
             GameState::PostGameResults(_) => return false,
         }
-        if shared.game_state.get() == GameState::MainMenu {
+        if shared.game_state.borrow().eq(&GameState::MainMenu) {
             return false;
         }
 
@@ -342,10 +351,19 @@ impl Wrapper {
     }
 
     fn get_networked_player_id(&mut self, ctx: &Context<Self>) {
+        let (shared, _) = ctx
+            .link()
+            .context::<SharedState>(Callback::noop())
+            .expect("state to be set");
+        let phrase_clone: Option<String> = shared.game_state.borrow().get_phrase();
+
         // make a request to get the player_id
-        ctx.link().send_future(async {
+        ctx.link().send_future(async move {
             let mut json_entries = HashMap::new();
             json_entries.insert("type".into(), "pairing_request".into());
+            if let Some(phrase_string) = phrase_clone {
+                json_entries.insert("phrase".into(), phrase_string);
+            }
 
             let send_to_backend_result = send_to_backend(json_entries).await;
             if let Err(e) = send_to_backend_result {
@@ -717,7 +735,7 @@ impl Component for Wrapper {
 
                 // check if player can make a move
                 if !msg.is_ai_pressed() {
-                    match shared.game_state.get() {
+                    match shared.game_state.borrow().clone() {
                         GameState::MainMenu => (),
                         GameState::SinglePlayer(turn, _) => {
                             if current_player != turn {
@@ -729,6 +747,7 @@ impl Component for Wrapper {
                             paired,
                             current_side,
                             current_turn,
+                            phrase: _,
                         } => {
                             if paired {
                                 if let Some(current_side) = current_side {
@@ -796,28 +815,29 @@ impl Component for Wrapper {
                 // info text right of the grid
                 {
                     let turn = shared.turn.get();
-                    let output_str =
-                        if let GameState::SinglePlayer(player_turn, _) = shared.game_state.get() {
-                            if shared.turn.get() == player_turn {
-                                format!(
-                                    "<b class=\"{}\">It is {}'s (player) turn</b>",
-                                    turn.get_color(),
-                                    turn
-                                )
-                            } else {
-                                format!(
-                                    "<b class=\"{}\">It is {}'s (ai) turn</b>",
-                                    turn.get_color(),
-                                    turn
-                                )
-                            }
+                    let output_str = if let GameState::SinglePlayer(player_turn, _) =
+                        shared.game_state.borrow().clone()
+                    {
+                        if shared.turn.get() == player_turn {
+                            format!(
+                                "<b class=\"{}\">It is {}'s (player) turn</b>",
+                                turn.get_color(),
+                                turn
+                            )
                         } else {
                             format!(
-                                "<b class=\"{}\">It is {}'s Turn</b>",
+                                "<b class=\"{}\">It is {}'s (ai) turn</b>",
                                 turn.get_color(),
                                 turn
                             )
-                        };
+                        }
+                    } else {
+                        format!(
+                            "<b class=\"{}\">It is {}'s Turn</b>",
+                            turn.get_color(),
+                            turn
+                        )
+                    };
 
                     let text_append_result =
                         append_to_info_text(&document, "info_text1", &output_str, 1);
@@ -1179,7 +1199,7 @@ impl Component for Wrapper {
 
                 // check if it is AI's turn
                 if let GameState::SinglePlayer(player_type, _ai_difficulty) =
-                    shared.game_state.get()
+                    shared.game_state.borrow().clone()
                 {
                     if shared.turn.get() != player_type {
                         ctx.link().send_message(WrapperMsg::AIChoice);
@@ -1192,7 +1212,8 @@ impl Component for Wrapper {
             }
             WrapperMsg::AIChoiceImpl => {
                 // get AI's choice
-                if let GameState::SinglePlayer(player_type, ai_difficulty) = shared.game_state.get()
+                if let GameState::SinglePlayer(player_type, ai_difficulty) =
+                    shared.game_state.borrow().clone()
                 {
                     if shared.turn.get() != player_type {
                         let choice =
@@ -1228,7 +1249,9 @@ impl Component for Wrapper {
                 ctx.link().send_message(WrapperMsg::BackendTick);
             }
             WrapperMsg::BackendTick => {
-                if !self.do_backend_tick || !shared.game_state.get().is_networked_multiplayer() {
+                let is_networked_multiplayer =
+                    shared.game_state.borrow().is_networked_multiplayer();
+                if !self.do_backend_tick || !is_networked_multiplayer {
                     // disconnect id if backend tick is to be stopped
                     if let Some(id) = self.player_id.take() {
                         let function = Function::new_no_args(&format!(
@@ -1248,12 +1271,15 @@ impl Component for Wrapper {
                     self.get_networked_player_id(ctx);
                 } else if shared
                     .game_state
-                    .get()
+                    .borrow()
                     .get_networked_current_side()
                     .is_none()
                 {
                     self.get_networked_player_type(ctx);
-                } else if !matches!(shared.game_state.get(), GameState::PostGameResults(_)) {
+                } else if !matches!(
+                    shared.game_state.borrow().clone(),
+                    GameState::PostGameResults(_)
+                ) {
                     if self.place_request.is_some() {
                         let placement = self.place_request.take().unwrap();
                         self.send_place_request(ctx, placement);
@@ -1323,10 +1349,10 @@ impl Component for Wrapper {
                         });
 
                         self.player_id = Some(id);
-                        let mut game_state = shared.game_state.get();
+                        let mut game_state = shared.game_state.borrow().clone();
                         game_state.set_networked_paired();
                         game_state.set_networked_current_side(turn_opt);
-                        shared.game_state.set(game_state);
+                        shared.game_state.replace(game_state);
                         if let Some(turn_type) = turn_opt {
                             append_to_info_text(
                                 &document,
@@ -1356,9 +1382,9 @@ impl Component for Wrapper {
                         }
                     }
                     BREnum::GotPairing(turn_opt) => {
-                        let mut game_state = shared.game_state.get();
+                        let mut game_state = shared.game_state.borrow().clone();
                         game_state.set_networked_current_side(turn_opt);
-                        shared.game_state.set(game_state);
+                        shared.game_state.replace(game_state);
                         if let Some(turn_type) = turn_opt {
                             append_to_info_text(
                                 &document,
@@ -1392,12 +1418,12 @@ impl Component for Wrapper {
                             self.update_board_from_string(&shared, &document, board_string);
                         }
 
-                        let mut current_game_state = shared.game_state.get();
+                        let mut current_game_state: GameState = shared.game_state.borrow().clone();
                         match networked_game_state {
                             NetworkedGameState::CyanTurn => {
                                 if current_game_state.get_current_turn() != Turn::CyanPlayer {
                                     current_game_state.set_networked_current_turn(Turn::CyanPlayer);
-                                    shared.game_state.set(current_game_state);
+                                    shared.game_state.replace(current_game_state.clone());
                                     append_to_info_text(
                                         &document,
                                         "info_text1",
@@ -1422,7 +1448,7 @@ impl Component for Wrapper {
                                 if current_game_state.get_current_turn() != Turn::MagentaPlayer {
                                     current_game_state
                                         .set_networked_current_turn(Turn::MagentaPlayer);
-                                    shared.game_state.set(current_game_state);
+                                    shared.game_state.replace(current_game_state.clone());
                                     append_to_info_text(
                                 &document,
                                 "info_text1",
@@ -1449,7 +1475,7 @@ impl Component for Wrapper {
                                 .ok();
                                 shared
                                     .game_state
-                                    .set(GameState::PostGameResults(BoardState::CyanWin));
+                                    .replace(GameState::PostGameResults(BoardState::CyanWin));
                                 self.do_backend_tick = false;
                             }
                             NetworkedGameState::MagentaWon => {
@@ -1462,7 +1488,7 @@ impl Component for Wrapper {
                                 .ok();
                                 shared
                                     .game_state
-                                    .set(GameState::PostGameResults(BoardState::MagentaWin));
+                                    .replace(GameState::PostGameResults(BoardState::MagentaWin));
                                 self.do_backend_tick = false;
                             }
                             NetworkedGameState::Draw => {
@@ -1475,7 +1501,7 @@ impl Component for Wrapper {
                                 .ok();
                                 shared
                                     .game_state
-                                    .set(GameState::PostGameResults(BoardState::Empty));
+                                    .replace(GameState::PostGameResults(BoardState::Empty));
                                 self.do_backend_tick = false;
                             }
                             NetworkedGameState::Disconnected => {
@@ -1495,7 +1521,7 @@ impl Component for Wrapper {
                                 .ok();
                                 shared
                                     .game_state
-                                    .set(GameState::PostGameResults(BoardState::Empty));
+                                    .replace(GameState::PostGameResults(BoardState::Empty));
                                 self.do_backend_tick = false;
                             }
                             NetworkedGameState::InternalError => {
@@ -1508,7 +1534,7 @@ impl Component for Wrapper {
                                 .ok();
                                 shared
                                     .game_state
-                                    .set(GameState::PostGameResults(BoardState::Empty));
+                                    .replace(GameState::PostGameResults(BoardState::Empty));
                                 self.do_backend_tick = false;
                             }
                             NetworkedGameState::NotPaired => (),
@@ -1522,7 +1548,7 @@ impl Component for Wrapper {
                                 .ok();
                                 shared
                                     .game_state
-                                    .set(GameState::PostGameResults(BoardState::Empty));
+                                    .replace(GameState::PostGameResults(BoardState::Empty));
                                 self.do_backend_tick = false;
                             }
                         }
@@ -1565,7 +1591,7 @@ impl Component for Wrapper {
                                     .ok();
                                     shared
                                         .game_state
-                                        .set(GameState::PostGameResults(BoardState::CyanWin));
+                                        .replace(GameState::PostGameResults(BoardState::CyanWin));
                                     self.do_backend_tick = false;
                                 }
                                 NetworkedGameState::MagentaWon => {
@@ -1576,9 +1602,9 @@ impl Component for Wrapper {
                                         1,
                                     )
                                     .ok();
-                                    shared
-                                        .game_state
-                                        .set(GameState::PostGameResults(BoardState::MagentaWin));
+                                    shared.game_state.replace(GameState::PostGameResults(
+                                        BoardState::MagentaWin,
+                                    ));
                                     self.do_backend_tick = false;
                                 }
                                 NetworkedGameState::Draw => {
@@ -1591,7 +1617,7 @@ impl Component for Wrapper {
                                     .ok();
                                     shared
                                         .game_state
-                                        .set(GameState::PostGameResults(BoardState::Empty));
+                                        .replace(GameState::PostGameResults(BoardState::Empty));
                                     self.do_backend_tick = false;
                                 }
                                 NetworkedGameState::Disconnected => {
@@ -1604,7 +1630,7 @@ impl Component for Wrapper {
                                     .ok();
                                     shared
                                         .game_state
-                                        .set(GameState::PostGameResults(BoardState::Empty));
+                                        .replace(GameState::PostGameResults(BoardState::Empty));
                                     self.do_backend_tick = false;
                                 }
                                 NetworkedGameState::InternalError => {
@@ -1617,7 +1643,7 @@ impl Component for Wrapper {
                                     .ok();
                                     shared
                                         .game_state
-                                        .set(GameState::PostGameResults(BoardState::Empty));
+                                        .replace(GameState::PostGameResults(BoardState::Empty));
                                     self.do_backend_tick = false;
                                 }
                                 NetworkedGameState::NotPaired => (),
@@ -1631,7 +1657,7 @@ impl Component for Wrapper {
                                     .ok();
                                     shared
                                         .game_state
-                                        .set(GameState::PostGameResults(BoardState::Empty));
+                                        .replace(GameState::PostGameResults(BoardState::Empty));
                                     self.do_backend_tick = false;
                                 }
                             },
@@ -1640,7 +1666,7 @@ impl Component for Wrapper {
                 }
             }
             WrapperMsg::Reset => {
-                shared.game_state.set(GameState::default());
+                shared.game_state.replace(GameState::default());
                 shared.turn.set(Turn::CyanPlayer);
                 for idx in 0..((ROWS * COLS) as usize) {
                     shared.placed[idx].set(false);
@@ -1718,7 +1744,7 @@ impl Component for InfoText {
                 }
             }
             1 => {
-                if shared.game_state.get() == GameState::MainMenu {
+                if shared.game_state.borrow().eq(&GameState::MainMenu) {
                     html! {
                         <div id={format!("info_text{}", ctx.props().id)} class={format!("info_text{}", ctx.props().id)}>
                             <p>