From e7421551d20422344467648edce7403b28445c93 Mon Sep 17 00:00:00 2001 From: itsscb Date: Fri, 6 Sep 2024 00:02:10 +0200 Subject: [PATCH] feat(frontend): improves game model and prepares for refactoring and storage --- frontend/src/pages/game.rs | 132 +++++++++++++++++++++++++++---------- frontend/src/pages/home.rs | 88 ++++++++++++++++++++----- 2 files changed, 167 insertions(+), 53 deletions(-) diff --git a/frontend/src/pages/game.rs b/frontend/src/pages/game.rs index bae27b0..92fc030 100644 --- a/frontend/src/pages/game.rs +++ b/frontend/src/pages/game.rs @@ -1,14 +1,27 @@ -use rand::seq::SliceRandom; +// use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; use crate::CharStatus; -#[derive(Debug, Serialize, Deserialize, Clone)] -struct Game { - word: String, - submitted_words: Vec>>, - result: GameResult, -} +const MAX_TRIES: usize = 5; + +// #[derive(Debug, Serialize, Deserialize, Clone)] +// struct Games(Vec); + +// impl Games { +// pub const fn new() -> Self { +// Self(Vec::new()) +// } + +// pub fn new_game(&mut self, word: String) { +// let game = Game::new(); +// self.0.push(game); +// } + +// pub fn current_game(&self) -> Option<&Game> { +// self.0.last() +// } +// } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct WordList { @@ -22,43 +35,90 @@ impl WordList { pub fn from_json(s: &str) -> Self { serde_json::from_str(s).map_or(Self::new(), |w| w) } - pub fn to_json(&self) -> String { - serde_json::to_string_pretty(self).map_or(String::new(), |w| w) - } - pub fn get_word(&self) -> String { - let mut rng = rand::thread_rng(); - self.words - .choose(&mut rng) - .map_or_else(String::new, |w| (*w).to_string()) - } + // pub fn to_json(&self) -> String { + // serde_json::to_string_pretty(self).map_or(String::new(), |w| w) + // } + // pub fn get_word(&self) -> String { + // let mut rng = rand::thread_rng(); + // self.words + // .choose(&mut rng) + // .map_or_else(String::new, |w| (*w).to_string()) + // } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Game { + pub word: Option, + pub submitted_words: Vec>>, + tries: usize, + status: Status, } impl Game { - #[allow(dead_code)] - pub fn new(word: String, submitted_words: Vec>>) -> Self { - let result = submitted_words - .clone() - .into_iter() - .last() - .map_or(GameResult::Lose, |w| { - if w.iter().all(|v| matches!(v, CharStatus::Match(_))) { - GameResult::Win - } else { - GameResult::Lose - } - }); - + pub const fn new() -> Self { Self { - word, - submitted_words, - result, + word: None, + tries: 0, + submitted_words: Vec::new(), + status: Status::New, } } + + pub fn start(&mut self, word: String) { + if self.word.is_none() && self.status == Status::New { + self.status = Status::InProgress; + self.word = Some(word); + } + } + + pub fn submit_answer(&mut self, answer: &[String]) { + if let Some(ref word) = self.word { + let res = crate::compare_strings(word, &answer.join("")); + self.submitted_words.push(res); + self.tries += 1; + self.status = self.current_status(); + } + } + + pub fn current_status(&self) -> Status { + self.word.as_ref().map_or(Status::New, |_| { + let word_count = self.submitted_words.len(); + if self.tries == 0 { + Status::New + } else if self.tries < MAX_TRIES { + if self + .submitted_words + .last() + .unwrap() + .iter() + .all(|v| matches!(v, CharStatus::Match(_))) + { + Status::Win(word_count) + } else { + Status::InProgress + } + } else if self + .submitted_words + .last() + .unwrap() + .iter() + .all(|v| matches!(v, CharStatus::Match(_))) + { + Status::Win(word_count) + } else { + Status::Lose(word_count) + } + }) + } } +type Tries = usize; + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[allow(clippy::module_name_repetitions)] -pub enum GameResult { - Win, - Lose, +pub enum Status { + New, + Win(Tries), + Lose(Tries), + InProgress, } diff --git a/frontend/src/pages/home.rs b/frontend/src/pages/home.rs index 04a3af5..52b0861 100644 --- a/frontend/src/pages/home.rs +++ b/frontend/src/pages/home.rs @@ -5,7 +5,7 @@ use web_sys::HtmlElement; use yew::prelude::*; use yew::{classes, function_component, Callback, Html}; -use crate::pages::game::GameResult; +use crate::pages::game::{Game, Status}; use crate::CharStatus; use super::game::WordList; @@ -17,7 +17,7 @@ static MAX_TRIES: usize = 5; fn set_focus(index: usize) { let prefix = match index { 0 => "", - _ => "-" + _ => "-", }; if let Some(w) = web_sys::window() { if let Some(d) = w.document() { @@ -111,7 +111,7 @@ fn fetch_new_word( game_over: &UseStateHandle, length: &UseStateHandle, node_refs: &UseStateHandle>, - result: &UseStateHandle, + result: &UseStateHandle, ) { let loading = loading.clone(); let submitted_words = submitted_words.clone(); @@ -133,7 +133,7 @@ fn fetch_new_word( word.set(w.to_uppercase()); submitted_words.set(Vec::with_capacity(MAX_TRIES)); game_over.set(false); - result.set(GameResult::Lose); + result.set(Status::New); loading.set(false); } } @@ -153,8 +153,23 @@ fn fetch_words(state: &UseStateHandle) { }); } +fn new_game(game: &UseStateHandle) { + let game = game.clone(); + wasm_bindgen_futures::spawn_local(async move { + let res = Request::get(NEW_WORD_URI).send().await; + if let Ok(r) = res { + if let Ok(w) = r.text().await { + let mut g = (*game).clone(); + g.start(w); + game.set(g); + } + } + }); +} + #[function_component] pub fn Home() -> Html { + let game: UseStateHandle = use_state(Game::new); let word: UseStateHandle = use_state(String::new); let loading: UseStateHandle = use_state(|| true); let curr_index: UseStateHandle = use_state(|| 0usize); @@ -167,9 +182,10 @@ pub fn Home() -> Html { let input_values: UseStateHandle> = use_state(|| vec![String::new(); *length]); let game_over = use_state(|| false); - let result = use_state(|| GameResult::Lose); + let result = use_state(|| Status::New); { + let game = game.clone(); let handle = word.clone(); let loading = loading.clone(); @@ -181,6 +197,7 @@ pub fn Home() -> Html { let result = result.clone(); use_effect_with((), move |()| { + new_game(&game); fetch_new_word( &handle, &loading, @@ -212,7 +229,7 @@ pub fn Home() -> Html { .iter() .all(|v| matches!(v, CharStatus::Match(_))) { - result.set(GameResult::Win); + result.set(Status::Win(submitted_words.iter().count())); } game_over.set(true); } @@ -224,13 +241,19 @@ pub fn Home() -> Html { let input_values = input_values.clone(); Callback::from(move |_e: MouseEvent| { - let index = input_values.iter().enumerate().find(|(_, v)| v.is_empty()).map_or(0, |(i,_)| i); + let index = input_values + .iter() + .enumerate() + .find(|(_, v)| v.is_empty()) + .map_or(0, |(i, _)| i); set_focus(index); curr_index.set(index); }) }; let on_submit = { + let game = game.clone(); + let input_values = input_values.clone(); let submitted_words = submitted_words.clone(); let game_over = game_over.clone(); @@ -268,6 +291,10 @@ pub fn Home() -> Html { if !values.iter().all(|v| !v.is_empty()) { return; } + let mut g = (*game).clone(); + g.submit_answer(&input_values); + game.set(g); + let mut new_items = (*submitted_words).clone(); new_items.push(crate::compare_strings(&word, &values.join(""))); submitted_words.set(new_items); @@ -367,6 +394,30 @@ pub fn Home() -> Html { ) } > + // { + // match game.current_status() { + // Status::New => html!{ + // <> + // + // + // + //

