diff --git a/README.adoc b/README.adoc index 660f24a..6c8b4a0 100644 --- a/README.adoc +++ b/README.adoc @@ -14,11 +14,15 @@ I figured, that since I already had to digitize every note, that I was required * [ ] Data Visualizer? * [ ] Dynamic Web Page? -** Server Side Events? -** IDK frontend web at all. -** plotly.rs? -** charming.rs? -** WASM? +* [ ] 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 @@ -59,7 +63,3 @@ I figured, that since I already had to digitize every note, that I was required ** [*] Avg. Offence Plays per quarter ** [*] Avg. Offence Plays per game ** [*] Avg. Penalties per game -* [ ] Play Trend Analysis -** [ ] Most Frequent Play -** [ ] Least Frequent Play -** [ ] Most Effective Play (Greatest Terrain Gain given sufficient data) diff --git a/gamelog.ron b/gamelog.ron index d3f5687..8d863bf 100644 --- a/gamelog.ron +++ b/gamelog.ron @@ -1105,5 +1105,745 @@ ] ) ] + ), + Game( + version: "0.6.0", + flags: [Interval(3)], + periods: [ + Period( + start: First, + end: None, + events: [ + Kickoff(Nebraska), + Play( + action: SlotOut, + down: First, + terrain: Yards(10) + ), + Play( + action: PlayActionPowerZero, + down: First, + terrain: Yards(10) + ), + Play( + action: StrongFlood, + down: First, + terrain: Yards(10) + ), + Play( + action: HalfbackSlipScreen, + down: Second, + terrain: Yards(13), + ), + Play( + action: PlayActionBoot, + down: Third, + terrain: Yards(13) + ), + Play( + action: SlotOut, + down: Fourth, + terrain: Yards(13) + ), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10) + ) + ] + ), + Period( + start: Second, + end: None, + events: [ + Play( + action: Curls, + down: Second, + terrain: Yards(10) + ), + Play( + action: CrackStudentBodyRightTackle, + down: Third, + terrain: Yards(4) + ), + Play( + action: Unknown, + down: First, + terrain: GoalLine + ), + Play( + action: Unknown, + down: Second, + terrain: GoalLine + ), + Score(Touchdown), + Score(PatTouchdown), + Kickoff(Syracuse), + Penalty(Yards(15)), + Play( + action: CrackStudentBodyRightTackle, + down: Second, + terrain: Yards(19) + ), + Play( + action: HalfbackSweep, + down: Third, + terrain: Yards(13) + ), + Play( + action: Unknown, + down: Fourth, + terrain: Yards(11) + ) + ] + ), + Period( + start: Third, + end: None, + events: [ + Kickoff(Syracuse), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10) + ), + Play( + action: StrongFlood, + down: Second, + terrain: Yards(11) + ), + Play( + action: PlayActionComebacks, + down: Third, + terrain: Inches, + ), + Play( + action: HalfbackSweep, + down: First, + terrain: Yards(10) + ), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10) + ), + Play( + action: PlayActionComebacks, + down: First, + terrain: GoalLine + ), + Play( + action: Unknown, + down: Second, + terrain: GoalLine + ) + ] + ), + Period( + start: Fourth, + end: None, + events: [ + Play( + action: StrongFlood, + down: Third, + terrain: GoalLine + ), + Score(Touchdown), + Score(PatTouchdown), + Kickoff(Nebraska), + Play( + action: Curls, + down: Second, + terrain: Yards(1) + ), + Play( + action: SlotOut, + down: First, + terrain: Yards(10) + ), + Play( + action: Curls, + down: Second, + terrain: Yards(10) + ), + Play( + action: SlotOut, + down: Third, + terrain: Yards(10) + ), + Turnover(Syracuse), + Play( + action: Unknown, + down: Second, + terrain: Yards(1) + ), + Play( + action: Unknown, + down: First, + terrain: Yards(10), + ), + ] + ), + Period( + start: Overtime(1), + end: None, + events: [ + Play( + action: StrongFlood, + down: Second, + terrain: Yards(8) + ), + Play( + action: SlotOut, + down: First, + terrain: Yards(10) + ), + Play( + action: PlayActionComebacks, + down: None, + terrain: None + ), + Turnover(Syracuse), + Play( + action: Unknown, // It failed so bad. + down: Second, + terrain: Yards(15) + ), + Play( + action: Unknown, // It failed so bad. + down: Third, + terrain: Yards(22) + ), + Play( + action: PlayActionBoot, + down: Fourth, + terrain: Yards(10) + ), + Score(FieldGoal) + ] + ) + ] + ), + Game( + version: "0.6.1", + flags: [Interval(3)], + periods: [ + Period( + start: First, + end: None, + events: [ + Kickoff(SouthCarolina), + Play( + action: FleaFlicker, + down: Second, + terrain: Yards(16) + ), + Play( + action: HalfbackSlipScreen, + down: Third, + terrain: Yards(13) + ), + Play( + action: Unknown, + down: Fourth, + terrain: Yards(8) + ), + Play( + action: HailMary, + down: None, + terrain: None + ), + Turnover(Colorado), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10) + ), + Play( + action: PlayActionComebacks, + down: None, + terrain: None + ), + Turnover(SouthCarolina) + ] + ), + Period( + start: Second, + end: None, + events: [ + Play( + action: HalfbackSweep, + down: Second, + terrain: Yards(13) + ), + Play( + action: Mesh, + down: Third, + terrain: Yards(9) + ), + Play( + action: FleaFlicker, + down: None, + terrain: None + ), + Turnover(Colorado), + Play( + action: StrongFlood, + down: Second, + terrain: Yards(8) + ), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10) + ), + Play( + action: SlotOut, + down: First, + terrain: Yards(10) + ), + Score(Touchdown), + Score(PatSafety), + Kickoff(SouthCarolina), + Play( + action: CrackStudentBodyRightTackle, + down: First, + terrain: Yards(10) + ), + Play( + action: CrackStudentBodyRightTackle, + down: Second, + terrain: Yards(9) + ), + Play( + action: Unknown, + down: None, + terrain: None + ) + ] + ), + Period( + start: Third, + end: None, + events: [ + Kickoff(Colorado), + Play( + action: SpeedOption, + down: Second, + terrain: Yards(4) + ), + Play( + action: Unknown, + down: First, + terrain: Yards(10) + ), + Play( + action: Unknown, + down: Second, + terrain: Yards(13) + ), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10) + ) + ] + ), + Period( + start: Fourth, + end: None, + events: [ + Play( + action: SpeedOption, + down: Second, + terrain: Yards(6) + ), + Penalty(Yards(11)), + Play( + action: PlayActionComebacks, + down: Third, + terrain: Yards(5) + ), + Play( + action: HalfbackSlipScreen, + down: First, + terrain: Yards(10) + ), + ] + ), + ] + ), + Game( + version: "0.7.0", + flags: [Interval(3)], + events: [ + Quarter(1), + Kickoff(ArizonaState), + Play( + action: Unknown, + down: Second, + terrain: Yards(4), + ), + Play( + action: Unknown, + down: Third, + terrain: Yards(4), + ), + Penalty(Yards(9), + Play( + action: PlayActionComebacks, + down: Fourth, + terrain: Yards(3), + ), + Quarter(Second), + Play( + action: HailMary, + down: None, + terrain: None + ), + Turnover(Iowa), + Play( + action: Unknown, // Iform throw + down: Second, + terrain: Inches + ), + Play( + action:Unknown, + down: First + terrain: Yards(10) + ), + Play( + action: Unknown, + down: Second, + terrain: Yards(11) + ), + Play( + action: Unknown, + down: Third, + terrain: Yards(11) + ), + Play( + action: PlayActionBoot, + down: Fourth, + terrain: Yards(11), + ), + Play( + action: Unknown, + down: None, + terrain: None + ), + Turnover(ArizonaState), + Play( + action: PlayActionComebacks, + down: Second, + terrain: Yards(1), + ), + Play( + action: Unknown, + down: None, + terrain: None, + ), + Quarter(Third), + Kickoff(Iowa), + Play( + action: SpeedOption, + down: Second, + terrain: Yards(11), + ), + Play( + action: SlotOut, + down: Third, + terrain: Yards(11), + ), + Play( + action: HalfbackSlipScreen, + down: First, + terrain: Yards(10), + ), + Play( + action: SpeedOption, + down: Second, + terrain: Yards(11), + ), + Play( + action: SlotOut, + down: Third, + terrain: Yards(11), + ), + Play( + action: Unknown, + down: Fourth, + terrain: Yards(11), + ), + Turnover(ArizonaState) + Play( + action: HalfbackSlipScreen, + down: Second, + terrain: Yards(10), + ), + Play( + action: HalfbackSlam, + down: First, + terrain: Yards(10) + ), + Quarter(Fourth), + Play( + action: PlayActionBoot, + down: Second, + terrain: Yards(10), + ), + Play( + action: SlotOut, + down: Third, + terrain: Yards(1), + ), + Play( + action: PowerZero, + down: First, + terrain: Yards(10), + ), + Play( + action: HalfbackSlam, + down: Second, + terrain: Yards(3), + ), + Play( + action: PlayActionBoot, + down: First, + terrain: Yards(10), + ), + Play( + action: SlotOut, + down: Second, + terrain: Yards(1), + ), + Play( + action: HalfbackSlam, + down: First, + terrain: Yards(10) + ), + Play( + action: PlayActionComebacks, + down: First, + terrain: GoalLine, + ), + Score(FieldGoal), + Kickoff(Iowa), + Play( + action: HailMary, + down: First, + terrain: Yards(10), + ) + ] + ), + Game( + version: "0.7.1", + flags: [Interval(3), SheerDumbFuckingLuck], + events: [ + Quarter(First), + Kickoff(Nebraska), + Play( + action: SlotOut, + down: First, + terrain: Yards(10), + ), + Play( + action: SlantBubble, + down: Second, + terrain: Yards(10), + ), + Play( + action: PowerZero, + down: First, + terrain: Yards(10), + ), + Play( + action: PlayActionPowerZero, + down: First, + terrain: Yards(10), + ), + Play( + action: CrackStudentBodyRightTackle, + down: Second, + terrain: Yards(5), + ), + Play( + action: HalfbackSlipScreen, + down: Third, + terrain: Yards(5), + ), + Play( + action: SlotOut, + down: First, + terrain: Yards(10), + ), + Penalty(Yards(15)), + Play( + action: StrongFlood, + down: Second, + terrain: Yards(22) + ) + Play( + action: SlotOut, + down: Third, + terrain: Yards(22) + ) + Quarter(Second), + Play( + action: StrongFlood, + down: None, + terrain: None, + ), + Turnover(TexasAnM), + Play( + action: Unknown, + down: None, + terrain: None, + ), + Play( + action: Unknown, + down: None, + terrain: None, + ), + Play( + action: Unknown, + down: None, + terrain: None, + ), + Play( + action: Unknown, + down: None, + terrain: None, + ), + Play( + action: Curls, + down: First, + terrain: Yards(10) + ), + Play( + action: SlotOut, + down: Second, + terrain: Yards(1), + ), + Play( + action: Unknown, + down: First, + terrain: GoalLine + ) + Play( + action: Unknown, + down: None, + terrain: None, + ), + Score(Touchdown), + Score(PatSafety), + Kickoff(Nebraska), + Play( + action: PlayActionBoot, + down: Second, + terrain: Yards(10), + ), + Play( + action: PlayActionComebacks, + down: Third, + terrain: Yards(13), + ), + Quarter(Third), + Kickoff(TexasAnM), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10), + ), + Play( + action: PlayActionComebacks, + down: Second, + terrain: Yards(15), + ), + Play( + action: FleaFlicker, + down: Third, + terrain: Yards(15), + ), + Play( + action: StrongFlood, + down: Fourth, + terrain: Yards(19), + ), + Play( + action: Unknown, + down: None, + terrain: None, + ), + Turnover(Nebraska), + Play( + action: SlotOut, + down: Second, + terrain: Yards(10), + ), + Play( + action: PowerZero, + down: Third, + terrain: Yards(7), + ), + Play( + action: PlayActionComebacks, + down: First, + terrain: Yards(10), + ), + Quarter(Fourth), + Penalty(Yards(15)) + Play( + action: StrongFlood, + down: Second, + terrain: Yards(11), + ), + Play( + action: PlayActionComebacks, + down: Third, + terrain: Yards(10), + ), + Play( + action: SlotOut, + down: Fourth, + terrain: Yards(1), + ), + Play( + action: PlayActionComebacks, + down: None, + terrain: None, + ), + Score(Touchdown), + Score(PatSafety), + Kickoff(TexasAnM), + Play( + action: StrongFlood, + down: Second, + terrain: Yards(Inches), + ), + Play( + action: PlayActionBoot, + down: First, + terrain: Yards(10), + ), + Play( + action: Unknown, + down: Second, + terrain: Yards(17), + ), + Play( + action: PlayActionBoot, //Fucking bull + down: None, + terrain: None, + ), + Score(Touchdown), // Accross 75% of the field + Score(2), + Kickoff(Nebraska), + Play( + action: Unknown, // Fucking hit the wrong button. Fake punted instead of punted. + down: None, + terrain: None, + ), + ] ) ] diff --git a/gamelog/Cargo.lock b/gamelog/Cargo.lock index 29cd030..53cf0a6 100644 --- a/gamelog/Cargo.lock +++ b/gamelog/Cargo.lock @@ -19,7 +19,7 @@ dependencies = [ [[package]] name = "gamelog" -version = "0.6.0" +version = "0.6.1" dependencies = [ "ron", "semver", diff --git a/gamelog/Cargo.toml b/gamelog/Cargo.toml index 9888864..fb27e9e 100644 --- a/gamelog/Cargo.toml +++ b/gamelog/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gamelog" -version = "0.6.0" +version = "0.6.1" edition = "2024" [dependencies] diff --git a/gamelog/src/action.rs b/gamelog/src/action.rs index 179e1d8..40ca054 100644 --- a/gamelog/src/action.rs +++ b/gamelog/src/action.rs @@ -5,6 +5,7 @@ pub enum Action { CrackStudentBodyRightTackle, Curls, FleaFlicker, + HailMary, HalfbackSlam, HalfbackSlipScreen, HalfbackSweep, diff --git a/gamelog/src/game.rs b/gamelog/src/game.rs index 2ed7852..50ef481 100644 --- a/gamelog/src/game.rs +++ b/gamelog/src/game.rs @@ -46,6 +46,8 @@ impl Game { let events = self .periods .iter() + // TOTALLY BORKED. + // BREAKS IF THE TEAMS ARE UNKNOWN, NEEDS FIXING. .filter_map(|period| Some(period.team_events(team.to_owned(), None).ok().unwrap())) .collect::>>() .concat(); diff --git a/miller/Cargo.lock b/miller/Cargo.lock index 4990f65..6498b4b 100644 --- a/miller/Cargo.lock +++ b/miller/Cargo.lock @@ -2,12 +2,24 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + [[package]] name = "base64" version = "0.22.1" @@ -23,6 +35,27 @@ dependencies = [ "serde", ] +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "4.5.32" @@ -62,14 +95,133 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "gamelog" -version = "0.6.0" +version = "0.6.1" dependencies = [ "ron", "semver", "serde", - "strum", + "strum 0.27.1", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", ] [[package]] @@ -78,14 +230,133 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] +name = "instability" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + [[package]] name = "miller" version = "0.1.0" dependencies = [ "clap", "gamelog", + "ratatui", ] +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "proc-macro2" version = "1.0.94" @@ -104,6 +375,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum 0.26.3", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "redox_syscall" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +dependencies = [ + "bitflags", +] + [[package]] name = "ron" version = "0.9.0" @@ -117,12 +418,37 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustversion" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "semver" version = "1.0.26" @@ -152,19 +478,83 @@ dependencies = [ "syn", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + [[package]] name = "strum" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" dependencies = [ - "strum_macros", + "strum_macros 0.27.1", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", ] [[package]] @@ -196,3 +586,142 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/miller/Cargo.toml b/miller/Cargo.toml index 8805ca2..e2cfd0f 100644 --- a/miller/Cargo.toml +++ b/miller/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2024" license = "MIT" +[dependencies] +ratatui = "0.29" + [dependencies.clap] version = "4.5" default-features = false diff --git a/miller/src/main.rs b/miller/src/main.rs index 411564e..ee4ddbd 100644 --- a/miller/src/main.rs +++ b/miller/src/main.rs @@ -1,7 +1,10 @@ +mod tui; + use clap::{ArgAction, Parser}; use core::panic; -use gamelog::{Action, Flags, Key, LogFile, Team}; -use std::path::PathBuf; +use gamelog::{Action, Down, Flags, Key, LogFile, Team}; +use std::{io, path::PathBuf, sync::mpsc, thread}; +use tui::App; #[derive(Debug, Parser)] #[clap(author, version, about)] @@ -18,11 +21,12 @@ struct Args { // Behaviour is backwards. // ArgAction::SetFalse by default evaluates to true, // ArgAction::SetTrue by default evaluates to false. - #[arg(short, long, action=ArgAction::SetFalse)] - display_results: bool, + /// Provide flag to disable tui and dump info via Debug pretty printing. + #[arg(short, long, action=ArgAction::SetTrue)] + no_tui: bool, } -fn main() { +fn main() -> io::Result<()> { let config = Args::parse(); let log: LogFile = match LogFile::try_from(config.logfile_path) { @@ -30,66 +34,85 @@ fn main() { Err(err) => panic!("Error: Failed to open logfile: {:?}", err), }; - let mut stats = vec![ - TeamStats::new(Team::ArizonaState), - #[allow(deprecated)] - TeamStats::new(Team::BoiseState), - TeamStats::new(Team::Colorado), - TeamStats::new(Team::Iowa), - TeamStats::new(Team::Nebraska), - TeamStats::new(Team::Syracuse), - TeamStats::new(Team::SouthCarolina), - TeamStats::new(Team::TexasAnM), - ]; + if config.no_tui { + let mut stats = vec![ + TeamStats::new(Team::ArizonaState), + #[allow(deprecated)] + TeamStats::new(Team::BoiseState), + TeamStats::new(Team::Colorado), + TeamStats::new(Team::Iowa), + TeamStats::new(Team::Nebraska), + TeamStats::new(Team::Syracuse), + TeamStats::new(Team::SouthCarolina), + TeamStats::new(Team::TexasAnM), + ]; - // Work on knocking down the nesting here? - for game in log.0.iter() { - let teams = match game.teams() { - Ok(teams) => teams, - Err(_) => continue, - }; + // Work on knocking down the nesting here? + for game in log.0.iter() { + let teams = match game.teams() { + Ok(teams) => teams, + Err(_) => continue, + }; - for team in teams { - // Skip team if they are to be ignored this game. - if game.flags.contains(&Flags::IgnoreTeam(team.to_owned())) { - continue; + for team in teams { + // Skip team if they are to be ignored this game. + if game.flags.contains(&Flags::IgnoreTeam(team.to_owned())) { + continue; + } + + let team_idx = stats + .iter() + .position(|stat| stat.team == team.to_owned()) + .unwrap(); + + stats[team_idx] + .avg_terrain_gain + .push(game.avg_gain(team.to_owned())); + + stats[team_idx] + .avg_terrain_loss + .push(game.avg_loss(team.to_owned())); + + stats[team_idx] + .avg_terrain_delta + .push(game.avg_delta(team.to_owned())); + + stats[team_idx] + .plays_per_quarter + .push(game.avg_plays_per_quarter(team.to_owned())); + + stats[team_idx] + .plays_per_game + .push(game.team_plays(team.to_owned())); + + stats[team_idx] + .penalties_per_game + .push(game.penalties(team.to_owned())); } - - let team_idx = stats - .iter() - .position(|stat| stat.team == team.to_owned()) - .unwrap(); - - stats[team_idx] - .avg_terrain_gain - .push(game.avg_gain(team.to_owned())); - - stats[team_idx] - .avg_terrain_loss - .push(game.avg_loss(team.to_owned())); - - stats[team_idx] - .avg_terrain_delta - .push(game.avg_delta(team.to_owned())); - - stats[team_idx] - .plays_per_quarter - .push(game.avg_plays_per_quarter(team.to_owned())); - - stats[team_idx] - .plays_per_game - .push(game.team_plays(team.to_owned())); - - stats[team_idx] - .penalties_per_game - .push(game.penalties(team.to_owned())); } - } - if config.display_results { // :#? for pretty-printing. stats.iter().for_each(|team| println!("{:#?}", team)); + + return Ok(()); } + + let mut app = App { exit: false }; + + // Enter Raw terminal mode. + let mut terminal = ratatui::init(); + + let (tx, rx) = mpsc::channel::(); + + let tx_input_fetcher = tx.clone(); + thread::spawn(move || tui::input_fetcher(tx_input_fetcher)); + + let app_result = app.run(&mut terminal, rx); + + // Exit Raw terminal mode. + ratatui::restore(); + + app_result } #[derive(Debug)] @@ -112,6 +135,9 @@ struct TeamStats { least_common_play: Option, most_common_key: Option, least_common_key: Option, + // Traits + // Typical number of downs to achieve 10 yards. + time_to_first_down: Option, } impl TeamStats { @@ -130,6 +156,7 @@ impl TeamStats { least_common_play: None, most_common_key: None, least_common_key: None, + time_to_first_down: None, } } } diff --git a/miller/src/tui.rs b/miller/src/tui.rs new file mode 100644 index 0000000..72a5bb7 --- /dev/null +++ b/miller/src/tui.rs @@ -0,0 +1,173 @@ +use std::{io, sync::mpsc}; + +use ratatui::{ + DefaultTerminal, Frame, + crossterm::{ + self, + event::{KeyCode, KeyEventKind}, + }, + layout::{Constraint, Layout}, + style::Color as Colour, + symbols::border, + text::Line, + widgets::{Block, Widget}, +}; + +pub enum Event { + Input(crossterm::event::KeyEvent), +} + +pub struct App { + pub exit: bool, +} + +impl App { + pub fn run( + &mut self, + terminal: &mut DefaultTerminal, + rx: mpsc::Receiver, + ) -> io::Result<()> { + while !self.exit { + // Render frame. + terminal.draw(|frame| self.draw(frame))?; + + // Event handler + // unwraps, bc what could go wrong? + match rx.recv().unwrap() { + Event::Input(key_event) => self.handle_key_event(key_event)?, + } + } + + Ok(()) + } + + pub fn draw(&self, frame: &mut Frame) { + frame.render_widget(self, frame.area()); + } + + pub fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) -> io::Result<()> { + if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') { + self.exit = true; + } + + Ok(()) + } +} + +// impl on ref to avoid accidentally mutating the struct. +impl Widget for &App { + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) + where + Self: Sized, + { + let [teams_area, main_area, instruction_area] = + Layout::vertical([Constraint::Max(3), Constraint::Fill(1), Constraint::Max(1)]) + .areas(area); + + let [left_area, right_area] = + Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]) + .areas(main_area); + + let [upper_left_area, common_play_area, common_config_area] = Layout::vertical([ + Constraint::Percentage(60), + Constraint::Percentage(20), + Constraint::Percentage(20), + ]) + .areas(left_area); + + let [quicks_area, pattern_area] = + Layout::horizontal([Constraint::Percentage(60), Constraint::Percentage(40)]) + .areas(upper_left_area); + + let [trends_area, graph_area, lower_right_area] = Layout::vertical([ + Constraint::Percentage(20), + Constraint::Percentage(55), + Constraint::Percentage(25), + ]) + .areas(right_area); + + let [legend_area, _, right_lower_right_area] = Layout::horizontal([ + Constraint::Percentage(40), + Constraint::Percentage(30), + Constraint::Percentage(30), + ]) + .areas(lower_right_area); + + let [_, compare_area] = + Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]) + .areas(right_lower_right_area); + + let teams_block = Block::bordered() + .title(Line::from(" Teams ")) + .border_set(border::THICK); + teams_block.render(teams_area, buf); + + let instructions = Line::from(vec![ + " ".into(), + "Quit ".into(), + " | ".into(), + "Function ".into(), + " | ".into(), + "Function ".into(), + " | ".into(), + "Function ".into(), + " | ".into(), + "Function ".into(), + " | ".into(), + "Function ".into(), + " | ".into(), + "Function ".into(), + " | ".into(), + "Function ".into(), + ]); + instructions.render(instruction_area, buf); + + let quicks_block = Block::bordered() + .title(" Quicks ") + .border_set(border::THICK); + quicks_block.render(quicks_area, buf); + + let pattern_block = Block::bordered() + .title(" Pattern ") + .border_set(border::THICK); + pattern_block.render(pattern_area, buf); + + let common_play_block = Block::bordered() + .title(" Most Freq. Play ") + .border_set(border::THICK); + common_play_block.render(common_play_area, buf); + + let common_config_block = Block::bordered() + .title(" Most Freq. Configuration ") + .border_set(border::THICK); + common_config_block.render(common_config_area, buf); + + let trends_block = Block::bordered() + .title(" Trends ") + .border_set(border::THICK); + trends_block.render(trends_area, buf); + + let graph_block = Block::bordered().title(" Graph ").border_set(border::THICK); + graph_block.render(graph_area, buf); + + let legend_block = Block::bordered() + .title(" Legend ") + .border_set(border::THICK); + legend_block.render(legend_area, buf); + + let compare_block = Block::bordered() + .title(" Compare ") + .border_set(border::THICK); + compare_block.render(compare_area, buf); + } +} + +pub fn input_fetcher(tx: mpsc::Sender) { + loop { + // unwraps, bc what could go wrong? + match crossterm::event::read().unwrap() { + crossterm::event::Event::Key(key_event) => tx.send(Event::Input(key_event)).unwrap(), + _ => (), + } + } +} diff --git a/zed_settings.jsonc b/zed_settings.jsonc new file mode 100644 index 0000000..87efb86 --- /dev/null +++ b/zed_settings.jsonc @@ -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, + }, + }, +}