simulation works

This commit is contained in:
2019-03-07 23:40:20 -05:00
parent cd548eaa01
commit f785062d70
4 changed files with 172 additions and 16 deletions

View File

@@ -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
View File

View 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)]

View File

@@ -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)
}
}