diff --git a/gamelog.ron b/gamelog.ron index 96398a3..fd21282 100644 --- a/gamelog.ron +++ b/gamelog.ron @@ -260,7 +260,7 @@ Play( action: Unknown, // Original note: Same Failed Throw down: Fourth, - terrain: Unknown, + terrain: None, ), Quarter(Third), Kickoff(Colorado), diff --git a/gamelog/src/error.rs b/gamelog/src/error.rs index 2bcd8dc..9794d77 100644 --- a/gamelog/src/error.rs +++ b/gamelog/src/error.rs @@ -5,7 +5,7 @@ use std::{fmt, io}; pub enum LogFileError { IOError(io::Error), RonSpanned(ron::error::SpannedError), - TooManyTeams(usize), + TeamCount(usize), } impl fmt::Display for LogFileError { @@ -13,7 +13,7 @@ impl fmt::Display for LogFileError { match self { Self::IOError(err) => write!(f, "{}", err), Self::RonSpanned(err) => write!(f, "{}", err), - Self::TooManyTeams(err) => write!(f, "Expected two, found: {:?}", err), + Self::TeamCount(err) => write!(f, "Expected two, found: {:?}", err), } } } diff --git a/gamelog/src/event.rs b/gamelog/src/event.rs index ce4cbf1..2d83b3e 100644 --- a/gamelog/src/event.rs +++ b/gamelog/src/event.rs @@ -1,5 +1,6 @@ use crate::{Down, Play, Quarter, TerrainState}; use serde::Deserialize; +use strum::EnumIter; type Offence = Team; @@ -86,7 +87,7 @@ impl Event { } } -#[derive(Debug, Deserialize, Clone, PartialEq, Default)] +#[derive(Debug, Deserialize, Clone, PartialEq, Default, EnumIter)] pub enum Team { ArizonaState, #[deprecated(since = "0.2.0", note = "Team left the project.")] diff --git a/gamelog/src/file.rs b/gamelog/src/file.rs index a76a63f..2789d4c 100644 --- a/gamelog/src/file.rs +++ b/gamelog/src/file.rs @@ -8,7 +8,7 @@ pub struct LogFile(pub Vec); impl LogFile { /// Returns the most common action for a given team. - pub fn most_frequent_action(&self, team: Team) -> Action { + pub fn most_frequent_action(&self, team: Team) -> (Action, usize) { let mut most_freq_action = Action::default(); let mut frequency = usize::MIN; let mut found = usize::MIN; @@ -25,12 +25,12 @@ impl LogFile { } }); - most_freq_action + (most_freq_action, frequency) } /// Returns the least common action for a given team. /// This action has to have been played at least once. - pub fn least_frequent_action(&self, team: Team) -> Action { + pub fn least_frequent_action(&self, team: Team) -> (Action, usize) { let mut least_freq_action = Action::default(); let mut frequency = usize::MAX; let mut found = usize::MAX; @@ -42,21 +42,20 @@ impl LogFile { found = team_actions.clone().filter(|a| *a == action).count(); if (found != 0_usize) && (found < frequency) { - dbg!("hit"); frequency = found; least_freq_action = action.to_owned(); } }); - least_freq_action + (least_freq_action, frequency) } pub fn most_effective_play(&self, team: Team) -> (Action, TerrainState) { - let deltas: Vec> = self + let deltas = self .0 .iter() .map(|game| game.deltas(team.to_owned())) - .collect(); + .collect::>>(); let team_events: Vec> = self .0 @@ -65,7 +64,7 @@ impl LogFile { .collect::>() .iter() .map(|team_events| team_events.0.to_owned()) - .collect::>>(); + .collect(); let mut action_return = Action::Unknown; let mut terrain_delta: u8 = 0; @@ -87,15 +86,15 @@ impl LogFile { } } - event_idx += 1; + if (event_idx + 1) == game.len() { + event_idx = 0; + continue; + } else { + event_idx += 1; + } } game_idx += 1; - - if (event_idx + 1) == game.len() { - event_idx = 0; - continue; - } } let sum: u8 = action_deltas.iter().sum::() as u8; @@ -194,11 +193,11 @@ mod tests { ..Default::default() }), Event::Play(Play { - action: Action::Mesh, + action: Action::Curls, ..Default::default() }), Event::Play(Play { - action: Action::Curls, + action: Action::Mesh, ..Default::default() }), Event::Play(Play { @@ -239,11 +238,11 @@ mod tests { ..Default::default() }), Event::Play(Play { - action: Action::Curls, + action: Action::SlotOut, ..Default::default() }), Event::Play(Play { - action: Action::SlotOut, + action: Action::Curls, ..Default::default() }), Event::Kickoff(Team::ArizonaState), diff --git a/gamelog/src/game.rs b/gamelog/src/game.rs index 310f914..28b6584 100644 --- a/gamelog/src/game.rs +++ b/gamelog/src/game.rs @@ -37,7 +37,7 @@ impl Game { if teams.len() == 2 || ignore.len() != 0 { Ok(teams) } else { - Err(error::LogFileError::TooManyTeams(teams.len())) + Err(error::LogFileError::TeamCount(teams.len())) } } @@ -56,7 +56,13 @@ impl Game { } }) .collect(); - let len = events.len() - 1; + + let len = if events.len() == 0 { + return vec![0]; + } else { + events.len() - 1 + }; + let mut idx: usize = 0; let mut deltas: Vec = vec![]; @@ -326,7 +332,7 @@ impl Period { #[derive(Debug, Deserialize, Clone, PartialEq)] pub enum Flags { - ClockBleeding(Team), + ClockBleed(Team), IgnoreActions, IgnoreTeam(Team), IgnoreScore, @@ -400,15 +406,6 @@ mod tests { }; let c = Game { - events: vec![ - Event::Kickoff(Team::Nebraska), - Event::Turnover(Team::ArizonaState), - Event::Kickoff(Team::Nebraska), - ], - ..Default::default() - }; - - let d = Game { flags: vec![Flags::IgnoreTeam(Team::Nebraska)], events: vec![Event::Kickoff(Team::Nebraska)], ..Default::default() @@ -416,8 +413,7 @@ mod tests { assert!(a.teams().unwrap() == vec![Team::Nebraska, Team::ArizonaState]); assert!(b.teams().is_err() == true); - assert!(c.teams().unwrap() == vec![Team::ArizonaState]); - assert!(d.teams().unwrap() == vec![]); + assert!(c.teams().unwrap() == vec![]); } #[test] diff --git a/miller/Cargo.lock b/miller/Cargo.lock index e1029b3..9311afe 100644 --- a/miller/Cargo.lock +++ b/miller/Cargo.lock @@ -314,6 +314,7 @@ dependencies = [ "clap", "gamelog", "ratatui", + "strum 0.27.1", ] [[package]] diff --git a/miller/Cargo.toml b/miller/Cargo.toml index e2cfd0f..17492c7 100644 --- a/miller/Cargo.toml +++ b/miller/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT" [dependencies] ratatui = "0.29" +strum = "0.27" [dependencies.clap] version = "4.5" diff --git a/miller/src/main.rs b/miller/src/main.rs index 84eba41..d0cf00a 100644 --- a/miller/src/main.rs +++ b/miller/src/main.rs @@ -2,8 +2,9 @@ mod tui; use clap::{ArgAction, Parser}; use core::panic; -use gamelog::{Action, Down, Flags, Key, LogFile, Team}; +use gamelog::{Action, Down, Flags, Key, LogFile, Team, TerrainState}; use std::{io, path::PathBuf, sync::mpsc, thread}; +use strum::IntoEnumIterator; use tui::App; #[derive(Debug, Parser)] @@ -43,16 +44,14 @@ fn main() -> io::Result<()> { TeamStats::new(Team::Iowa), TeamStats::new(Team::Nebraska), TeamStats::new(Team::Syracuse), + #[allow(deprecated)] TeamStats::new(Team::SouthCarolina), TeamStats::new(Team::TexasAnM), ]; // Work on knocking down the nesting here? for game in log.0.iter() { - let teams = match game.teams() { - Ok(teams) => teams, - Err(_) => continue, - }; + let teams = game.teams().unwrap(); for team in teams { // Skip team if they are to be ignored this game. @@ -89,6 +88,21 @@ fn main() -> io::Result<()> { .penalties_per_game .push(game.penalties(team.to_owned())); } + + for team in gamelog::Team::iter() { + let team_idx = stats + .iter() + .position(|stat| stat.team == team.to_owned()) + .unwrap(); + + stats[team_idx].most_common_play = Some(log.most_frequent_action(team.to_owned())); + stats[team_idx].least_common_play = + Some(log.least_frequent_action(team.to_owned())); + /* + stats[team_idx].most_effective_play = + Some(log.most_effective_play(team.to_owned())); + */ + } } // :#? for pretty-printing. @@ -127,14 +141,12 @@ struct TeamStats { plays_per_game: Vec, // Penalties penalties_per_game: Vec, - // Score - points_per_quarter: Vec, - points_per_game: Vec, // Biases - most_common_play: Option, - least_common_play: Option, + most_common_play: Option<(Action, usize)>, + least_common_play: Option<(Action, usize)>, most_common_key: Option, least_common_key: Option, + most_effective_play: Option<(Action, TerrainState)>, // Traits // Typical number of downs to achieve 10 yards. time_to_first_down: Option, @@ -150,12 +162,11 @@ impl TeamStats { plays_per_quarter: vec![], plays_per_game: vec![], penalties_per_game: vec![], - points_per_quarter: vec![], - points_per_game: vec![], most_common_play: None, least_common_play: None, most_common_key: None, least_common_key: None, + most_effective_play: None, time_to_first_down: None, } }