Compare commits
1 Commits
b11f6037c2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6496beeee5 |
55
README.adoc
55
README.adoc
@@ -9,3 +9,58 @@ For my stats course (4U Data Management) I have to interpret a bunch of data gen
|
|||||||
In an effort to not spend ages mindlessly using a calculator every summative check-in, I have started this project.
|
In an effort to not spend ages mindlessly using a calculator every summative check-in, I have started this project.
|
||||||
|
|
||||||
I figured, that since I already had to digitize every note, that I was required to make by hand, from during the game, that I might as well employ a format of my own. This format, would allow me to use code to interpret the monotonous mathematics for me. Even if I don't fully complete the code, it would ease the burden by completing some of the math for me.
|
I figured, that since I already had to digitize every note, that I was required to make by hand, from during the game, that I might as well employ a format of my own. This format, would allow me to use code to interpret the monotonous mathematics for me. Even if I don't fully complete the code, it would ease the burden by completing some of the math for me.
|
||||||
|
|
||||||
|
== Goals
|
||||||
|
|
||||||
|
* [ ] Auto Ranking system?
|
||||||
|
* [ ] Data Visualizer?
|
||||||
|
* [ ] Dynamic Web Page?
|
||||||
|
* [ ] Pattern Analysis / Play Trend Analysis
|
||||||
|
** [ ] Most Frequent Play
|
||||||
|
** [ ] Least Frequent Play
|
||||||
|
** [ ] Most Effective Play (Greatest Terrain Gain on average)
|
||||||
|
** [ ] Most frequent play set.
|
||||||
|
** [ ] Repeating play pattern.
|
||||||
|
** [ ] Slow after score.
|
||||||
|
** [ ] Bias to using Play Actions
|
||||||
|
** [ ] Bias to using Runs
|
||||||
|
|
||||||
|
=== Gamelog
|
||||||
|
* [*] Data Format
|
||||||
|
** [*] Support recording multiple games
|
||||||
|
** [*] Periods
|
||||||
|
*** Quarters
|
||||||
|
*** Overtime
|
||||||
|
** [*] Teams
|
||||||
|
*** Iowa
|
||||||
|
*** Syracuse
|
||||||
|
*** Texas A&M
|
||||||
|
*** Colorado
|
||||||
|
*** Nebraska
|
||||||
|
*** Boise State [deprecated]
|
||||||
|
*** Arizona State
|
||||||
|
*** South Carolina
|
||||||
|
** [*] Downs
|
||||||
|
*** First - Fourth
|
||||||
|
*** Kickoff
|
||||||
|
*** PAT (Point After Touchdown)
|
||||||
|
**** One
|
||||||
|
**** Two
|
||||||
|
**** Failed
|
||||||
|
** [*] Score
|
||||||
|
** [*] Terrain Position
|
||||||
|
*** [*] Yards
|
||||||
|
*** [*] In (Inches)
|
||||||
|
*** [*] GL (Goal Line)
|
||||||
|
** [*] Penalty
|
||||||
|
** Out?
|
||||||
|
** [*] Plays
|
||||||
|
|
||||||
|
=== Miller:
|
||||||
|
* [ ] Mathematics
|
||||||
|
** [*] Avg. Terrain Gain
|
||||||
|
** [*] Avg. Terrain Loss
|
||||||
|
** [*] Avg. Terrain Delta
|
||||||
|
** [*] Avg. Offence Plays per quarter
|
||||||
|
** [*] Avg. Offence Plays per game
|
||||||
|
** [*] Avg. Penalties per game
|
||||||
|
|||||||
1700
gamelog.ron
1700
gamelog.ron
File diff suppressed because it is too large
Load Diff
14
gamelog/Cargo.lock
generated
14
gamelog/Cargo.lock
generated
@@ -19,7 +19,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gamelog"
|
name = "gamelog"
|
||||||
version = "0.7.3"
|
version = "0.7.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ron",
|
"ron",
|
||||||
"semver",
|
"semver",
|
||||||
@@ -35,9 +35,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -53,9 +53,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ron"
|
name = "ron"
|
||||||
version = "0.10.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f"
|
checksum = "63f3aa105dea217ef30d89581b65a4d527a19afc95ef5750be3890e8d3c5b837"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
@@ -123,9 +123,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gamelog"
|
name = "gamelog"
|
||||||
version = "0.7.3"
|
version = "0.7.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ron = "0.10"
|
ron = "0.9"
|
||||||
|
|
||||||
[dependencies.strum]
|
[dependencies.strum]
|
||||||
version = "0.27"
|
version = "0.27"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use strum::EnumIter;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Default, PartialEq, EnumIter)]
|
#[derive(Debug, Deserialize, Clone, Default, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
CrackStudentBodyRightTackle,
|
CrackStudentBodyRightTackle,
|
||||||
Curls,
|
Curls,
|
||||||
@@ -15,7 +14,6 @@ pub enum Action {
|
|||||||
PlayActionComebacks,
|
PlayActionComebacks,
|
||||||
PlayActionPowerZero,
|
PlayActionPowerZero,
|
||||||
PowerZero,
|
PowerZero,
|
||||||
Punt,
|
|
||||||
SlantBubble,
|
SlantBubble,
|
||||||
SlotOut,
|
SlotOut,
|
||||||
SpeedOption,
|
SpeedOption,
|
||||||
@@ -85,7 +83,7 @@ impl Action {
|
|||||||
Some(match self {
|
Some(match self {
|
||||||
Self::SlantBubble | Self::HalfbackSlam | Self::PlayActionBoot => Playset::PistolSpread,
|
Self::SlantBubble | Self::HalfbackSlam | Self::PlayActionBoot => Playset::PistolSpread,
|
||||||
Self::StrongFlood | Self::SpeedOption | Self::HalfbackSlipScreen => {
|
Self::StrongFlood | Self::SpeedOption | Self::HalfbackSlipScreen => {
|
||||||
Playset::ShotgunWingOffsetWk
|
Playset::ShotgunTripleWingsOffset
|
||||||
}
|
}
|
||||||
Self::SlotOut | Self::HalfbackSweep | Self::PlayActionComebacks => {
|
Self::SlotOut | Self::HalfbackSweep | Self::PlayActionComebacks => {
|
||||||
Playset::ShotgunDoubleFlex
|
Playset::ShotgunDoubleFlex
|
||||||
@@ -127,7 +125,7 @@ impl Action {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Playset {
|
pub enum Playset {
|
||||||
PistolSpread,
|
PistolSpread,
|
||||||
ShotgunWingOffsetWk,
|
ShotgunTripleWingsOffset,
|
||||||
ShotgunDoubleFlex,
|
ShotgunDoubleFlex,
|
||||||
IFormNormal,
|
IFormNormal,
|
||||||
IFormTight,
|
IFormTight,
|
||||||
|
|||||||
@@ -1,31 +1,47 @@
|
|||||||
use ron::de::SpannedError;
|
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LogFileError {
|
pub enum LogFileError {
|
||||||
IOError(io::Error),
|
FailedToOpen(io::Error),
|
||||||
RonSpanned(ron::error::SpannedError),
|
RonSpannedError(ron::error::SpannedError),
|
||||||
TeamCount(usize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LogFileError {
|
impl fmt::Display for LogFileError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::IOError(err) => write!(f, "{}", err),
|
Self::FailedToOpen(err) => write!(f, "{}", err),
|
||||||
Self::RonSpanned(err) => write!(f, "{}", err),
|
Self::RonSpannedError(err) => write!(f, "{}", err),
|
||||||
Self::TeamCount(err) => write!(f, "Expected two, found: {:?}", err),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SpannedError> for LogFileError {
|
#[derive(Debug)]
|
||||||
fn from(value: SpannedError) -> Self {
|
pub enum TeamsError {
|
||||||
Self::RonSpanned(value)
|
NumberFound(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TeamsError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NumberFound(err) => write!(f, "Expected two, found: {:?}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for LogFileError {
|
#[derive(Debug)]
|
||||||
fn from(value: io::Error) -> Self {
|
pub struct NoTeamAttribute;
|
||||||
Self::IOError(value)
|
|
||||||
|
impl fmt::Display for NoTeamAttribute {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Object has no team definition.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CannotDetermineTeams;
|
||||||
|
|
||||||
|
impl fmt::Display for CannotDetermineTeams {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Cannot determine teams present.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::{Down, Play, Quarter, TerrainState};
|
use crate::{Down, Play, Quarter, TerrainState, error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use strum::EnumIter;
|
|
||||||
|
|
||||||
type Offence = Team;
|
type Offence = Team;
|
||||||
|
|
||||||
@@ -15,17 +14,35 @@ pub enum Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
/// Returns the terrain delta between the self and given following events.
|
|
||||||
/// Returns `None` if no delta can be calculated between
|
|
||||||
/// `self` and following events.
|
|
||||||
pub fn delta(&self, following: &Self) -> Option<i8> {
|
pub fn delta(&self, following: &Self) -> Option<i8> {
|
||||||
let preceeding = self.to_play()?;
|
// Clean this trash spaghetti code up.
|
||||||
|
|
||||||
|
fn make_play(event: &Event) -> Option<Play> {
|
||||||
|
match event {
|
||||||
|
Event::Kickoff(_) | Event::Turnover(_) => Some(Play::default()),
|
||||||
|
Event::Play(play) => {
|
||||||
|
let p = play.to_owned();
|
||||||
|
|
||||||
|
if p.down.is_none()
|
||||||
|
|| p.terrain.is_none()
|
||||||
|
|| p.terrain.as_ref()? == &TerrainState::Unknown
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let preceeding = make_play(self)?;
|
||||||
let following = if let Event::Turnover(_) = following {
|
let following = if let Event::Turnover(_) = following {
|
||||||
// I should really just early return
|
// I should really just early return
|
||||||
// but this is too funny to look at.
|
// but this is too funny to look at.
|
||||||
None?
|
None?
|
||||||
} else {
|
} else {
|
||||||
following.to_play()?
|
make_play(following)?
|
||||||
};
|
};
|
||||||
|
|
||||||
if following.down? == Down::First {
|
if following.down? == Down::First {
|
||||||
@@ -51,18 +68,14 @@ impl Event {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the team for variants which possess this attribute.
|
pub fn team(&self) -> Result<Team, error::NoTeamAttribute> {
|
||||||
/// Errors if `self` has no team attribute.
|
|
||||||
pub fn team(&self) -> Option<Team> {
|
|
||||||
match self {
|
match self {
|
||||||
Self::Kickoff(team) => Some(team.to_owned()),
|
Self::Kickoff(team) => Ok(team.to_owned()),
|
||||||
Self::Turnover(team) => Some(team.to_owned()),
|
Self::Turnover(team) => Ok(team.to_owned()),
|
||||||
_ => None,
|
_ => Err(error::NoTeamAttribute),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the team for variants which possess this attribute.
|
|
||||||
/// Returns `None` if `self` has no team attribute.
|
|
||||||
pub fn quarter(&self) -> Option<Quarter> {
|
pub fn quarter(&self) -> Option<Quarter> {
|
||||||
if let Event::Quarter(quarter) = self {
|
if let Event::Quarter(quarter) = self {
|
||||||
Some(quarter.to_owned())
|
Some(quarter.to_owned())
|
||||||
@@ -70,33 +83,16 @@ impl Event {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an event into it's associated Play object, if there is one.
|
|
||||||
fn to_play(self: &Event) -> Option<Play> {
|
|
||||||
if let Event::Play(play) = self {
|
|
||||||
if play.down.is_none() || play.terrain.is_none() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(play.to_owned())
|
|
||||||
}
|
|
||||||
} else if let Event::Kickoff(_) | Event::Turnover(_) = self {
|
|
||||||
Some(Play::default())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, Default, EnumIter)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
pub enum Team {
|
pub enum Team {
|
||||||
ArizonaState,
|
ArizonaState,
|
||||||
#[deprecated(since = "0.2.0", note = "Team left the project.")]
|
#[deprecated(since = "0.2.0", note = "Team left the project.")]
|
||||||
BoiseState,
|
BoiseState,
|
||||||
Colorado,
|
Colorado,
|
||||||
Iowa,
|
Iowa,
|
||||||
#[default]
|
|
||||||
Nebraska,
|
Nebraska,
|
||||||
#[deprecated(since = "0.7.3", note = "Team left the project.")]
|
|
||||||
SouthCarolina,
|
SouthCarolina,
|
||||||
Syracuse,
|
Syracuse,
|
||||||
TexasAnM,
|
TexasAnM,
|
||||||
|
|||||||
@@ -1,180 +1,36 @@
|
|||||||
use crate::{Action, Event, Game, Play, Team, TeamEvents, TerrainState, error};
|
use crate::error;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{fs::File, path::PathBuf, usize};
|
use std::{fs::File, path::PathBuf};
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Default)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct LogFile(pub Vec<Game>);
|
pub struct LogFile(pub Vec<super::Game>);
|
||||||
|
|
||||||
impl LogFile {
|
impl LogFile {
|
||||||
/// Returns the most common action for a given team.
|
pub fn min_ver(&self) -> semver::Version {
|
||||||
pub fn most_frequent_action(&self, team: Team) -> (Action, usize) {
|
let mut lowest = semver::Version::new(u64::MAX, u64::MAX, u64::MAX);
|
||||||
let mut most_freq_action = Action::default();
|
|
||||||
let mut frequency = usize::MIN;
|
|
||||||
let mut found = usize::MIN;
|
|
||||||
let team_actions = self.team_actions(team).into_iter();
|
|
||||||
|
|
||||||
Action::iter()
|
self.0.iter().for_each(|x| {
|
||||||
.filter(|action| *action != Action::Unknown)
|
if x.version.cmp_precedence(&lowest).is_lt() {
|
||||||
.for_each(|action| {
|
lowest = x.version.clone()
|
||||||
found = team_actions.clone().filter(|a| *a == action).count();
|
}
|
||||||
|
|
||||||
if found > frequency {
|
|
||||||
frequency = found;
|
|
||||||
most_freq_action = action.to_owned();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
(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, usize) {
|
|
||||||
let mut least_freq_action = Action::default();
|
|
||||||
let mut frequency = usize::MAX;
|
|
||||||
let mut found = usize::MAX;
|
|
||||||
let team_actions = self.team_actions(team).into_iter();
|
|
||||||
|
|
||||||
Action::iter()
|
|
||||||
.filter(|action| *action != Action::Unknown)
|
|
||||||
.for_each(|action| {
|
|
||||||
found = team_actions.clone().filter(|a| *a == action).count();
|
|
||||||
|
|
||||||
if (found != 0_usize) && (found < frequency) {
|
|
||||||
frequency = found;
|
|
||||||
least_freq_action = action.to_owned();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
(least_freq_action, frequency)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn frequency_of_plays(&self, team: Team) -> Vec<(Action, usize)> {
|
|
||||||
let team_actions = self.team_actions(team.to_owned()).into_iter();
|
|
||||||
let mut plays: Vec<(Action, usize)> = vec![];
|
|
||||||
|
|
||||||
Action::iter().for_each(|action| {
|
|
||||||
plays.push((
|
|
||||||
action.to_owned(),
|
|
||||||
team_actions.clone().filter(|a| *a == action).count(),
|
|
||||||
))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
plays.sort_by(|a, b| a.1.cmp(&b.1));
|
lowest
|
||||||
plays
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn most_effective_play(&self, team: Team) -> (Action, TerrainState) {
|
/// Returns if the LogFile min version is compatible.
|
||||||
let deltas = self
|
pub fn is_compatible(&self) -> bool {
|
||||||
.0
|
self.min_ver().cmp_precedence(&crate::MIN_VER).is_lt()
|
||||||
.iter()
|
|
||||||
.map(|game| game.deltas(team.to_owned()))
|
|
||||||
.collect::<Vec<Vec<i8>>>();
|
|
||||||
|
|
||||||
let team_events: Vec<Vec<Event>> = self
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.filter_map(|game| game.team_events(team.to_owned()))
|
|
||||||
.collect::<Vec<TeamEvents>>()
|
|
||||||
.iter()
|
|
||||||
.map(|team_events| team_events.0.to_owned())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut action_return = Action::Unknown;
|
|
||||||
let mut terrain_delta: u8 = 0;
|
|
||||||
|
|
||||||
let mut action_deltas: Vec<i8>;
|
|
||||||
let mut game_idx: usize;
|
|
||||||
let mut event_idx: usize;
|
|
||||||
|
|
||||||
for action in Action::iter().filter(|action| *action != Action::Unknown) {
|
|
||||||
action_deltas = vec![];
|
|
||||||
game_idx = 0;
|
|
||||||
event_idx = 0;
|
|
||||||
|
|
||||||
for game in &team_events {
|
|
||||||
for _ in game {
|
|
||||||
if let Event::Play(play) = &team_events[game_idx][event_idx] {
|
|
||||||
if play.action == action {
|
|
||||||
action_deltas.push(deltas[game_idx][event_idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event_idx + 1) == game.len() {
|
|
||||||
event_idx = 0;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
event_idx += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
game_idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sum: u8 = action_deltas.iter().sum::<i8>() as u8;
|
|
||||||
|
|
||||||
if sum > terrain_delta {
|
|
||||||
terrain_delta = sum;
|
|
||||||
action_return = action.to_owned();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(action_return, TerrainState::Yards(terrain_delta))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_teams(self) -> Result<LogFile, error::LogFileError> {
|
|
||||||
for game in &self.0 {
|
|
||||||
if let Err(err) = game.teams() {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the team actions.
|
|
||||||
/// The following code is equivalent to:
|
|
||||||
fn team_actions(&self, team: Team) -> Vec<Action> {
|
|
||||||
// ```
|
|
||||||
// fn foo(&self, team: Team) -> Vec<Action> {
|
|
||||||
// let mut actions = vec![];
|
|
||||||
//
|
|
||||||
// for game in &self.0 {
|
|
||||||
// for play in game.team_plays(team.to_owned()).0 {
|
|
||||||
// actions.push(play.action.to_owned())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// actions
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
// I just write iterators more naturally for some reason
|
|
||||||
// despite them being less readable afterward. I came from
|
|
||||||
// loving python's list comprehensions.
|
|
||||||
// I suppose I like the lack of nesting.
|
|
||||||
//
|
|
||||||
// I have no clue if the iterator is actually more efficient.
|
|
||||||
self.0
|
|
||||||
.iter()
|
|
||||||
.filter_map(|game| Some(game.team_plays(team.to_owned()).0))
|
|
||||||
.collect::<Vec<Vec<Play>>>()
|
|
||||||
.concat()
|
|
||||||
.iter()
|
|
||||||
.filter_map(|play| Some(play.action.to_owned()))
|
|
||||||
.collect::<Vec<Action>>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<File> for LogFile {
|
impl TryFrom<File> for LogFile {
|
||||||
type Error = error::LogFileError;
|
type Error = ron::error::SpannedError;
|
||||||
|
|
||||||
fn try_from(file: File) -> Result<LogFile, Self::Error> {
|
fn try_from(file: File) -> Result<Self, Self::Error> {
|
||||||
let file: LogFile = ron::Options::default()
|
ron::Options::default()
|
||||||
.with_default_extension(ron::extensions::Extensions::EXPLICIT_STRUCT_NAMES)
|
.with_default_extension(ron::extensions::Extensions::EXPLICIT_STRUCT_NAMES)
|
||||||
.from_reader(file)?;
|
.from_reader(file)
|
||||||
|
|
||||||
file.check_teams()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,89 +38,17 @@ impl TryFrom<PathBuf> for LogFile {
|
|||||||
type Error = error::LogFileError;
|
type Error = error::LogFileError;
|
||||||
|
|
||||||
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
|
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
|
||||||
Self::try_from(
|
match Self::try_from(
|
||||||
std::fs::OpenOptions::new() // Defaults to setting all options false.
|
match std::fs::OpenOptions::new() // Defaults to setting all options false.
|
||||||
.read(true) // Only need ensure that reading is possible.
|
.read(true) // Only need ensure that reading is possible.
|
||||||
.open(path.as_path())?,
|
.open(path.as_path())
|
||||||
)
|
{
|
||||||
}
|
Ok(f) => f,
|
||||||
}
|
Err(err) => return Err(error::LogFileError::FailedToOpen(err)),
|
||||||
|
},
|
||||||
#[cfg(test)]
|
) {
|
||||||
mod tests {
|
Ok(f) => Ok(f),
|
||||||
use crate::*;
|
Err(err) => Err(error::LogFileError::RonSpannedError(err)),
|
||||||
|
}
|
||||||
#[test]
|
|
||||||
fn most_frequent_action() {
|
|
||||||
let a = LogFile(vec![Game {
|
|
||||||
events: vec![
|
|
||||||
Event::Kickoff(Team::Nebraska),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Mesh,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Mesh,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Curls,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Mesh,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Curls,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::SlotOut,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Kickoff(Team::ArizonaState),
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}]);
|
|
||||||
|
|
||||||
assert!(a.most_frequent_action(Team::Nebraska) == (Action::Mesh, 3_usize))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn least_frequent_action() {
|
|
||||||
let a = LogFile(vec![Game {
|
|
||||||
events: vec![
|
|
||||||
Event::Kickoff(Team::Nebraska),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Mesh,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Mesh,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Mesh,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Curls,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::SlotOut,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Curls,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
Event::Kickoff(Team::ArizonaState),
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}]);
|
|
||||||
|
|
||||||
assert!(a.least_frequent_action(Team::Nebraska) == (Action::SlotOut, 1_usize))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Event, MIN_VER, Play, Quarter, Team, error};
|
use crate::{Event, Quarter, Team, error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ pub struct Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
/// Returns the teams that played.
|
/// Returns the teams of this game.
|
||||||
pub fn teams(&self) -> Result<Vec<Team>, error::LogFileError> {
|
pub fn teams(&self) -> Result<Vec<Team>, error::TeamsError> {
|
||||||
let ignore: Vec<Team> = self
|
let ignore: Vec<Team> = self
|
||||||
.flags
|
.flags
|
||||||
.iter()
|
.iter()
|
||||||
@@ -27,7 +27,7 @@ impl Game {
|
|||||||
let mut teams = vec![];
|
let mut teams = vec![];
|
||||||
|
|
||||||
self.events.iter().for_each(|event| {
|
self.events.iter().for_each(|event| {
|
||||||
if let Some(team) = event.team() {
|
if let Ok(team) = event.team() {
|
||||||
if !ignore.contains(&team) && !teams.contains(&team) {
|
if !ignore.contains(&team) && !teams.contains(&team) {
|
||||||
teams.push(team)
|
teams.push(team)
|
||||||
}
|
}
|
||||||
@@ -37,16 +37,13 @@ impl Game {
|
|||||||
if teams.len() == 2 || ignore.len() != 0 {
|
if teams.len() == 2 || ignore.len() != 0 {
|
||||||
Ok(teams)
|
Ok(teams)
|
||||||
} else {
|
} else {
|
||||||
Err(error::LogFileError::TeamCount(teams.len()))
|
Err(error::TeamsError::NumberFound(teams.len()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all of the terrain deltas of a team.
|
|
||||||
pub fn deltas(&self, team: Team) -> Vec<i8> {
|
pub fn deltas(&self, team: Team) -> Vec<i8> {
|
||||||
let events: Vec<Event> = self
|
let events: Vec<Event> = self
|
||||||
.team_events(team)
|
.team_events(team)
|
||||||
.unwrap_or(TeamEvents(vec![]))
|
|
||||||
.0
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|event| {
|
.filter_map(|event| {
|
||||||
if let Event::Quarter(_) = event {
|
if let Event::Quarter(_) = event {
|
||||||
@@ -56,13 +53,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.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 idx: usize = 0;
|
||||||
let mut deltas: Vec<i8> = vec![];
|
let mut deltas: Vec<i8> = vec![];
|
||||||
|
|
||||||
@@ -77,55 +68,48 @@ impl Game {
|
|||||||
deltas
|
deltas
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all of the plays of a team.
|
pub fn team_plays(&self, team: Team) -> usize {
|
||||||
pub fn team_plays(&self, team: Team) -> TeamPlays {
|
self.team_events(team)
|
||||||
TeamPlays(
|
|
||||||
self.team_events(team)
|
|
||||||
.unwrap_or(TeamEvents(vec![]))
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.filter_map(|event| {
|
|
||||||
if let Event::Play(play) = event {
|
|
||||||
Some(play.to_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<Play>>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The average number of plays in a quarter.
|
|
||||||
pub fn avg_plays_per_quarter(&self, team: Team) -> f64 {
|
|
||||||
let periods: Vec<Period> = Quarter::iter()
|
|
||||||
.filter_map(|quarter| self.get_period(quarter.to_owned()))
|
|
||||||
.to_owned()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let quarterly_avgs: Vec<f64> = periods
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|period| {
|
.filter_map(|event| {
|
||||||
if !period.is_overtime() {
|
if let Event::Play(_) = event {
|
||||||
Some(period.team_plays(team.to_owned()) as f64)
|
Some(event)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<f64>>();
|
.collect::<Vec<&Event>>()
|
||||||
|
.len()
|
||||||
quarterly_avgs.iter().sum::<f64>() / quarterly_avgs.len() as f64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the average delta of a team.
|
/// The average number of plays in a quarter.
|
||||||
pub fn avg_delta(&self, team: Team) -> f64 {
|
pub fn avg_plays_per_quarter(&self, team: Team) -> f32 {
|
||||||
|
let periods: Vec<Period> = Quarter::iter()
|
||||||
|
.filter_map(|quarter| Some(self.to_owned().get_period(quarter.to_owned())).to_owned())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let quarterly_avgs: Vec<f32> = periods
|
||||||
|
.iter()
|
||||||
|
.filter_map(|period| {
|
||||||
|
if !period.is_overtime() {
|
||||||
|
Some(period.team_plays(team.to_owned()) as f32)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<f32>>();
|
||||||
|
|
||||||
|
quarterly_avgs.iter().sum::<f32>() / quarterly_avgs.len() as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn avg_delta(&self, team: Team) -> f32 {
|
||||||
let deltas = self.deltas(team);
|
let deltas = self.deltas(team);
|
||||||
|
|
||||||
// Summation doesn't like directly returning f64 from i8.
|
// Summation doesn't like directly returning f32 from i8.
|
||||||
deltas.iter().sum::<i8>() as f64 / deltas.len() as f64
|
deltas.iter().sum::<i8>() as f32 / deltas.len() as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the average delta for a team's positive deltas.
|
pub fn avg_gain(&self, team: Team) -> f32 {
|
||||||
pub fn avg_gain(&self, team: Team) -> f64 {
|
|
||||||
let deltas: Vec<u8> = self
|
let deltas: Vec<u8> = self
|
||||||
.deltas(team)
|
.deltas(team)
|
||||||
.iter()
|
.iter()
|
||||||
@@ -138,12 +122,11 @@ impl Game {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Summation doesn't like directly returning f64 from u8.
|
// Summation doesn't like directly returning f32 from u8.
|
||||||
deltas.iter().sum::<u8>() as f64 / deltas.len() as f64
|
deltas.iter().sum::<u8>() as f32 / deltas.len() as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the average delta for a team's negative deltas.
|
pub fn avg_loss(&self, team: Team) -> f32 {
|
||||||
pub fn avg_loss(&self, team: Team) -> f64 {
|
|
||||||
let deltas: Vec<i8> = self
|
let deltas: Vec<i8> = self
|
||||||
.deltas(team)
|
.deltas(team)
|
||||||
.iter()
|
.iter()
|
||||||
@@ -156,14 +139,11 @@ impl Game {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
deltas.iter().sum::<i8>() as f64 / deltas.len() as f64
|
deltas.iter().sum::<i8>() as f32 / deltas.len() as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of penalties that a team experienced.
|
|
||||||
pub fn penalties(&self, team: Team) -> usize {
|
pub fn penalties(&self, team: Team) -> usize {
|
||||||
self.team_events(team)
|
self.team_events(team)
|
||||||
.unwrap_or(TeamEvents(vec![]))
|
|
||||||
.0
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|event| {
|
.filter_map(|event| {
|
||||||
if let Event::Penalty(_) = event {
|
if let Event::Penalty(_) = event {
|
||||||
@@ -176,13 +156,10 @@ impl Game {
|
|||||||
.len()
|
.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the requested quarter.
|
pub fn get_period(&self, quarter: Quarter) -> Period {
|
||||||
/// If there is no history logged for the requested quarter, returns `None`.
|
|
||||||
/// For example, if requesting an OT that doesn't exist.
|
|
||||||
pub fn get_period(&self, quarter: Quarter) -> Option<Period> {
|
|
||||||
let mut record = false;
|
let mut record = false;
|
||||||
|
|
||||||
let period = Period {
|
Period {
|
||||||
period: quarter.to_owned(),
|
period: quarter.to_owned(),
|
||||||
events: self
|
events: self
|
||||||
.events
|
.events
|
||||||
@@ -199,21 +176,10 @@ impl Game {
|
|||||||
None
|
None
|
||||||
})
|
})
|
||||||
.collect::<Vec<Event>>(),
|
.collect::<Vec<Event>>(),
|
||||||
};
|
|
||||||
|
|
||||||
if period.events.len() != 0 {
|
|
||||||
Some(period)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all events relevent to a team's deltas and score.
|
pub fn team_events(&self, team: Team) -> Vec<Event> {
|
||||||
pub fn team_events(&self, team: Team) -> Option<TeamEvents> {
|
|
||||||
if !self.teams().is_ok_and(|ok| ok.contains(&team)) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut events: Vec<Event> = vec![];
|
let mut events: Vec<Event> = vec![];
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
let mut record: bool = true;
|
let mut record: bool = true;
|
||||||
@@ -244,26 +210,10 @@ impl Game {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// If already handled or assumption override applicable
|
// If already handled or assumption override applicable
|
||||||
Some(TeamEvents(events))
|
events
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Game {
|
|
||||||
fn default() -> Self {
|
|
||||||
Game {
|
|
||||||
version: MIN_VER,
|
|
||||||
flags: vec![],
|
|
||||||
events: vec![Event::Quarter(Quarter::default())],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TeamEvents(pub Vec<Event>);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TeamPlays(pub Vec<Play>);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Period {
|
pub struct Period {
|
||||||
period: Quarter,
|
period: Quarter,
|
||||||
@@ -271,7 +221,6 @@ pub struct Period {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Period {
|
impl Period {
|
||||||
/// Returns all events relevent to a team's deltas and score.
|
|
||||||
pub fn team_events(&self, team: Team) -> Vec<Event> {
|
pub fn team_events(&self, team: Team) -> Vec<Event> {
|
||||||
let mut events: Vec<Event> = vec![];
|
let mut events: Vec<Event> = vec![];
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
@@ -305,7 +254,6 @@ impl Period {
|
|||||||
events
|
events
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all of the plays of a team.
|
|
||||||
pub fn team_plays(&self, team: Team) -> usize {
|
pub fn team_plays(&self, team: Team) -> usize {
|
||||||
self.team_events(team)
|
self.team_events(team)
|
||||||
.iter()
|
.iter()
|
||||||
@@ -320,7 +268,6 @@ impl Period {
|
|||||||
.len()
|
.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the current period is overtime.
|
|
||||||
pub fn is_overtime(&self) -> bool {
|
pub fn is_overtime(&self) -> bool {
|
||||||
if let Quarter::Overtime(_) = self.period {
|
if let Quarter::Overtime(_) = self.period {
|
||||||
true
|
true
|
||||||
@@ -332,17 +279,14 @@ impl Period {
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
pub enum Flags {
|
pub enum Flags {
|
||||||
ClockBleed(Team),
|
|
||||||
Gameplan(u8),
|
|
||||||
IgnoreActions,
|
IgnoreActions,
|
||||||
IgnoreTeam(Team),
|
IgnoreTeam(Team),
|
||||||
IgnoreScore,
|
IgnoreScore,
|
||||||
Interval(u8),
|
Interval(u8),
|
||||||
LowDataCredibility,
|
SheerDumbFuckingLuck
|
||||||
SheerDumbFuckingLuck,
|
|
||||||
Stub,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
@@ -350,6 +294,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn avg_plays_per_quarter() {
|
fn avg_plays_per_quarter() {
|
||||||
let a = Game {
|
let a = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
|
flags: vec![],
|
||||||
events: vec![
|
events: vec![
|
||||||
Event::Quarter(Quarter::First),
|
Event::Quarter(Quarter::First),
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
@@ -365,103 +311,201 @@ mod tests {
|
|||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
Event::Turnover(Team::ArizonaState),
|
Event::Turnover(Team::ArizonaState),
|
||||||
],
|
],
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let b = Game {
|
let b = Game {
|
||||||
events: vec![
|
version: crate::MIN_VER,
|
||||||
Event::Quarter(Quarter::First),
|
flags: vec![],
|
||||||
Event::Turnover(Team::Nebraska),
|
periods: vec![Period {
|
||||||
Event::Play(Play::default()),
|
start: Quarter::Second,
|
||||||
Event::Turnover(Team::ArizonaState),
|
end: Some(Quarter::Fourth),
|
||||||
],
|
events: vec![
|
||||||
..Default::default()
|
Event::Turnover(Team::Nebraska),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
],
|
||||||
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
dbg!(a.avg_plays_per_quarter(Team::Nebraska));
|
assert!(a.avg_plays_per_quarter(Team::Nebraska) == ((1_f32 + 2_f32) / 2_f32));
|
||||||
dbg!(b.avg_plays_per_quarter(Team::Nebraska));
|
assert!(b.avg_plays_per_quarter(Team::Nebraska) == (1_f32 / 3_f32))
|
||||||
|
}
|
||||||
|
|
||||||
assert!(a.avg_plays_per_quarter(Team::Nebraska) == ((1_f64 + 6_f64) / 2_f64));
|
#[test]
|
||||||
assert!(b.avg_plays_per_quarter(Team::Nebraska) == 1_f64)
|
fn team_plays() {
|
||||||
|
let a = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
|
flags: vec![],
|
||||||
|
periods: vec![
|
||||||
|
Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: Some(Quarter::Fourth),
|
||||||
|
events: vec![
|
||||||
|
Event::Turnover(Team::Nebraska),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(a.team_plays(Team::Nebraska) == 12_usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
fn teams() {
|
fn teams() {
|
||||||
let a = Game {
|
let a = Game {
|
||||||
events: vec![
|
version: crate::MIN_VER,
|
||||||
Event::Kickoff(Team::Nebraska),
|
flags: vec![],
|
||||||
Event::Turnover(Team::ArizonaState),
|
periods: vec![
|
||||||
Event::Kickoff(Team::Nebraska),
|
Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![Event::Kickoff(Team::Nebraska)],
|
||||||
|
},
|
||||||
|
Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: Some(Quarter::Fourth),
|
||||||
|
events: vec![
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let b = Game {
|
let b = Game {
|
||||||
events: vec![
|
version: crate::MIN_VER,
|
||||||
Event::Kickoff(Team::Nebraska),
|
flags: vec![],
|
||||||
Event::Turnover(Team::ArizonaState),
|
periods: vec![
|
||||||
Event::Kickoff(Team::BoiseState),
|
Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![Event::Kickoff(Team::Nebraska)],
|
||||||
|
},
|
||||||
|
Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: Some(Quarter::Fourth),
|
||||||
|
events: vec![
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
Event::Kickoff(Team::BoiseState),
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let c = Game {
|
let c = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
flags: vec![Flags::IgnoreTeam(Team::Nebraska)],
|
flags: vec![Flags::IgnoreTeam(Team::Nebraska)],
|
||||||
events: vec![Event::Kickoff(Team::Nebraska)],
|
periods: vec![
|
||||||
..Default::default()
|
Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![Event::Kickoff(Team::Nebraska)],
|
||||||
|
},
|
||||||
|
Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: Some(Quarter::Fourth),
|
||||||
|
events: vec![
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
|
flags: vec![Flags::IgnoreTeam(Team::Nebraska)],
|
||||||
|
periods: vec![Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![Event::Kickoff(Team::Nebraska)],
|
||||||
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(a.teams().unwrap() == vec![Team::Nebraska, Team::ArizonaState]);
|
assert!(a.teams().unwrap() == vec![Team::Nebraska, Team::ArizonaState]);
|
||||||
assert!(b.teams().is_err() == true);
|
assert!(b.teams().is_err() == true);
|
||||||
assert!(c.teams().unwrap() == vec![]);
|
assert!(c.teams().unwrap() == vec![Team::ArizonaState]);
|
||||||
|
assert!(d.teams().unwrap() == vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deltas() {
|
fn deltas() {
|
||||||
let game = Game {
|
let game = Game {
|
||||||
events: vec![
|
version: crate::MIN_VER,
|
||||||
Event::Kickoff(Team::Nebraska),
|
flags: vec![],
|
||||||
Event::Play(Play {
|
periods: vec![
|
||||||
action: Action::Unknown,
|
Period {
|
||||||
down: Some(Down::First),
|
start: Quarter::First,
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
end: None,
|
||||||
}),
|
events: vec![
|
||||||
Event::Play(Play {
|
Event::Kickoff(Team::Nebraska),
|
||||||
action: Action::Unknown,
|
Event::Play(Play {
|
||||||
down: Some(Down::Second),
|
action: Action::Unknown,
|
||||||
terrain: Some(TerrainState::Yards(13)),
|
down: Some(Down::First),
|
||||||
}),
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
Event::Play(Play {
|
}),
|
||||||
action: Action::Unknown,
|
Event::Play(Play {
|
||||||
down: Some(Down::Third),
|
action: Action::Unknown,
|
||||||
terrain: Some(TerrainState::Yards(8)),
|
down: Some(Down::Second),
|
||||||
}),
|
terrain: Some(TerrainState::Yards(13)),
|
||||||
Event::Turnover(Team::ArizonaState),
|
}),
|
||||||
Event::Play(Play {
|
Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::First),
|
down: Some(Down::Third),
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
terrain: Some(TerrainState::Yards(8)),
|
||||||
}),
|
}),
|
||||||
Event::Play(Play {
|
Event::Turnover(Team::ArizonaState),
|
||||||
action: Action::Unknown,
|
Event::Play(Play {
|
||||||
down: Some(Down::Second),
|
action: Action::Unknown,
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
down: Some(Down::First),
|
||||||
}),
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
Event::Turnover(Team::Nebraska),
|
}),
|
||||||
Event::Play(Play {
|
Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::Second),
|
down: Some(Down::Second),
|
||||||
terrain: Some(TerrainState::Yards(12)),
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
}),
|
}),
|
||||||
Event::Play(Play {
|
Event::Turnover(Team::Nebraska),
|
||||||
action: Action::Unknown,
|
Event::Play(Play {
|
||||||
down: Some(Down::First),
|
action: Action::Unknown,
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
down: Some(Down::Second),
|
||||||
}),
|
terrain: Some(TerrainState::Yards(12)),
|
||||||
Event::Turnover(Team::ArizonaState),
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: None,
|
||||||
|
events: vec![
|
||||||
|
Event::Play(Play {
|
||||||
|
action: Action::Unknown,
|
||||||
|
down: Some(Down::First),
|
||||||
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
|
}),
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(game.deltas(Team::Nebraska) == vec![10_i8, -3_i8, 5_i8, -2_i8, 12_i8]);
|
assert!(game.deltas(Team::Nebraska) == vec![10_i8, -3_i8, 5_i8, -2_i8, 12_i8]);
|
||||||
@@ -469,39 +513,76 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(deprecated)]
|
|
||||||
fn team_events() {
|
fn team_events() {
|
||||||
let a = Game {
|
let a = Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
events: vec![
|
events: vec![
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
Event::Turnover(Team::SouthCarolina),
|
Event::Turnover(Team::ArizonaState),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Score(ScorePoints::Touchdown),
|
Event::Score(ScorePoints::Touchdown),
|
||||||
Event::Kickoff(Team::SouthCarolina),
|
Event::Kickoff(Team::SouthCarolina),
|
||||||
],
|
],
|
||||||
..Default::default()
|
};
|
||||||
|
|
||||||
|
let b = Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: None,
|
||||||
|
events: vec![
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::SouthCarolina),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let c = Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: None,
|
||||||
|
events: vec![
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::Nebraska),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: None,
|
||||||
|
events: vec![Event::Play(Play::default())],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
a.team_events(Team::Nebraska).unwrap().0
|
a.team_events(Team::Nebraska, None).unwrap()
|
||||||
== vec![
|
== vec![
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
Event::Turnover(Team::SouthCarolina),
|
Event::Turnover(Team::ArizonaState),
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Score(ScorePoints::Touchdown),
|
Event::Score(ScorePoints::Touchdown),
|
||||||
Event::Kickoff(Team::SouthCarolina),
|
Event::Kickoff(Team::SouthCarolina),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert!(a.team_events(Team::BoiseState).is_none());
|
assert!(
|
||||||
|
b.team_events(Team::Nebraska, None).unwrap()
|
||||||
|
== vec![
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::SouthCarolina)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
c.team_events(Team::Nebraska, None).unwrap() == vec![Event::Turnover(Team::Nebraska)]
|
||||||
|
);
|
||||||
|
assert!(true == d.team_events(Team::Nebraska, None).is_err());
|
||||||
|
assert!(false == d.team_events(Team::Nebraska, Some(true)).is_err())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn team_plays() {
|
fn team_plays() {
|
||||||
let game = Game {
|
let period = Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
events: vec![
|
events: vec![
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
@@ -511,17 +592,65 @@ mod tests {
|
|||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
Event::Score(ScorePoints::default()),
|
Event::Score(ScorePoints::default()),
|
||||||
Event::Kickoff(Team::ArizonaState),
|
Event::Kickoff(Team::SouthCarolina),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
Event::Turnover(Team::Nebraska),
|
Event::Turnover(Team::Nebraska),
|
||||||
Event::Play(Play::default()),
|
Event::Play(Play::default()),
|
||||||
],
|
],
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
game.team_plays(Team::Nebraska).0
|
period.team_plays(Team::Nebraska, None).unwrap()
|
||||||
== vec![Play::default(), Play::default(), Play::default()]
|
== vec![Play::default(), Play::default(), Play::default()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quarters() {
|
||||||
|
let first = Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let second_fourth = Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: Some(Quarter::Fourth),
|
||||||
|
events: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let third_ot_three = Period {
|
||||||
|
start: Quarter::Third,
|
||||||
|
end: Some(Quarter::Overtime(3)),
|
||||||
|
events: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let ot_one_three = Period {
|
||||||
|
start: Quarter::Overtime(1),
|
||||||
|
end: Some(Quarter::Overtime(3)),
|
||||||
|
events: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(first.quarters() == vec![Quarter::First]);
|
||||||
|
assert!(second_fourth.quarters() == vec![Quarter::Second, Quarter::Third, Quarter::Fourth]);
|
||||||
|
assert!(
|
||||||
|
third_ot_three.quarters()
|
||||||
|
== vec![
|
||||||
|
Quarter::Third,
|
||||||
|
Quarter::Fourth,
|
||||||
|
Quarter::Overtime(1),
|
||||||
|
Quarter::Overtime(2),
|
||||||
|
Quarter::Overtime(3)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
ot_one_three.quarters()
|
||||||
|
== vec![
|
||||||
|
Quarter::Overtime(1),
|
||||||
|
Quarter::Overtime(2),
|
||||||
|
Quarter::Overtime(3)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use strum::EnumIter;
|
use strum::EnumIter;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, EnumIter, Default)]
|
#[derive(Debug, Deserialize, Clone, PartialEq, EnumIter)]
|
||||||
pub enum Quarter {
|
pub enum Quarter {
|
||||||
#[default]
|
|
||||||
First,
|
First,
|
||||||
Second,
|
Second,
|
||||||
Third,
|
Third,
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, Default, PartialEq)]
|
||||||
pub enum TerrainState {
|
pub enum TerrainState {
|
||||||
Yards(u8),
|
Yards(u8),
|
||||||
GoalLine,
|
GoalLine,
|
||||||
Inches,
|
Inches,
|
||||||
}
|
#[default]
|
||||||
|
Unknown,
|
||||||
impl Default for TerrainState {
|
|
||||||
fn default() -> Self {
|
|
||||||
TerrainState::Yards(10)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
35
miller/Cargo.lock
generated
35
miller/Cargo.lock
generated
@@ -58,9 +58,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.37"
|
version = "4.5.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -68,9 +68,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.37"
|
version = "4.5.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
@@ -205,7 +205,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gamelog"
|
name = "gamelog"
|
||||||
version = "0.7.3"
|
version = "0.7.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ron",
|
"ron",
|
||||||
"semver",
|
"semver",
|
||||||
@@ -215,9 +215,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.3"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"equivalent",
|
"equivalent",
|
||||||
@@ -272,9 +272,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.172"
|
version = "0.2.171"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
@@ -314,7 +314,6 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"gamelog",
|
"gamelog",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"strum 0.27.1",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -360,9 +359,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -408,9 +407,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ron"
|
name = "ron"
|
||||||
version = "0.10.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f"
|
checksum = "63f3aa105dea217ef30d89581b65a4d527a19afc95ef5750be3890e8d3c5b837"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
@@ -502,9 +501,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.5"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -573,9 +572,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ license = "MIT"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ratatui = "0.29"
|
ratatui = "0.29"
|
||||||
strum = "0.27"
|
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "4.5"
|
version = "4.5"
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ mod tui;
|
|||||||
|
|
||||||
use clap::{ArgAction, Parser};
|
use clap::{ArgAction, Parser};
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use gamelog::{Action, Down, Flags, Key, LogFile, Team, TerrainState};
|
use gamelog::{Action, Down, Flags, Key, LogFile, Team};
|
||||||
use std::{io, path::PathBuf, sync::mpsc, thread};
|
use std::{io, path::PathBuf, sync::mpsc, thread};
|
||||||
//use strum::IntoEnumIterator;
|
|
||||||
use tui::App;
|
use tui::App;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
@@ -43,15 +42,17 @@ fn main() -> io::Result<()> {
|
|||||||
TeamStats::new(Team::Colorado),
|
TeamStats::new(Team::Colorado),
|
||||||
TeamStats::new(Team::Iowa),
|
TeamStats::new(Team::Iowa),
|
||||||
TeamStats::new(Team::Nebraska),
|
TeamStats::new(Team::Nebraska),
|
||||||
#[allow(deprecated)]
|
|
||||||
TeamStats::new(Team::SouthCarolina),
|
|
||||||
TeamStats::new(Team::Syracuse),
|
TeamStats::new(Team::Syracuse),
|
||||||
|
TeamStats::new(Team::SouthCarolina),
|
||||||
TeamStats::new(Team::TexasAnM),
|
TeamStats::new(Team::TexasAnM),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Work on knocking down the nesting here?
|
// Work on knocking down the nesting here?
|
||||||
for game in log.0.iter() {
|
for game in log.0.iter() {
|
||||||
let teams = game.teams().unwrap();
|
let teams = match game.teams() {
|
||||||
|
Ok(teams) => teams,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
for team in teams {
|
for team in teams {
|
||||||
// Skip team if they are to be ignored this game.
|
// Skip team if they are to be ignored this game.
|
||||||
@@ -82,30 +83,12 @@ fn main() -> io::Result<()> {
|
|||||||
|
|
||||||
stats[team_idx]
|
stats[team_idx]
|
||||||
.plays_per_game
|
.plays_per_game
|
||||||
.push(game.team_plays(team.to_owned()).0.len());
|
.push(game.team_plays(team.to_owned()));
|
||||||
|
|
||||||
stats[team_idx]
|
stats[team_idx]
|
||||||
.penalties_per_game
|
.penalties_per_game
|
||||||
.push(game.penalties(team.to_owned()));
|
.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()));
|
|
||||||
|
|
||||||
//stats[team_idx].play_frequencies = Some(log.frequency_of_plays(team.to_owned()))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// :#? for pretty-printing.
|
// :#? for pretty-printing.
|
||||||
@@ -136,21 +119,22 @@ fn main() -> io::Result<()> {
|
|||||||
struct TeamStats {
|
struct TeamStats {
|
||||||
team: gamelog::Team,
|
team: gamelog::Team,
|
||||||
// Terrain
|
// Terrain
|
||||||
avg_terrain_gain: Vec<f64>,
|
avg_terrain_gain: Vec<f32>,
|
||||||
avg_terrain_loss: Vec<f64>,
|
avg_terrain_loss: Vec<f32>,
|
||||||
avg_terrain_delta: Vec<f64>,
|
avg_terrain_delta: Vec<f32>,
|
||||||
// Play rate
|
// Play rate
|
||||||
plays_per_quarter: Vec<f64>,
|
plays_per_quarter: Vec<f32>,
|
||||||
plays_per_game: Vec<usize>,
|
plays_per_game: Vec<usize>,
|
||||||
// Penalties
|
// Penalties
|
||||||
penalties_per_game: Vec<usize>,
|
penalties_per_game: Vec<usize>,
|
||||||
|
// Score
|
||||||
|
points_per_quarter: Vec<u8>,
|
||||||
|
points_per_game: Vec<u8>,
|
||||||
// Biases
|
// Biases
|
||||||
most_common_play: Option<(Action, usize)>,
|
most_common_play: Option<Action>,
|
||||||
least_common_play: Option<(Action, usize)>,
|
least_common_play: Option<Action>,
|
||||||
most_common_key: Option<Key>,
|
most_common_key: Option<Key>,
|
||||||
least_common_key: Option<Key>,
|
least_common_key: Option<Key>,
|
||||||
most_effective_play: Option<(Action, TerrainState)>,
|
|
||||||
play_frequencies: Option<Vec<(Action, usize)>>,
|
|
||||||
// Traits
|
// Traits
|
||||||
// Typical number of downs to achieve 10 yards.
|
// Typical number of downs to achieve 10 yards.
|
||||||
time_to_first_down: Option<Down>,
|
time_to_first_down: Option<Down>,
|
||||||
@@ -166,12 +150,12 @@ impl TeamStats {
|
|||||||
plays_per_quarter: vec![],
|
plays_per_quarter: vec![],
|
||||||
plays_per_game: vec![],
|
plays_per_game: vec![],
|
||||||
penalties_per_game: vec![],
|
penalties_per_game: vec![],
|
||||||
|
points_per_quarter: vec![],
|
||||||
|
points_per_game: vec![],
|
||||||
most_common_play: None,
|
most_common_play: None,
|
||||||
least_common_play: None,
|
least_common_play: None,
|
||||||
most_common_key: None,
|
most_common_key: None,
|
||||||
least_common_key: None,
|
least_common_key: None,
|
||||||
most_effective_play: None,
|
|
||||||
play_frequencies: None,
|
|
||||||
time_to_first_down: None,
|
time_to_first_down: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,22 @@
|
|||||||
|
|
||||||
[
|
[
|
||||||
Game(
|
Game(
|
||||||
version: "0.7.0",
|
version: "0.5.0",
|
||||||
flags: [],
|
flags: [],
|
||||||
events: [
|
periods: [
|
||||||
Quarter(First),
|
Period(
|
||||||
Kickoff(Nebraska),
|
start: First,
|
||||||
Play(
|
end: Fourth,
|
||||||
action: Unknown,
|
events: [
|
||||||
down: Second,
|
Kickoff(Nebraska),
|
||||||
terrain: Yards(10),
|
Play(
|
||||||
),
|
action: Unknown,
|
||||||
|
down: First,
|
||||||
|
terrain: Yards(10)
|
||||||
|
),
|
||||||
|
Score(FieldGoal),
|
||||||
|
]
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
52
zed_settings.jsonc
Normal file
52
zed_settings.jsonc
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Zed settings
|
||||||
|
//
|
||||||
|
// For information on how to configure Zed, see the Zed
|
||||||
|
// documentation: https://zed.dev/docs/configuring-zed
|
||||||
|
//
|
||||||
|
// To see all of Zed's default settings without changing your
|
||||||
|
// custom settings, run `zed: open default settings` from the
|
||||||
|
// command palette (cmd-shift-p / ctrl-shift-p)
|
||||||
|
{
|
||||||
|
"telemetry": {
|
||||||
|
"diagnostics": true,
|
||||||
|
"metrics": false,
|
||||||
|
},
|
||||||
|
"ui_font_size": 16,
|
||||||
|
"buffer_font_size": 13,
|
||||||
|
"icon_theme": "Colored Zed Icons Theme Dark",
|
||||||
|
"theme": {
|
||||||
|
"mode": "system",
|
||||||
|
"light": "One Light",
|
||||||
|
"dark": "One Dark",
|
||||||
|
},
|
||||||
|
"restore_on_startup": "last_workspace",
|
||||||
|
"soft_wrap": "preferred_line_length",
|
||||||
|
|
||||||
|
// Remove AI Crap
|
||||||
|
"features": {
|
||||||
|
"edit_prediction_provider": "none",
|
||||||
|
"copilot": false,
|
||||||
|
},
|
||||||
|
"assistant": {
|
||||||
|
"version": "1",
|
||||||
|
"enabled": false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Language Overrides
|
||||||
|
"languages": {
|
||||||
|
"Python": {
|
||||||
|
"show_wrap_guides": true,
|
||||||
|
"preferred_line_length": 80,
|
||||||
|
},
|
||||||
|
"Rust": {
|
||||||
|
"show_wrap_guides": true,
|
||||||
|
"preferred_line_length": 100,
|
||||||
|
},
|
||||||
|
"JSON": {
|
||||||
|
"tab_size": 4,
|
||||||
|
},
|
||||||
|
"JSONC": {
|
||||||
|
"tab_size": 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user