use std::collections::HashMap; use std::str::FromStr; use chrono::{DateTime, Duration, Utc}; use regex::Regex; use serde::{Deserialize, Serialize}; use crate::error::WoxlfError; use crate::game::player_data::PlayerData; use crate::game::Phase; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct GameState { pub game_name: String, pub phase_number: u64, pub current_phase: Phase, pub starting_phase: Phase, pub phase_end_time: DateTime, pub player_data: Vec, } impl GameState { pub fn new(starting_phase: Phase, starting_phase_duration: Duration, game_name: &str) -> Self { let phase_end_time = Utc::now() + starting_phase_duration; Self { game_name: game_name.to_string(), phase_number: 1, current_phase: starting_phase, phase_end_time, player_data: vec![], starting_phase, } } pub fn get_player_from_channel(&self, channel_id: u64) -> Option<&PlayerData> { self.player_data.iter().find(|p| p.channel == channel_id) } pub fn get_player_from_discord_id(&self, discord_id: u64) -> Option<&PlayerData> { self.player_data.iter().find(|p| p.discord_id == discord_id) } pub fn get_player_from_channel_mut(&mut self, channel_id: u64) -> Option<&mut PlayerData> { self.player_data .iter_mut() .find(|p| p.channel == channel_id) } pub fn get_player_by_codename(&self, codename: &str) -> Option { self.player_data .iter() .find(|p| p.codename.to_lowercase() == codename.to_lowercase()) .cloned() } pub fn next_phase(&mut self, duration: Duration) { if self.current_phase == Phase::Night { self.current_phase = Phase::Day } else { self.current_phase = Phase::Night } if self.current_phase == self.starting_phase { self.phase_number += 1; } for player in &mut self.player_data { player.clear() } self.set_phase_end_time(duration); } pub fn set_phase_end_time(&mut self, duration: Duration) { self.phase_end_time = Utc::now() + duration; } pub fn add_time_to_phase(&mut self, duration: Duration) { self.phase_end_time = self.phase_end_time + duration; } pub fn get_vote_tallies(&self) -> HashMap { let mut vote_set: HashMap = HashMap::new(); for player in &self.player_data { if let Some(vote_target) = player.vote_target { let target = self.get_player_from_discord_id(vote_target); if let Some(target) = target { *vote_set.entry(target.codename.clone()).or_insert(0) += 1; } } } vote_set } } #[derive(Debug, Clone)] pub struct PhaseDuration(Duration); impl From for Duration { fn from(dur: PhaseDuration) -> Self { dur.0 } } impl FromStr for PhaseDuration { type Err = WoxlfError; fn from_str(s: &str) -> std::result::Result { let re = Regex::new(r"(((?P[0-9]*)h)?((?P[0-9]*)m)?)").unwrap(); let mut duration = Duration::hours(0); if re.is_match(s) { let cap = re.captures_iter(s).next().unwrap(); if let Some(hour_str) = cap.name("hour") { let hours: i64 = hour_str .as_str() .parse() .map_err(|_| WoxlfError::DurationParseError)?; duration = duration + Duration::hours(hours); } if let Some(minute_str) = cap.name("min") { let minutes: i64 = minute_str .as_str() .parse() .map_err(|_| WoxlfError::DurationParseError)?; duration = duration + Duration::minutes(minutes); } Ok(PhaseDuration(duration)) } else { Err(WoxlfError::DurationParseError) } } }