Bump tui branch to using Gamelog v0.7.1 #13
@@ -12,6 +12,7 @@ I figured, that since I already had to digitize every note, that I was required
|
|||||||
|
|
||||||
== Goals
|
== Goals
|
||||||
|
|
||||||
|
* [ ] Auto Ranking system?
|
||||||
* [ ] Data Visualizer?
|
* [ ] Data Visualizer?
|
||||||
* [ ] Dynamic Web Page?
|
* [ ] Dynamic Web Page?
|
||||||
** Server Side Events?
|
** Server Side Events?
|
||||||
|
|||||||
2011
gamelog.ron
2011
gamelog.ron
File diff suppressed because it is too large
Load Diff
2
gamelog/Cargo.lock
generated
2
gamelog/Cargo.lock
generated
@@ -19,7 +19,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gamelog"
|
name = "gamelog"
|
||||||
version = "0.6.0"
|
version = "0.7.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ron",
|
"ron",
|
||||||
"semver",
|
"semver",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gamelog"
|
name = "gamelog"
|
||||||
version = "0.6.0"
|
version = "0.7.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Down, Play, TerrainState, error};
|
use crate::{Down, Play, Quarter, TerrainState, error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
type Offence = Team;
|
type Offence = Team;
|
||||||
@@ -10,6 +10,7 @@ pub enum Event {
|
|||||||
Turnover(Offence),
|
Turnover(Offence),
|
||||||
Penalty(TerrainState),
|
Penalty(TerrainState),
|
||||||
Score(ScorePoints),
|
Score(ScorePoints),
|
||||||
|
Quarter(Quarter),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
@@ -74,6 +75,14 @@ impl Event {
|
|||||||
_ => Err(error::NoTeamAttribute),
|
_ => Err(error::NoTeamAttribute),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn quarter(&self) -> Option<Quarter> {
|
||||||
|
if let Event::Quarter(quarter) = self {
|
||||||
|
Some(quarter.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
@@ -171,6 +180,20 @@ mod tests {
|
|||||||
terrain: Some(TerrainState::Inches),
|
terrain: Some(TerrainState::Inches),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let quarter = Event::Quarter(Quarter::First);
|
||||||
|
|
||||||
|
assert!(None == quarter.delta(&kickoff));
|
||||||
|
assert!(None == quarter.delta(&first_down));
|
||||||
|
assert!(None == quarter.delta(&second_down));
|
||||||
|
assert!(None == quarter.delta(&third_down));
|
||||||
|
assert!(None == quarter.delta(&fourth_down));
|
||||||
|
assert!(None == quarter.delta(&turnover));
|
||||||
|
assert!(None == quarter.delta(&penalty));
|
||||||
|
assert!(None == quarter.delta(&goal_line));
|
||||||
|
assert!(None == quarter.delta(&inches));
|
||||||
|
assert!(None == quarter.delta(&score));
|
||||||
|
assert!(None == quarter.delta(&quarter));
|
||||||
|
|
||||||
assert!(10_i8 == kickoff.delta(&first_down).unwrap());
|
assert!(10_i8 == kickoff.delta(&first_down).unwrap());
|
||||||
assert!(0_i8 == kickoff.delta(&second_down).unwrap());
|
assert!(0_i8 == kickoff.delta(&second_down).unwrap());
|
||||||
assert!(None == kickoff.delta(&penalty));
|
assert!(None == kickoff.delta(&penalty));
|
||||||
@@ -248,9 +271,9 @@ mod tests {
|
|||||||
|
|
||||||
assert!(None == inches.delta(&kickoff));
|
assert!(None == inches.delta(&kickoff));
|
||||||
assert!(None == inches.delta(&first_down));
|
assert!(None == inches.delta(&first_down));
|
||||||
assert!(-10_i8 == goal_line.delta(&second_down).unwrap());
|
assert!(-10_i8 == inches.delta(&second_down).unwrap());
|
||||||
assert!(-13_i8 == goal_line.delta(&third_down).unwrap());
|
assert!(-13_i8 == inches.delta(&third_down).unwrap());
|
||||||
assert!(-5_i8 == goal_line.delta(&fourth_down).unwrap());
|
assert!(-5_i8 == inches.delta(&fourth_down).unwrap());
|
||||||
assert!(None == inches.delta(&turnover));
|
assert!(None == inches.delta(&turnover));
|
||||||
assert!(None == inches.delta(&penalty));
|
assert!(None == inches.delta(&penalty));
|
||||||
assert!(None == inches.delta(&goal_line));
|
assert!(None == inches.delta(&goal_line));
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use crate::{Event, Period, Team, error};
|
use crate::{Event, Quarter, Team, error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
#[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<Flags>,
|
pub flags: Vec<Flags>,
|
||||||
pub periods: Vec<Period>,
|
pub events: Vec<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
@@ -25,12 +26,10 @@ impl Game {
|
|||||||
|
|
||||||
let mut teams = vec![];
|
let mut teams = vec![];
|
||||||
|
|
||||||
self.periods.iter().for_each(|period| {
|
self.events.iter().for_each(|event| {
|
||||||
for event in period.events.iter() {
|
if let Ok(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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -43,12 +42,17 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deltas(&self, team: Team) -> Vec<i8> {
|
pub fn deltas(&self, team: Team) -> Vec<i8> {
|
||||||
let events = self
|
let events: Vec<Event> = self
|
||||||
.periods
|
.team_events(team)
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|period| Some(period.team_events(team.to_owned(), None).ok().unwrap()))
|
.filter_map(|event| {
|
||||||
.collect::<Vec<Vec<Event>>>()
|
if let Event::Quarter(_) = event {
|
||||||
.concat();
|
None
|
||||||
|
} else {
|
||||||
|
Some(event.to_owned())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
let len = events.len() - 1;
|
let len = 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![];
|
||||||
@@ -65,33 +69,30 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn team_plays(&self, team: Team) -> usize {
|
pub fn team_plays(&self, team: Team) -> usize {
|
||||||
self.periods
|
self.team_events(team)
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|period| {
|
.filter_map(|event| {
|
||||||
if !period.is_overtime() {
|
if let Event::Play(_) = event {
|
||||||
let plays = period.team_plays(team.to_owned(), None);
|
Some(event)
|
||||||
Some(plays.unwrap().len())
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<usize>>()
|
.collect::<Vec<&Event>>()
|
||||||
.iter()
|
.len()
|
||||||
.sum::<usize>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The average number of plays in a quarter.
|
/// 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 {
|
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 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> = self
|
let quarterly_avgs: Vec<f32> = periods
|
||||||
.periods
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|period| {
|
.filter_map(|period| {
|
||||||
if !period.is_overtime() {
|
if !period.is_overtime() {
|
||||||
let plays = period.team_plays(team.to_owned(), None);
|
Some(period.team_plays(team.to_owned()) as f32)
|
||||||
Some(plays.unwrap().len() as f32 / period.quarters().len() as f32)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -142,29 +143,138 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn penalties(&self, team: Team) -> usize {
|
pub fn penalties(&self, team: Team) -> usize {
|
||||||
// Knock down nesting?
|
self.team_events(team)
|
||||||
self.periods
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|period| {
|
.filter_map(|event| {
|
||||||
Some(
|
if let Event::Penalty(_) = event {
|
||||||
period
|
Some(event.to_owned())
|
||||||
.team_events(team.to_owned(), None)
|
} else {
|
||||||
.ok()?
|
None
|
||||||
.iter()
|
}
|
||||||
.filter_map(|event| {
|
|
||||||
if let Event::Penalty(_) = event {
|
|
||||||
Some(event.to_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<Event>>(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<Vec<Event>>>()
|
.collect::<Vec<Event>>()
|
||||||
.concat()
|
|
||||||
.len()
|
.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_period(&self, quarter: Quarter) -> Period {
|
||||||
|
let mut record = false;
|
||||||
|
|
||||||
|
Period {
|
||||||
|
period: quarter.to_owned(),
|
||||||
|
events: self
|
||||||
|
.events
|
||||||
|
.iter()
|
||||||
|
.filter_map(|event| {
|
||||||
|
if let Event::Quarter(_) = event {
|
||||||
|
record = Event::Quarter(quarter.to_owned()) == *event;
|
||||||
|
}
|
||||||
|
|
||||||
|
if record {
|
||||||
|
return Some(event.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect::<Vec<Event>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn team_events(&self, team: Team) -> Vec<Event> {
|
||||||
|
let mut events: Vec<Event> = vec![];
|
||||||
|
let mut first = true;
|
||||||
|
let mut record: bool = true;
|
||||||
|
|
||||||
|
self.events.iter().for_each(|event| {
|
||||||
|
if let Event::Kickoff(_) | Event::Turnover(_) = event {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if record {
|
||||||
|
events.push(event.to_owned());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If already handled or assumption override applicable
|
||||||
|
events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Period {
|
||||||
|
period: Quarter,
|
||||||
|
events: Vec<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Period {
|
||||||
|
pub fn team_events(&self, team: Team) -> Vec<Event> {
|
||||||
|
let mut events: Vec<Event> = vec![];
|
||||||
|
let mut first = true;
|
||||||
|
let mut record: bool = true;
|
||||||
|
|
||||||
|
self.events.iter().for_each(|event| {
|
||||||
|
if let Event::Kickoff(_) | Event::Turnover(_) = event {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if record {
|
||||||
|
events.push(event.to_owned());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
events
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn team_plays(&self, team: Team) -> usize {
|
||||||
|
self.team_events(team)
|
||||||
|
.iter()
|
||||||
|
.filter_map(|event| {
|
||||||
|
if let Event::Play(_) = event {
|
||||||
|
Some(event)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<&Event>>()
|
||||||
|
.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_overtime(&self) -> bool {
|
||||||
|
if let Quarter::Overtime(_) = self.period {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
@@ -173,8 +283,10 @@ pub enum Flags {
|
|||||||
IgnoreTeam(Team),
|
IgnoreTeam(Team),
|
||||||
IgnoreScore,
|
IgnoreScore,
|
||||||
Interval(u8),
|
Interval(u8),
|
||||||
|
SheerDumbFuckingLuck
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
@@ -184,30 +296,20 @@ mod tests {
|
|||||||
let a = Game {
|
let a = Game {
|
||||||
version: crate::MIN_VER,
|
version: crate::MIN_VER,
|
||||||
flags: vec![],
|
flags: vec![],
|
||||||
periods: vec![
|
events: vec![
|
||||||
Period {
|
Event::Quarter(Quarter::First),
|
||||||
start: Quarter::First,
|
Event::Kickoff(Team::Nebraska),
|
||||||
end: None,
|
Event::Play(Play::default()),
|
||||||
events: vec![
|
Event::Turnover(Team::ArizonaState),
|
||||||
Event::Kickoff(Team::Nebraska),
|
Event::Quarter(Quarter::Second),
|
||||||
Event::Play(Play::default()),
|
Event::Turnover(Team::Nebraska),
|
||||||
Event::Turnover(Team::ArizonaState),
|
Event::Play(Play::default()),
|
||||||
],
|
Event::Play(Play::default()),
|
||||||
},
|
Event::Play(Play::default()),
|
||||||
Period {
|
Event::Play(Play::default()),
|
||||||
start: Quarter::Second,
|
Event::Play(Play::default()),
|
||||||
end: Some(Quarter::Fourth),
|
Event::Play(Play::default()),
|
||||||
events: vec![
|
Event::Turnover(Team::ArizonaState),
|
||||||
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),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -409,4 +511,146 @@ mod tests {
|
|||||||
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]);
|
||||||
assert!(game.deltas(Team::ArizonaState) == vec![10_i8, 0_i8]);
|
assert!(game.deltas(Team::ArizonaState) == vec![10_i8, 0_i8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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 {
|
||||||
|
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::Play(Play::default()),
|
||||||
|
Event::Score(ScorePoints::default()),
|
||||||
|
Event::Kickoff(Team::SouthCarolina),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
Event::Turnover(Team::Nebraska),
|
||||||
|
Event::Play(Play::default()),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
period.team_plays(Team::Nebraska, None).unwrap()
|
||||||
|
== 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)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ mod play;
|
|||||||
mod terrain;
|
mod terrain;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub const MIN_VER: semver::Version = semver::Version::new(0, 6, 0);
|
pub const MIN_VER: semver::Version = semver::Version::new(0, 7, 0);
|
||||||
|
|
||||||
// I'm lazy.
|
// I'm lazy.
|
||||||
pub use action::*;
|
pub use action::*;
|
||||||
|
|||||||
@@ -1,125 +1,7 @@
|
|||||||
use crate::{Event, Play, Team, error};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use strum::EnumIter;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone, PartialEq, EnumIter)]
|
||||||
pub struct Period {
|
|
||||||
pub start: Quarter,
|
|
||||||
pub end: Option<Quarter>,
|
|
||||||
pub events: Vec<Event>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Period {
|
|
||||||
pub fn team_events(
|
|
||||||
&self,
|
|
||||||
team: Team,
|
|
||||||
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);
|
|
||||||
|
|
||||||
self.events.iter().for_each(|event| {
|
|
||||||
if let Event::Kickoff(_) | Event::Turnover(_) = event {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if record {
|
|
||||||
events.push(event.to_owned());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
self.start.is_overtime() || self.end.as_ref().is_some_and(|some| some.is_overtime())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
|
||||||
pub enum Quarter {
|
pub enum Quarter {
|
||||||
First,
|
First,
|
||||||
Second,
|
Second,
|
||||||
@@ -127,159 +9,3 @@ pub enum Quarter {
|
|||||||
Fourth,
|
Fourth,
|
||||||
Overtime(u8),
|
Overtime(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Quarter {
|
|
||||||
pub fn is_overtime(&self) -> bool {
|
|
||||||
if let Self::Overtime(_) = self {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
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 {
|
|
||||||
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::Play(Play::default()),
|
|
||||||
Event::Score(ScorePoints::default()),
|
|
||||||
Event::Kickoff(Team::SouthCarolina),
|
|
||||||
Event::Play(Play::default()),
|
|
||||||
Event::Turnover(Team::Nebraska),
|
|
||||||
Event::Play(Play::default()),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
period.team_plays(Team::Nebraska, None).unwrap()
|
|
||||||
== 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)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
2
miller/Cargo.lock
generated
2
miller/Cargo.lock
generated
@@ -64,7 +64,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gamelog"
|
name = "gamelog"
|
||||||
version = "0.6.0"
|
version = "0.7.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ron",
|
"ron",
|
||||||
"semver",
|
"semver",
|
||||||
|
|||||||
Reference in New Issue
Block a user