simulation works
This commit is contained in:
31
src/main.rs
31
src/main.rs
@@ -24,6 +24,13 @@ impl Api {
|
|||||||
.expect("team abbrev not found")
|
.expect("team abbrev not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_team_by_id(&self, team_id: u32) -> &Team {
|
||||||
|
self.teams
|
||||||
|
.iter()
|
||||||
|
.find(|t| t.id == team_id)
|
||||||
|
.expect("team id not found")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_points(&self, team_id: u32, past: bool) -> u32 {
|
pub fn get_points(&self, team_id: u32, past: bool) -> u32 {
|
||||||
if !past {
|
if !past {
|
||||||
self.standings
|
self.standings
|
||||||
@@ -163,8 +170,18 @@ impl<'m> MatchupPre<'m> {
|
|||||||
{
|
{
|
||||||
home_team
|
home_team
|
||||||
} else {
|
} else {
|
||||||
// TODO: simulation
|
const TIMES: u32 = 50_000;
|
||||||
home_team
|
if self.is_result {
|
||||||
|
simulation::pick_ideal_winner(
|
||||||
|
a.api,
|
||||||
|
a.my_team,
|
||||||
|
&a.api.past_standings,
|
||||||
|
self.game,
|
||||||
|
TIMES,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
simulation::pick_ideal_winner(a.api, a.my_team, &a.api.standings, self.game, TIMES)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Matchup {
|
Matchup {
|
||||||
@@ -177,11 +194,11 @@ impl<'m> MatchupPre<'m> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> reqwest::Result<()> {
|
fn main() -> reqwest::Result<()> {
|
||||||
let teams = nhlapi::teams::get()?;
|
let teams = nhlapi::teams::get().expect("error getting teams");
|
||||||
let past_standings = nhlapi::standings::yesterday()?;
|
let past_standings = nhlapi::standings::yesterday().expect("error getting past standings");
|
||||||
let standings = nhlapi::standings::today()?;
|
let standings = nhlapi::standings::today().expect("error getting standings");
|
||||||
let results = nhlapi::schedule::yesterday()?;
|
let results = nhlapi::schedule::yesterday().expect("error getting results");
|
||||||
let games = nhlapi::schedule::today()?;
|
let games = nhlapi::schedule::today().expect("error getting games");
|
||||||
|
|
||||||
let api = Api {
|
let api = Api {
|
||||||
teams,
|
teams,
|
||||||
|
|||||||
0
src/markdown.rs
Normal file
0
src/markdown.rs
Normal file
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
//! Docs: https://gitlab.com/dword4/nhlapi
|
//! Docs: https://gitlab.com/dword4/nhlapi
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@@ -116,7 +117,7 @@ pub mod schedule {
|
|||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let mut root: Root = client
|
let mut root: Root = client
|
||||||
.get("https://statsapi.web.nhl.com/api/v1/schedule")
|
.get("https://statsapi.web.nhl.com/api/v1/schedule?expand=schedule.linescore")
|
||||||
.query(&[("date", date)])
|
.query(&[("date", date)])
|
||||||
.send()?
|
.send()?
|
||||||
.json()?;
|
.json()?;
|
||||||
@@ -208,8 +209,6 @@ pub mod standings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod teams {
|
pub mod teams {
|
||||||
use std::cmp;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
|
use std::cmp::Reverse;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
|
||||||
use crate::nhlapi::LeagueRecord;
|
use crate::nhlapi;
|
||||||
|
use crate::nhlapi::schedule::Game;
|
||||||
|
use crate::nhlapi::standings::TeamRecord;
|
||||||
|
use crate::nhlapi::teams::Team;
|
||||||
|
use crate::Api;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
struct Entry {
|
struct Entry {
|
||||||
@@ -10,6 +17,7 @@ struct Entry {
|
|||||||
wins: u32,
|
wins: u32,
|
||||||
losses: u32,
|
losses: u32,
|
||||||
ot: u32,
|
ot: u32,
|
||||||
|
games_played: u32,
|
||||||
points: u32,
|
points: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,15 +28,147 @@ enum Event {
|
|||||||
Ot,
|
Ot,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_event(rec: &LeagueRecord) -> Event {
|
impl Event {
|
||||||
|
fn points(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Event::Win => 2,
|
||||||
|
Event::Loss => 0,
|
||||||
|
Event::Ot => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_event(base: &Entry) -> Event {
|
||||||
[
|
[
|
||||||
(Event::Win, rec.wins),
|
(Event::Win, base.wins),
|
||||||
(Event::Loss, rec.losses),
|
(Event::Loss, base.losses),
|
||||||
(Event::Ot, rec.ot),
|
(Event::Ot, base.ot),
|
||||||
]
|
]
|
||||||
.choose_weighted(&mut rand::thread_rng(), |x| x.1)
|
.choose_weighted(&mut rand::thread_rng(), |x| x.1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Simulation {}
|
pub fn pick_ideal_winner<'a>(
|
||||||
|
api: &'a Api,
|
||||||
|
my_team: &'a Team,
|
||||||
|
records: &'a [TeamRecord],
|
||||||
|
game: &'a Game,
|
||||||
|
times: u32,
|
||||||
|
) -> &'a nhlapi::Team {
|
||||||
|
let mut home_win_sim = Simulation::new(api, my_team, records);
|
||||||
|
home_win_sim.give_team_win(game.home_team().id);
|
||||||
|
home_win_sim.give_team_loss(game.away_team().id);
|
||||||
|
let mut home_win_x = 0;
|
||||||
|
for _ in 0..times {
|
||||||
|
if home_win_sim.run() {
|
||||||
|
home_win_x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut away_win_sim = Simulation::new(api, my_team, records);
|
||||||
|
away_win_sim.give_team_win(game.away_team().id);
|
||||||
|
away_win_sim.give_team_loss(game.home_team().id);
|
||||||
|
let mut away_win_x = 0;
|
||||||
|
for _ in 0..times {
|
||||||
|
if away_win_sim.run() {
|
||||||
|
away_win_x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"{} ({}) at {} ({})",
|
||||||
|
game.away_team().name,
|
||||||
|
away_win_x,
|
||||||
|
game.home_team().name,
|
||||||
|
home_win_x
|
||||||
|
);
|
||||||
|
|
||||||
|
if home_win_x > away_win_x {
|
||||||
|
game.home_team()
|
||||||
|
} else {
|
||||||
|
game.away_team()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Simulation<'a> {
|
||||||
|
my_team: &'a Team,
|
||||||
|
base: Vec<Entry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Simulation<'_> {
|
||||||
|
pub fn new<'a>(api: &'a Api, my_team: &'a Team, records: &'a [TeamRecord]) -> Simulation<'a> {
|
||||||
|
let mut base = Vec::new();
|
||||||
|
for record in records {
|
||||||
|
let team = api.get_team_by_id(record.team.id);
|
||||||
|
if team.conference.id == my_team.conference.id {
|
||||||
|
base.push(Entry {
|
||||||
|
team_id: team.id,
|
||||||
|
division_id: team.division.id,
|
||||||
|
conference_id: team.conference.id,
|
||||||
|
wins: record.league_record.wins,
|
||||||
|
losses: record.league_record.losses,
|
||||||
|
ot: record.league_record.ot,
|
||||||
|
games_played: record.games_played,
|
||||||
|
points: record.points,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Simulation { my_team, base }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn give_team_win(&mut self, team_id: u32) {
|
||||||
|
if let Some(entry) = self.base.iter_mut().find(|x| x.team_id == team_id) {
|
||||||
|
entry.wins += 1;
|
||||||
|
entry.points += 2;
|
||||||
|
entry.games_played += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn give_team_loss(&mut self, team_id: u32) {
|
||||||
|
if let Some(entry) = self.base.iter_mut().find(|x| x.team_id == team_id) {
|
||||||
|
entry.losses += 1;
|
||||||
|
entry.games_played += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&self) -> bool {
|
||||||
|
let mut entries = self.base.clone();
|
||||||
|
for (base, entry) in self.base.iter().zip(entries.iter_mut()) {
|
||||||
|
while entry.games_played < 82 {
|
||||||
|
let event = random_event(base);
|
||||||
|
entry.games_played += 1;
|
||||||
|
entry.points += event.points();
|
||||||
|
match event {
|
||||||
|
Event::Win => entry.wins += 1,
|
||||||
|
Event::Loss => entry.losses += 1,
|
||||||
|
Event::Ot => entry.ot += 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.sort_unstable_by_key(|e| Reverse((e.points, e.wins)));
|
||||||
|
|
||||||
|
let top_3_teams: BTreeSet<u32> = entries
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.division_id == self.my_team.division.id)
|
||||||
|
.take(3)
|
||||||
|
.map(|x| x.team_id)
|
||||||
|
.chain(
|
||||||
|
entries
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.division_id != self.my_team.division.id)
|
||||||
|
.take(3)
|
||||||
|
.map(|x| x.team_id),
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let wildcard: BTreeSet<u32> = entries
|
||||||
|
.iter()
|
||||||
|
.filter(|x| !top_3_teams.contains(&x.team_id))
|
||||||
|
.take(2)
|
||||||
|
.map(|x| x.team_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
top_3_teams.contains(&self.my_team.id) || wildcard.contains(&self.my_team.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user