{"Loading..."}

+ // + // }, + // Status::Win(tries) => html!{ + //

{format!("WIN: {tries}")}

+ // }, + // Status::Lose(tries) => html!{ + //

{format!("LOSE: {tries}")}

+ // }, + // Status::InProgress => html!{ + //
+ //

{"IN PROGRESS"}

+ //

{&game.word}

+ //
+ // }, + // } + // } if *loading { @@ -385,7 +436,7 @@ pub fn Home() -> Html { ) } > - +
Html { > { if *game_over { - + let (text, color) = match *result { - GameResult::Win => { + Status::Win(_) => { ("FOUND", "bg-green-600") }, - GameResult::Lose => { + Status::Lose(_) => { ("WANTED", "bg-red-600") - } + }, + _ => { + ("NEW", "bg-gray-600") + }, }; html! (
@@ -434,7 +488,7 @@ pub fn Home() -> Html { > { word.chars().map(|e|{ - + let text = e; html!{
  • Html { node_refs.iter().enumerate().map(|(index, node_ref)| { let on_focus = { let curr_index = curr_index.clone(); - + Callback::from(move |e: FocusEvent| { let target = e.target_unchecked_into::(); if let Some(index) = target.get_attribute("tabindex") { @@ -479,7 +533,7 @@ pub fn Home() -> Html { curr_index.set(i); } } - + }) }; let prefix = match index { @@ -523,7 +577,7 @@ pub fn Home() -> Html { "w-full", "flex", "justify-end", - + ) } > @@ -571,7 +625,7 @@ pub fn Home() -> Html { } } } - +
  • }