use crate::attempt::Attempt; use crate::status::Status; use crate::words::get_word_of_the_day; use std::collections::{HashMap, VecDeque}; use uuid::Uuid; pub type Board = [Option<Attempt>; 6]; #[derive(Clone)] pub enum InsertionStatus { Ok, LengthError, GameOver, } #[derive(Clone, Debug)] pub enum GameStatus { Playing, Win(String), Fail, } #[derive(Clone)] pub struct Game { pub uuid: Uuid, pub attempt_index: usize, pub answer: String, pub board: Board, pub status: GameStatus, pub last_insertion_status: InsertionStatus, } impl Game { pub async fn new() -> Self { Game { uuid: Uuid::new_v4(), attempt_index: 0, answer: get_word_of_the_day().await, board: Default::default(), status: GameStatus::Playing, last_insertion_status: InsertionStatus::Ok, } } pub fn add_attempt(&mut self, word: &String) -> InsertionStatus { let word = word.to_uppercase(); let win = word.eq(&self.answer); let insertion_status = match (self.attempt_index, word.chars().count(), &self.status) { (0..=5, 5, &GameStatus::Playing) => { let new_attempt = Attempt::from_string(&word, &self.answer); self.board[self.attempt_index] = Some(new_attempt); self.attempt_index += 1; match (win, self.attempt_index) { (true, _) => { self.status = GameStatus::Win(self.generate_result_pattern()); InsertionStatus::GameOver } (false, 6) => { self.status = GameStatus::Fail; InsertionStatus::GameOver } (_, _) => InsertionStatus::Ok, } } (0..=5, _, &GameStatus::Playing) => InsertionStatus::LengthError, (_, _, &GameStatus::Win(_) | &GameStatus::Fail) => InsertionStatus::GameOver, _ => todo!(), }; self.last_insertion_status = insertion_status.clone(); insertion_status } fn generate_result_pattern(&self) -> String { let mut output = String::new(); for attempt in &self.board { match attempt { Some(att) => { for ch in &att.chars { output.push(match ch.status { Status::Found => '🟩', Status::Almost => '🟨', Status::NotFound => '⬛', }); } output.push('\n'); } None => {} } } output } } pub struct SlotManager { max_length: usize, slots: HashMap<Uuid, Game>, order: VecDeque<Uuid>, } impl SlotManager { pub fn new(max_length: usize) -> Self { SlotManager { max_length, slots: HashMap::new(), order: VecDeque::new(), } } pub async fn new_game(&mut self) -> &mut Game { if self.order.len() >= self.max_length { if let Some(oldest_id) = self.order.pop_front() { self.slots.remove(&oldest_id); } } let new_game = Game::new().await; let new_id = new_game.uuid; self.slots.insert(new_id, new_game); self.order.push_back(new_id); // This should be safe because the game is inserted previously self.slots.get_mut(&new_id).unwrap() } pub fn get_game(&mut self, id: Uuid) -> Option<&mut Game> { self.slots.get_mut(&id) } }