Implement PPQ Calculation [per team per game] and housekeeping.
This commit is contained in:
69
gamelog.ron
69
gamelog.ron
@@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
[
|
[
|
||||||
Game(
|
Game(
|
||||||
version: "0.3.0",
|
version: "0.5.0",
|
||||||
|
flags: [IgnoreScore],
|
||||||
periods: [
|
periods: [
|
||||||
Period(
|
Period(
|
||||||
start: First,
|
start: First,
|
||||||
end: Third,
|
end: Third,
|
||||||
plays: [
|
events: [
|
||||||
Kickoff(ArizonaState),
|
Kickoff(ArizonaState),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown,
|
action: Unknown,
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
terrain: Yards(7),
|
terrain: Yards(7),
|
||||||
),
|
),
|
||||||
Play(
|
Play(
|
||||||
action: HalfbackSweep
|
action: HalfbackSweep,
|
||||||
down: Fourth,
|
down: Fourth,
|
||||||
terrain: Yards(11),
|
terrain: Yards(11),
|
||||||
),
|
),
|
||||||
@@ -130,7 +131,7 @@
|
|||||||
Period(
|
Period(
|
||||||
start: Fourth,
|
start: Fourth,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Play(
|
Play(
|
||||||
action: PlayActionComebacks, // Original note: Dupe Spike Centre
|
action: PlayActionComebacks, // Original note: Dupe Spike Centre
|
||||||
down: Fourth,
|
down: Fourth,
|
||||||
@@ -182,12 +183,13 @@
|
|||||||
]
|
]
|
||||||
),
|
),
|
||||||
Game(
|
Game(
|
||||||
version: "0.3.1",
|
version: "0.5.0",
|
||||||
|
flags: [IgnoreScore],
|
||||||
periods: [
|
periods: [
|
||||||
Period(
|
Period(
|
||||||
start: First,
|
start: First,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Kickoff(Syracuse),
|
Kickoff(Syracuse),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown,
|
action: Unknown,
|
||||||
@@ -224,7 +226,7 @@
|
|||||||
Period(
|
Period(
|
||||||
start: Second,
|
start: Second,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Play(
|
Play(
|
||||||
action: Unknown, // Original note: PA Throw Centre
|
action: Unknown, // Original note: PA Throw Centre
|
||||||
down: Second,
|
down: Second,
|
||||||
@@ -254,7 +256,7 @@
|
|||||||
action: Unknown, // HalfbackSlam? Original note: PA Rush Centre
|
action: Unknown, // HalfbackSlam? Original note: PA Rush Centre
|
||||||
down: None,
|
down: None,
|
||||||
terrain: None,
|
terrain: None,
|
||||||
)
|
),
|
||||||
Turnover(Colorado),
|
Turnover(Colorado),
|
||||||
Play(
|
Play(
|
||||||
action: PlayActionComebacks,
|
action: PlayActionComebacks,
|
||||||
@@ -281,7 +283,7 @@
|
|||||||
Period(
|
Period(
|
||||||
start: Third,
|
start: Third,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Kickoff(Colorado),
|
Kickoff(Colorado),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown, // Original note: Throw Right
|
action: Unknown, // Original note: Throw Right
|
||||||
@@ -308,7 +310,7 @@
|
|||||||
Period(
|
Period(
|
||||||
start: Fourth,
|
start: Fourth,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Play(
|
Play(
|
||||||
action: CrackStudentBodyRightTackle,
|
action: CrackStudentBodyRightTackle,
|
||||||
down: Third,
|
down: Third,
|
||||||
@@ -319,7 +321,6 @@
|
|||||||
down: Fourth,
|
down: Fourth,
|
||||||
terrain: Yards(3),
|
terrain: Yards(3),
|
||||||
),
|
),
|
||||||
Score(0),
|
|
||||||
Kickoff(Syracuse),
|
Kickoff(Syracuse),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown, // Original note: PA Throw Centre
|
action: Unknown, // Original note: PA Throw Centre
|
||||||
@@ -336,18 +337,19 @@
|
|||||||
]
|
]
|
||||||
),
|
),
|
||||||
Game(
|
Game(
|
||||||
version: "0.3.1",
|
version: "0.5.0",
|
||||||
|
flags: [],
|
||||||
periods: [
|
periods: [
|
||||||
Period(
|
Period(
|
||||||
start: First,
|
start: First,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Kickoff(Nebraska),
|
Kickoff(Nebraska),
|
||||||
Play(
|
Play(
|
||||||
action: Curls,
|
action: Curls,
|
||||||
down: First,
|
down: First,
|
||||||
terrain: Yards(10)
|
terrain: Yards(10)
|
||||||
)
|
),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown,
|
action: Unknown,
|
||||||
down: Second,
|
down: Second,
|
||||||
@@ -379,14 +381,14 @@
|
|||||||
Period(
|
Period(
|
||||||
start: Second,
|
start: Second,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Play(
|
Play(
|
||||||
action: Unknown, // Original note: Throw Centre
|
action: Unknown, // Original note: Throw Centre
|
||||||
down: First,
|
down: First,
|
||||||
terrain: GoalLine,
|
terrain: GoalLine,
|
||||||
),
|
),
|
||||||
Pat(Fail),
|
Score(Touchdown),
|
||||||
Score(6),
|
Score(PatFail),
|
||||||
Kickoff(Nebraska),
|
Kickoff(Nebraska),
|
||||||
Play(
|
Play(
|
||||||
action: StrongFlood,
|
action: StrongFlood,
|
||||||
@@ -429,7 +431,7 @@
|
|||||||
Period(
|
Period(
|
||||||
start: Third,
|
start: Third,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Kickoff(SouthCarolina),
|
Kickoff(SouthCarolina),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown,
|
action: Unknown,
|
||||||
@@ -452,7 +454,7 @@
|
|||||||
down: None,
|
down: None,
|
||||||
terrain: None,
|
terrain: None,
|
||||||
),
|
),
|
||||||
Score(3),
|
Score(FieldGoal),
|
||||||
Kickoff(SouthCarolina),
|
Kickoff(SouthCarolina),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown,
|
action: Unknown,
|
||||||
@@ -469,7 +471,7 @@
|
|||||||
Period(
|
Period(
|
||||||
start: Fourth,
|
start: Fourth,
|
||||||
end: None,
|
end: None,
|
||||||
plays: [
|
events: [
|
||||||
Play(
|
Play(
|
||||||
action: Unknown, // Original note: Throw Centre
|
action: Unknown, // Original note: Throw Centre
|
||||||
down: Fourth,
|
down: Fourth,
|
||||||
@@ -490,8 +492,8 @@
|
|||||||
down: Third,
|
down: Third,
|
||||||
terrain: Yards(5),
|
terrain: Yards(5),
|
||||||
),
|
),
|
||||||
Pat(), // Original note: Dupe, Throw Centre
|
Score(Touchdown),
|
||||||
Score(12),
|
Score(PatFail),
|
||||||
Kickoff(Nebraska),
|
Kickoff(Nebraska),
|
||||||
Play(
|
Play(
|
||||||
action: PowerZero,
|
action: PowerZero,
|
||||||
@@ -536,7 +538,7 @@
|
|||||||
),
|
),
|
||||||
Turnover(Nebraska),
|
Turnover(Nebraska),
|
||||||
// Field Goal 41 yrds
|
// Field Goal 41 yrds
|
||||||
Score(6),
|
Score(FieldGoal),
|
||||||
Kickoff(SouthCarolina),
|
Kickoff(SouthCarolina),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown,
|
action: Unknown,
|
||||||
@@ -548,7 +550,8 @@
|
|||||||
]
|
]
|
||||||
),
|
),
|
||||||
Game(
|
Game(
|
||||||
version: "0.4.0",
|
version: "0.5.0",
|
||||||
|
flags: [],
|
||||||
periods: [
|
periods: [
|
||||||
Period(
|
Period(
|
||||||
start: First,
|
start: First,
|
||||||
@@ -617,8 +620,8 @@
|
|||||||
terrain: GoalLine,
|
terrain: GoalLine,
|
||||||
),
|
),
|
||||||
// Touchdown
|
// Touchdown
|
||||||
Pat(One), // Throw
|
Score(Touchdown),
|
||||||
Score(7),
|
Score(PatSafety),
|
||||||
Kickoff(Iowa),
|
Kickoff(Iowa),
|
||||||
Penalty(Yards(15)),
|
Penalty(Yards(15)),
|
||||||
Play(
|
Play(
|
||||||
@@ -656,7 +659,7 @@
|
|||||||
down: First,
|
down: First,
|
||||||
terrain: GoalLine,
|
terrain: GoalLine,
|
||||||
),
|
),
|
||||||
Score(3),
|
Score(FieldGoal),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
Period(
|
Period(
|
||||||
@@ -716,8 +719,8 @@
|
|||||||
terrain: Yards(4),
|
terrain: Yards(4),
|
||||||
),
|
),
|
||||||
// Touchdown
|
// Touchdown
|
||||||
Pat(One)
|
Score(Touchdown),
|
||||||
Score(7),
|
Score(PatSafety),
|
||||||
Kickoff(Colorado),
|
Kickoff(Colorado),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown, // Original note: Dupe, Throw Left
|
action: Unknown, // Original note: Dupe, Throw Left
|
||||||
@@ -752,13 +755,14 @@
|
|||||||
terrain: Yards(15),
|
terrain: Yards(15),
|
||||||
),
|
),
|
||||||
//Field Goal
|
//Field Goal
|
||||||
Score(10)
|
Score(FieldGoal)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
Game(
|
Game(
|
||||||
version: "0.5.0",
|
version: "0.5.0",
|
||||||
|
flags: [],
|
||||||
periods: [
|
periods: [
|
||||||
Period(
|
Period(
|
||||||
start: First,
|
start: First,
|
||||||
@@ -878,7 +882,7 @@
|
|||||||
terrain:Yards(10),
|
terrain:Yards(10),
|
||||||
),
|
),
|
||||||
Play(
|
Play(
|
||||||
action: //IForm Normal, Thrown
|
action: Unknown,//IForm Normal, Thrown
|
||||||
down: None,
|
down: None,
|
||||||
terrain: None
|
terrain: None
|
||||||
),
|
),
|
||||||
@@ -985,6 +989,7 @@
|
|||||||
// TexasAnM were opponents, but not recorded as
|
// TexasAnM were opponents, but not recorded as
|
||||||
// they were not present; Miller played in place.
|
// they were not present; Miller played in place.
|
||||||
version: "0.5.0",
|
version: "0.5.0",
|
||||||
|
flags: [IgnoreTeam(TexasAnM)],
|
||||||
periods: [
|
periods: [
|
||||||
Period(
|
Period(
|
||||||
start: First,
|
start: First,
|
||||||
@@ -1053,7 +1058,7 @@
|
|||||||
terrain: Yards(13)
|
terrain: Yards(13)
|
||||||
),
|
),
|
||||||
Play(
|
Play(
|
||||||
action: First,
|
action: Unknown,
|
||||||
down: First,
|
down: First,
|
||||||
terrain: Yards(10)
|
terrain: Yards(10)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -27,3 +27,21 @@ impl fmt::Display for TeamsError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NoTeamAttribute;
|
||||||
|
|
||||||
|
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,9 +1,9 @@
|
|||||||
use crate::{Down, Play, Team, TerrainState};
|
use crate::{Down, Play, Team, TerrainState, error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
type Offence = Team;
|
type Offence = Team;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Play(Play),
|
Play(Play),
|
||||||
Kickoff(Offence),
|
Kickoff(Offence),
|
||||||
@@ -18,7 +18,7 @@ impl Event {
|
|||||||
|
|
||||||
fn make_play(event: &Event) -> Option<Play> {
|
fn make_play(event: &Event) -> Option<Play> {
|
||||||
match event {
|
match event {
|
||||||
Event::Kickoff(_) => Some(Play::default()),
|
Event::Kickoff(_) | Event::Turnover(_) => Some(Play::default()),
|
||||||
Event::Play(play) => {
|
Event::Play(play) => {
|
||||||
let p = play.to_owned();
|
let p = play.to_owned();
|
||||||
|
|
||||||
@@ -36,7 +36,13 @@ impl Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let preceeding = make_play(self)?;
|
let preceeding = make_play(self)?;
|
||||||
let following = make_play(following)?;
|
let following = if let Event::Turnover(_) = following {
|
||||||
|
// I should really just early return
|
||||||
|
// but this is too funny to look at.
|
||||||
|
None?
|
||||||
|
} else {
|
||||||
|
make_play(following)?
|
||||||
|
};
|
||||||
|
|
||||||
if following.down? == Down::First {
|
if following.down? == Down::First {
|
||||||
if let TerrainState::Yards(yrds) = preceeding.terrain? {
|
if let TerrainState::Yards(yrds) = preceeding.terrain? {
|
||||||
@@ -60,6 +66,14 @@ impl Event {
|
|||||||
Some(a as i8 - b as i8)
|
Some(a as i8 - b as i8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn team(&self) -> Result<Team, error::NoTeamAttribute> {
|
||||||
|
match self {
|
||||||
|
Self::Kickoff(team) => Ok(team.to_owned()),
|
||||||
|
Self::Turnover(team) => Ok(team.to_owned()),
|
||||||
|
_ => Err(error::NoTeamAttribute),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, Default)]
|
#[derive(Debug, Deserialize, Clone, PartialEq, Default)]
|
||||||
@@ -90,45 +104,54 @@ impl ScorePoints {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use crate::*;
|
||||||
use crate::{Action, Down, Team, TerrainState};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta() {
|
fn delta() {
|
||||||
let kickoff = Event::Kickoff(Team::Nebraska);
|
let kickoff = Event::Kickoff(Team::Nebraska);
|
||||||
|
|
||||||
let first_down = Event::Play(Play {
|
let first_down = Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::First),
|
down: Some(Down::First),
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let second_down = Event::Play(Play {
|
let second_down = Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::Second),
|
down: Some(Down::Second),
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let third_down = Event::Play(Play {
|
let third_down = Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::Third),
|
down: Some(Down::Third),
|
||||||
terrain: Some(TerrainState::Yards(13)),
|
terrain: Some(TerrainState::Yards(13)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let fourth_down = Event::Play(Play {
|
let fourth_down = Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::Fourth),
|
down: Some(Down::Fourth),
|
||||||
terrain: Some(TerrainState::Yards(5)),
|
terrain: Some(TerrainState::Yards(5)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let penalty = Event::Penalty(TerrainState::Yards(15));
|
let penalty = Event::Penalty(TerrainState::Yards(15));
|
||||||
|
|
||||||
let turnover = Event::Turnover(Team::Nebraska);
|
let turnover = Event::Turnover(Team::Nebraska);
|
||||||
|
|
||||||
let noned_down = Event::Play(Play {
|
let noned_down = Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: None,
|
down: None,
|
||||||
terrain: None,
|
terrain: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let score = Event::Score(ScorePoints::default());
|
let score = Event::Score(ScorePoints::default());
|
||||||
|
|
||||||
let goal_line = Event::Play(Play {
|
let goal_line = Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::First),
|
down: Some(Down::First),
|
||||||
terrain: Some(TerrainState::GoalLine),
|
terrain: Some(TerrainState::GoalLine),
|
||||||
});
|
});
|
||||||
|
|
||||||
let inches = Event::Play(Play {
|
let inches = Event::Play(Play {
|
||||||
action: Action::Unknown,
|
action: Action::Unknown,
|
||||||
down: Some(Down::First),
|
down: Some(Down::First),
|
||||||
@@ -155,5 +178,8 @@ mod tests {
|
|||||||
assert!(None == goal_line.delta(&first_down));
|
assert!(None == goal_line.delta(&first_down));
|
||||||
assert!(None == inches.delta(&first_down));
|
assert!(None == inches.delta(&first_down));
|
||||||
assert!(None == goal_line.delta(&inches));
|
assert!(None == goal_line.delta(&inches));
|
||||||
|
assert!(10_i8 == turnover.delta(&first_down).unwrap());
|
||||||
|
assert!(0_i8 == turnover.delta(&second_down).unwrap());
|
||||||
|
assert!(-3_i8 == turnover.delta(&third_down).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use serde::Deserialize;
|
|||||||
use std::{fs::File, path::PathBuf};
|
use std::{fs::File, path::PathBuf};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct LogFile(Vec<super::Game>);
|
pub struct LogFile(pub Vec<super::Game>);
|
||||||
|
|
||||||
impl LogFile {
|
impl LogFile {
|
||||||
pub fn min_ver(&self) -> semver::Version {
|
pub fn min_ver(&self) -> semver::Version {
|
||||||
|
|||||||
@@ -1,58 +1,356 @@
|
|||||||
use crate::{Event, Period, Play, PlayHandle, Team, error};
|
use crate::{Event, Period, Team, error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[deprecated(since = "0.2.0", note = "Migrated to Game")]
|
|
||||||
pub type GameRecord = Game;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
pub version: semver::Version,
|
pub version: semver::Version,
|
||||||
pub flags: Vec<FeatureFlags>,
|
pub flags: Vec<Flags>,
|
||||||
pub periods: Vec<Period>,
|
pub periods: Vec<Period>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
/// Returns the teams of this game.
|
/// Returns the teams of this game.
|
||||||
pub fn teams(&self) -> Result<Vec<Team>, error::TeamsError> {
|
pub fn teams(&self) -> Result<Vec<Team>, error::TeamsError> {
|
||||||
|
let ignore: Vec<Team> = self
|
||||||
|
.flags
|
||||||
|
.iter()
|
||||||
|
.filter_map(|flag| {
|
||||||
|
if let Flags::IgnoreTeam(team) = flag {
|
||||||
|
Some(team.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut teams = vec![];
|
let mut teams = vec![];
|
||||||
|
|
||||||
self.periods.iter().for_each(|period| {
|
self.periods.iter().for_each(|period| {
|
||||||
period.events.iter().for_each(|event| {
|
for event in period.events.iter() {
|
||||||
if let Event::Kickoff(t) | Event::Turnover(t) = event {
|
if let Ok(team) = event.team() {
|
||||||
if teams.contains(t) {
|
if !ignore.contains(&team) && !teams.contains(&team) {
|
||||||
teams.push(t.to_owned())
|
teams.push(team)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if teams.len() == 2 {
|
if teams.len() == 2 || ignore.len() != 0 {
|
||||||
Ok(teams)
|
Ok(teams)
|
||||||
} else {
|
} else {
|
||||||
Err(error::TeamsError::NumberFound(teams.len()))
|
Err(error::TeamsError::NumberFound(teams.len()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deltas(&self) -> Vec<i8> {
|
pub fn deltas(&self, team: Team) -> Vec<i8> {
|
||||||
self.periods
|
let events = self
|
||||||
|
.periods
|
||||||
.iter()
|
.iter()
|
||||||
.map(|period| period.deltas())
|
.filter_map(|period| Some(period.team_events(team.to_owned(), None).ok().unwrap()))
|
||||||
.collect::<Vec<Vec<i8>>>()
|
.collect::<Vec<Vec<Event>>>()
|
||||||
.concat()
|
.concat();
|
||||||
|
let len = events.len() - 1;
|
||||||
|
let mut idx: usize = 0;
|
||||||
|
let mut deltas: Vec<i8> = vec![];
|
||||||
|
|
||||||
|
dbg!(&events);
|
||||||
|
|
||||||
|
while idx < len {
|
||||||
|
if let Some(value) = events[idx].delta(&events[idx + 1]) {
|
||||||
|
deltas.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
idx += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
deltas
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The average number of plays in a quarter.
|
||||||
|
/// Does not include OT plays or quarters where team indeterminate.
|
||||||
|
pub fn avg_plays_per_quarter(&self, team: Team) -> f32 {
|
||||||
|
// Handle if teams known at start or not override via index calculation of all game events.
|
||||||
|
|
||||||
|
let quarterly_avgs: Vec<f32> = self
|
||||||
|
.periods
|
||||||
|
.iter()
|
||||||
|
.filter_map(|period| {
|
||||||
|
if !period.is_overtime() {
|
||||||
|
let plays = period.team_plays(team.to_owned(), None);
|
||||||
|
Some(plays.unwrap().len() as f32 / period.quarters().len() as f32)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<f32>>();
|
||||||
|
|
||||||
|
let mut summation = 0_f32;
|
||||||
|
|
||||||
|
quarterly_avgs.iter().for_each(|float| summation += float);
|
||||||
|
|
||||||
|
summation / quarterly_avgs.len() as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn team_plays(&self, team: Team) -> usize {
|
||||||
|
let quarterly_plays: Vec<usize> = self
|
||||||
|
.periods
|
||||||
|
.iter()
|
||||||
|
.filter_map(|period| {
|
||||||
|
if !period.is_overtime() {
|
||||||
|
let plays = period.team_plays(team.to_owned(), None);
|
||||||
|
Some(plays.unwrap().len())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<usize>>();
|
||||||
|
|
||||||
|
let mut summation = 0_usize;
|
||||||
|
|
||||||
|
quarterly_plays.iter().for_each(|value| summation += value);
|
||||||
|
|
||||||
|
summation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayHandle for Game {
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
fn plays(&self) -> Vec<Play> {
|
pub enum Flags {
|
||||||
self.periods
|
IgnoreTeam(Team),
|
||||||
.iter()
|
IgnoreScore,
|
||||||
.map(|period| period.plays())
|
|
||||||
.collect::<Vec<Vec<Play>>>() // Make compiler happy with turbofish.
|
|
||||||
.concat()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[cfg(test)]
|
||||||
pub enum FeatureFlags {
|
mod tests {
|
||||||
Ignore(Team),
|
use crate::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn avg_plays_per_quarter() {
|
||||||
|
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::Turnover(Team::ArizonaState),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
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()),
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
|
flags: vec![],
|
||||||
|
periods: vec![Period {
|
||||||
|
start: Quarter::Second,
|
||||||
|
end: Some(Quarter::Fourth),
|
||||||
|
events: vec![
|
||||||
|
Event::Turnover(Team::Nebraska),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(a.avg_plays_per_quarter(Team::Nebraska) == ((1_f32 + 2_f32) / 2_f32));
|
||||||
|
assert!(b.avg_plays_per_quarter(Team::Nebraska) == (1_f32 / 3_f32))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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]
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn teams() {
|
||||||
|
let a = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
|
flags: vec![],
|
||||||
|
periods: vec![
|
||||||
|
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 b = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
|
flags: vec![],
|
||||||
|
periods: vec![
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let c = 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)],
|
||||||
|
},
|
||||||
|
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!(b.teams().is_err() == true);
|
||||||
|
assert!(c.teams().unwrap() == vec![Team::ArizonaState]);
|
||||||
|
assert!(d.teams().unwrap() == vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deltas() {
|
||||||
|
let game = Game {
|
||||||
|
version: crate::MIN_VER,
|
||||||
|
flags: vec![],
|
||||||
|
periods: vec![
|
||||||
|
Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
Event::Play(Play {
|
||||||
|
action: Action::Unknown,
|
||||||
|
down: Some(Down::First),
|
||||||
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
|
}),
|
||||||
|
Event::Play(Play {
|
||||||
|
action: Action::Unknown,
|
||||||
|
down: Some(Down::Second),
|
||||||
|
terrain: Some(TerrainState::Yards(13)),
|
||||||
|
}),
|
||||||
|
Event::Play(Play {
|
||||||
|
action: Action::Unknown,
|
||||||
|
down: Some(Down::Third),
|
||||||
|
terrain: Some(TerrainState::Yards(8)),
|
||||||
|
}),
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
Event::Play(Play {
|
||||||
|
action: Action::Unknown,
|
||||||
|
down: Some(Down::First),
|
||||||
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
|
}),
|
||||||
|
Event::Play(Play {
|
||||||
|
action: Action::Unknown,
|
||||||
|
down: Some(Down::Second),
|
||||||
|
terrain: Some(TerrainState::Yards(10)),
|
||||||
|
}),
|
||||||
|
Event::Turnover(Team::Nebraska),
|
||||||
|
Event::Play(Play {
|
||||||
|
action: Action::Unknown,
|
||||||
|
down: Some(Down::Second),
|
||||||
|
terrain: Some(TerrainState::Yards(12)),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(dbg!(game.deltas(Team::Nebraska)) == vec![10_i8, -3_i8, 5_i8, -2_i8, 12_i8]);
|
||||||
|
assert!(dbg!(game.deltas(Team::ArizonaState)) == vec![10_i8, 0_i8]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
mod action;
|
mod action;
|
||||||
mod error;
|
mod error;
|
||||||
mod event;
|
mod event;
|
||||||
mod file;
|
mod file;
|
||||||
mod game;
|
mod game;
|
||||||
mod period;
|
mod period;
|
||||||
|
#[allow(deprecated)]
|
||||||
mod play;
|
mod play;
|
||||||
mod terrain;
|
mod terrain;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub const MIN_VER: semver::Version = semver::Version::new(0, 5, 0);
|
pub const MIN_VER: semver::Version = semver::Version::new(0, 5, 0);
|
||||||
|
|
||||||
|
// I'm lazy.
|
||||||
pub use action::*;
|
pub use action::*;
|
||||||
pub use event::Event;
|
pub use event::*;
|
||||||
pub use file::LogFile;
|
pub use file::*;
|
||||||
pub use game::{Game, GameRecord};
|
pub use game::*;
|
||||||
pub use period::*;
|
pub use period::*;
|
||||||
pub use play::*;
|
pub use play::*;
|
||||||
pub use terrain::TerrainState;
|
pub use terrain::*;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Event, Play, PlayHandle};
|
use crate::{Event, Play, PlayHandle, Team, error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
@@ -24,20 +24,117 @@ impl PlayHandle for Period {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Period {
|
impl Period {
|
||||||
pub fn deltas(&self) -> Vec<i8> {
|
pub fn team_events(
|
||||||
let len = self.events.len() - 1;
|
&self,
|
||||||
let mut idx: usize = 0;
|
team: Team,
|
||||||
let mut deltas: Vec<i8> = vec![];
|
assume_team_known: Option<bool>,
|
||||||
|
) -> Result<Vec<Event>, error::CannotDetermineTeams> {
|
||||||
|
let mut events: Vec<Event> = vec![];
|
||||||
|
let mut first = true;
|
||||||
|
let mut record: bool = true;
|
||||||
|
let assume_team_known = assume_team_known.unwrap_or(false);
|
||||||
|
|
||||||
while idx < len {
|
for event in self.events.iter() {
|
||||||
if let Some(value) = self.events[idx].delta(&self.events[idx + 1]) {
|
if let Event::Kickoff(_) | Event::Turnover(_) = event {
|
||||||
deltas.push(value);
|
record = {
|
||||||
|
if team == event.team().unwrap() {
|
||||||
|
// Wipe events vec if the start of quarter was opposition
|
||||||
|
// on offence.
|
||||||
|
if first {
|
||||||
|
events = vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
events.push(event.to_owned());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx += 1
|
if record {
|
||||||
|
events.push(event.to_owned());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deltas
|
// If already handled or assumption override applicable
|
||||||
|
if !first || (first && assume_team_known) {
|
||||||
|
Ok(events)
|
||||||
|
} else {
|
||||||
|
Err(error::CannotDetermineTeams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn team_plays(
|
||||||
|
&self,
|
||||||
|
team: Team,
|
||||||
|
assume_team_known: Option<bool>,
|
||||||
|
) -> Result<Vec<Play>, error::CannotDetermineTeams> {
|
||||||
|
Ok(self
|
||||||
|
.team_events(team, assume_team_known)?
|
||||||
|
.iter()
|
||||||
|
.filter_map(|event| {
|
||||||
|
if let Event::Play(play) = event {
|
||||||
|
Some(play.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn quarters(&self) -> Vec<Quarter> {
|
||||||
|
let mut quarters: Vec<Quarter> = vec![self.start.to_owned()];
|
||||||
|
|
||||||
|
if self.end.is_none() {
|
||||||
|
return quarters;
|
||||||
|
}
|
||||||
|
|
||||||
|
let order = vec![
|
||||||
|
Quarter::First,
|
||||||
|
Quarter::Second,
|
||||||
|
Quarter::Third,
|
||||||
|
Quarter::Fourth,
|
||||||
|
];
|
||||||
|
|
||||||
|
let start = if let Quarter::Overtime(x) = self.start {
|
||||||
|
(3 + x) as usize
|
||||||
|
} else {
|
||||||
|
order.iter().position(|q| q == &self.start).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let end = if let Quarter::Overtime(x) = self.end.as_ref().unwrap() {
|
||||||
|
(3 + x) as usize
|
||||||
|
} else {
|
||||||
|
order
|
||||||
|
.iter()
|
||||||
|
.position(|q| q == self.end.as_ref().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let range: Vec<usize> = ((start + 1)..=end).collect();
|
||||||
|
|
||||||
|
for i in range {
|
||||||
|
quarters.push(match i {
|
||||||
|
0 => Quarter::First,
|
||||||
|
1 => Quarter::Second,
|
||||||
|
2 => Quarter::Third,
|
||||||
|
3 => Quarter::Fourth,
|
||||||
|
_ => Quarter::Overtime((i - 3) as u8),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
quarters
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_overtime(&self) -> bool {
|
||||||
|
if self.start.is_overtime() || self.end.as_ref().is_some_and(|some| some.is_overtime()) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,49 +147,158 @@ pub enum Quarter {
|
|||||||
Overtime(u8),
|
Overtime(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Quarter {
|
||||||
|
pub fn is_overtime(&self) -> bool {
|
||||||
|
if let Self::Overtime(_) = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use crate::*;
|
||||||
use crate::{Action, Down, Team, TerrainState};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deltas() {
|
fn team_events() {
|
||||||
|
let a = Period {
|
||||||
|
start: Quarter::First,
|
||||||
|
end: None,
|
||||||
|
events: vec![
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
Event::Score(ScorePoints::Touchdown),
|
||||||
|
Event::Kickoff(Team::SouthCarolina),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
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!(
|
||||||
|
a.team_events(Team::Nebraska, None).unwrap()
|
||||||
|
== vec![
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::ArizonaState),
|
||||||
|
Event::Kickoff(Team::Nebraska),
|
||||||
|
Event::Score(ScorePoints::Touchdown),
|
||||||
|
Event::Kickoff(Team::SouthCarolina),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
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]
|
||||||
|
fn team_plays() {
|
||||||
let period = Period {
|
let period = Period {
|
||||||
start: Quarter::First,
|
start: Quarter::First,
|
||||||
end: None,
|
end: None,
|
||||||
events: vec![
|
events: vec![
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Play(Play {
|
Event::Play(Play::default()),
|
||||||
action: Action::Unknown,
|
Event::Turnover(Team::ArizonaState),
|
||||||
down: Some(Down::First),
|
Event::Play(Play::default()),
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
Event::Play(Play::default()),
|
||||||
}),
|
Event::Kickoff(Team::Nebraska),
|
||||||
Event::Play(Play {
|
Event::Play(Play::default()),
|
||||||
action: Action::Unknown,
|
Event::Score(ScorePoints::default()),
|
||||||
down: Some(Down::Second),
|
Event::Kickoff(Team::SouthCarolina),
|
||||||
terrain: Some(TerrainState::Yards(13)),
|
Event::Play(Play::default()),
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Unknown,
|
|
||||||
down: Some(Down::Third),
|
|
||||||
terrain: Some(TerrainState::Yards(8)),
|
|
||||||
}),
|
|
||||||
Event::Turnover(Team::Nebraska),
|
Event::Turnover(Team::Nebraska),
|
||||||
Event::Play(Play {
|
Event::Play(Play::default()),
|
||||||
action: Action::Unknown,
|
|
||||||
down: Some(Down::First),
|
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
|
||||||
}),
|
|
||||||
Event::Play(Play {
|
|
||||||
action: Action::Unknown,
|
|
||||||
down: Some(Down::First),
|
|
||||||
terrain: Some(TerrainState::Yards(10)),
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
let expected: Vec<i8> = vec![10, -3, 5, 10];
|
assert!(
|
||||||
|
period.team_plays(Team::Nebraska, None).unwrap()
|
||||||
|
== vec![Play::default(), Play::default(), Play::default()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
assert!(period.deltas() == expected)
|
#[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,11 +1,6 @@
|
|||||||
use crate::{Action, TerrainState};
|
use crate::{Action, TerrainState};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
pub trait PlayHandle {
|
|
||||||
/// Returns all plays within object's scope.
|
|
||||||
fn plays(&self) -> Vec<Play>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
pub struct Play {
|
pub struct Play {
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
@@ -13,12 +8,6 @@ pub struct Play {
|
|||||||
pub terrain: Option<TerrainState>,
|
pub terrain: Option<TerrainState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayHandle for Play {
|
|
||||||
fn plays(&self) -> Vec<Play> {
|
|
||||||
vec![self.to_owned()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Play {
|
impl Default for Play {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ name = "miller"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
license-file = "LICENSE"
|
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "4.5"
|
version = "4.5"
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
mod calculator;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use gamelog::LogFile;
|
use gamelog::{Flags, LogFile};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
@@ -12,7 +10,7 @@ struct Args {
|
|||||||
short,
|
short,
|
||||||
long,
|
long,
|
||||||
value_hint = clap::ValueHint::DirPath,
|
value_hint = clap::ValueHint::DirPath,
|
||||||
default_value = format!("{}/templates/logfile.ron", std::env::current_dir()
|
default_value = format!("{}/../templates/logfile.ron", std::env::current_dir()
|
||||||
.expect("Failed to get current working dir.")
|
.expect("Failed to get current working dir.")
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.to_str()
|
.to_str()
|
||||||
@@ -24,10 +22,22 @@ struct Args {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let config = Args::parse();
|
let config = Args::parse();
|
||||||
|
|
||||||
let log: LogFile = {
|
let log: LogFile = match LogFile::try_from(config.logfile_path) {
|
||||||
let file = match LogFile::try_from(config.logfile_path) {
|
Ok(f) => f,
|
||||||
Ok(f) => f,
|
Err(err) => panic!("Error: Failed to open logfile: {:?}", err),
|
||||||
Err(err) => panic!("Error: Failed to open logfile: {:?}", err),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for game in log.0.iter() {
|
||||||
|
if let Ok(teams) = game.teams() {
|
||||||
|
for team in teams {
|
||||||
|
if !game.flags.contains(&Flags::IgnoreTeam(team.to_owned())) {
|
||||||
|
println!(
|
||||||
|
"{:?}: {:?}",
|
||||||
|
&team,
|
||||||
|
game.avg_plays_per_quarter(team.to_owned())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,20 +4,20 @@
|
|||||||
|
|
||||||
[
|
[
|
||||||
Game(
|
Game(
|
||||||
version: "0.3.0",
|
version: "0.5.0",
|
||||||
|
flags: [],
|
||||||
periods: [
|
periods: [
|
||||||
Period(
|
Period(
|
||||||
start: First,
|
start: First,
|
||||||
end: Fourth,
|
end: Fourth,
|
||||||
plays: [
|
events: [
|
||||||
Kickoff(Nebraska),
|
Kickoff(Nebraska),
|
||||||
Play(
|
Play(
|
||||||
action: Unknown,
|
action: Unknown,
|
||||||
down: First,
|
down: First,
|
||||||
terrain: Yards(10)
|
terrain: Yards(10)
|
||||||
),
|
),
|
||||||
Score(3),
|
Score(FieldGoal),
|
||||||
Pat(Fail)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user