worsdle_gemini/src/gameslot.rs

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)
}
}