use serde::{Serialize, Deserialize}; use std::collections::BTreeMap; #[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] pub enum GameState { Starting, Creating, Guessing } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct CreatingEx { pub question: String, pub letters: Vec } #[derive(Serialize, Clone, Debug)] pub struct Player { pub nick: String, pub creating_exercise: Option, pub submitted_word: Option, pub submitted_guess: Option> } #[derive(Serialize, Clone, Debug)] pub struct Game { pub players: Vec, pub round_number: i32, pub additional_questions: Vec, pub state: GameState, } impl Player { pub fn has_submitted_word(&self) -> bool { self.submitted_word.is_some() } pub fn has_submitted_guess(&self) -> bool { self.submitted_guess.is_some() } } impl Game { pub fn new() -> Self { Self { players: Vec::new(), round_number: 0, additional_questions: Vec::new(), state: GameState::Starting } } pub fn get_player_mut(&mut self, nick: &str) -> Option<&mut Player> { self.players.iter_mut().find(|x| x.nick == nick) } pub fn get_player(&self, nick: &str) -> Option<&Player> { self.players.iter().find(|x| x.nick == nick) } pub fn check_letters(word: &str, letters: &Vec) -> bool { let allowed_chars = [" ", "-", "'"]; if word.len() == 0 { return false; } let mut countmap: BTreeMap = BTreeMap::new(); for c in letters { let upper: String = c.to_uppercase().collect(); let val = countmap.get(&upper).unwrap_or(&0) + 1; countmap.insert(upper, val); } for c in word.to_uppercase().chars() { let upper = String::from(c); let count = countmap.get(&upper); if allowed_chars.iter().any(|x| &upper == *x) { continue; } if let Some(&v) = count { if v <= 0 { return false; } countmap.insert(upper, v - 1); } else { return false; } } true } pub fn submit_creation(&mut self, player_nick: &str, word: String) -> bool { match self.state { GameState::Creating => { if let Some(player) = self.get_player_mut(player_nick) { if let Some(ex) = &player.creating_exercise { if Self::check_letters(&word, &ex.letters) { player.submitted_word = Some(word); return true; } } } }, _ => {} } false } pub fn submit_guess(&mut self, player_nick: &str, guess: Vec<(String, String)>) -> bool { match self.state { GameState::Guessing => { if let Some(player) = self.get_player_mut(player_nick) { player.submitted_guess = Some(guess); true } else { false } }, _ => false } } pub fn all_words_submitted(&self) -> bool { self.state == GameState::Creating && self.players.iter().map(|p| p.submitted_word.is_some()).fold(true, |a, b| a && b) } pub fn all_guesses_submitted(&self) -> bool { self.state == GameState::Guessing && self.players.iter().map(|p| p.submitted_guess.is_some()).fold(true, |a, b| a && b) } pub fn next_state(&mut self) { if self.state == GameState::Guessing { self.clear_submissions(); } self.state = match self.state { GameState::Starting => GameState::Creating, GameState::Creating => GameState::Guessing, GameState::Guessing => GameState::Starting } } pub fn start_round(&mut self, mut word_generator: F, mut char_generator: G) where F: FnMut() -> String, G: FnMut() -> Vec { self.clear_submissions(); /*let mut exes = vec![ CreatingEx{ question: "Delikatessfutter für Hunde".to_owned(), letters: vec!['a', 'b', 'c'] }, CreatingEx{ question: "Ein Hotel für die ganze Familie".to_owned(), letters: vec!['b', 'c', 'd'] }, CreatingEx{ question: "Brasilianischer Superstar".to_owned(), letters: vec!['c', 'd', 'e'] }, CreatingEx{ question: "Buchstabe des griechischen Alphabets".to_owned(), letters: vec!['d', 'e', 'f'] }, ];*/ for p in &mut self.players { p.creating_exercise = Some( CreatingEx{ question: word_generator(), letters: char_generator() } ); } self.additional_questions.clear(); self.additional_questions.push(word_generator()); self.additional_questions.push(word_generator()); self.state = GameState::Creating; } pub fn clear_submissions(&mut self) { for p in &mut self.players { p.submitted_word = None; p.submitted_guess = None; p.creating_exercise = None; } } pub fn player_join(&mut self, nick: String) { self.players.push(Player{ nick: nick, creating_exercise: None, submitted_word: None, submitted_guess: None }); } pub fn remove_player(&mut self, nick: &str) { self.players.retain(|p| p.nick != nick); } pub fn create_results(&self) -> (Vec>>, Vec<(String, i64)>) { let mut result: Vec>> = Vec::new(); let mut questions = self.players.iter() .map(|p| p.creating_exercise.clone().unwrap().question) .collect::>(); questions.append(&mut self.additional_questions.iter() .map(|x| x.clone()) .collect::>() ); let mut player_points: Vec = vec![0; self.players.len()]; for (player, p_id) in self.players.iter().zip(0..) { let mut line: Vec> = Vec::new(); let word = player.submitted_word.as_ref().unwrap(); for (question, q_id) in questions.iter().zip(0..) { let mut cell: Vec = Vec::new(); for (p, inner_p) in self.players.iter().zip(0..) { if let Some(guess) = &p.submitted_guess { if let Some(_corr) = guess.iter().find(|(w, q)| w == word && q == question) { cell.push(p.nick.clone()); // if solution is correct and not its own if p_id == q_id && p_id != inner_p { player_points[p_id] += 1; player_points[inner_p] += 1; } } } } line.push(cell); } result.push(line); } let point_list = self.players.iter() .map(|p| p.nick.clone()) .zip(player_points) .collect::>(); return (result, point_list); } }