132 lines
3.7 KiB
Rust
132 lines
3.7 KiB
Rust
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)
|
|
}
|
|
}
|