52482eea5f
Adds EventBuilder::scores_with_sigma, the fluent-builder ergonomic mirror of Outcome::scores_with_sigma. Lets users write h.event(t).team(...).team(...).scores_with_sigma([..], sigma).commit() to set a per-event score_sigma override.
110 lines
3.4 KiB
Rust
110 lines
3.4 KiB
Rust
use smallvec::SmallVec;
|
|
|
|
use crate::{
|
|
InferenceError, Outcome,
|
|
drift::Drift,
|
|
event::{Event, Member, Team},
|
|
history::History,
|
|
observer::Observer,
|
|
time::Time,
|
|
};
|
|
|
|
pub struct EventBuilder<'h, T, D, O, K>
|
|
where
|
|
T: Time,
|
|
D: Drift<T>,
|
|
O: Observer<T>,
|
|
K: Eq + std::hash::Hash + Clone,
|
|
{
|
|
history: &'h mut History<T, D, O, K>,
|
|
event: Event<T, K>,
|
|
current_team_idx: Option<usize>,
|
|
}
|
|
|
|
impl<'h, T, D, O, K> EventBuilder<'h, T, D, O, K>
|
|
where
|
|
T: Time,
|
|
D: Drift<T>,
|
|
O: Observer<T>,
|
|
K: Eq + std::hash::Hash + Clone,
|
|
{
|
|
pub(crate) fn new(history: &'h mut History<T, D, O, K>, time: T) -> Self {
|
|
Self {
|
|
history,
|
|
event: Event {
|
|
time,
|
|
teams: SmallVec::new(),
|
|
outcome: Outcome::Ranked(SmallVec::new()),
|
|
},
|
|
current_team_idx: None,
|
|
}
|
|
}
|
|
|
|
/// Add a team by its member keys (weight 1.0 each, no prior overrides).
|
|
pub fn team<I: IntoIterator<Item = K>>(mut self, keys: I) -> Self {
|
|
let members: SmallVec<[Member<K>; 4]> = keys.into_iter().map(Member::new).collect();
|
|
self.event.teams.push(Team { members });
|
|
self.current_team_idx = Some(self.event.teams.len() - 1);
|
|
self
|
|
}
|
|
|
|
/// Set per-member weights for the most recently added team.
|
|
///
|
|
/// Panics in debug builds if called before `.team(...)` or if the length
|
|
/// doesn't match the team's member count.
|
|
pub fn weights<I: IntoIterator<Item = f64>>(mut self, weights: I) -> Self {
|
|
let idx = self
|
|
.current_team_idx
|
|
.expect(".weights(...) called before any .team(...)");
|
|
let ws: Vec<f64> = weights.into_iter().collect();
|
|
let team = &mut self.event.teams[idx];
|
|
debug_assert_eq!(
|
|
ws.len(),
|
|
team.members.len(),
|
|
"weights length must match team size"
|
|
);
|
|
for (m, w) in team.members.iter_mut().zip(ws) {
|
|
m.weight = w;
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Set explicit ranks per team (length must equal number of teams).
|
|
pub fn ranking<I: IntoIterator<Item = u32>>(mut self, ranks: I) -> Self {
|
|
self.event.outcome = Outcome::ranking(ranks);
|
|
self
|
|
}
|
|
|
|
/// Set explicit per-team continuous scores; higher = better.
|
|
pub fn scores<I: IntoIterator<Item = f64>>(mut self, scores: I) -> Self {
|
|
self.event.outcome = crate::Outcome::scores(scores);
|
|
self
|
|
}
|
|
|
|
/// Set explicit per-team continuous scores with a per-event noise override.
|
|
///
|
|
/// `sigma` overrides `HistoryBuilder::score_sigma` for this event only.
|
|
/// Must be `> 0.0`; debug-asserts otherwise via `Outcome::scores_with_sigma`.
|
|
pub fn scores_with_sigma<I: IntoIterator<Item = f64>>(mut self, scores: I, sigma: f64) -> Self {
|
|
self.event.outcome = crate::Outcome::scores_with_sigma(scores, sigma);
|
|
self
|
|
}
|
|
|
|
/// Mark team `winner_idx` as winner; others tied for last.
|
|
pub fn winner(mut self, winner_idx: u32) -> Self {
|
|
self.event.outcome = Outcome::winner(winner_idx, self.event.teams.len() as u32);
|
|
self
|
|
}
|
|
|
|
/// All teams tied.
|
|
pub fn draw(mut self) -> Self {
|
|
self.event.outcome = Outcome::draw(self.event.teams.len() as u32);
|
|
self
|
|
}
|
|
|
|
/// Commit the event to the history.
|
|
pub fn commit(self) -> Result<(), InferenceError> {
|
|
self.history.add_events(std::iter::once(self.event))
|
|
}
|
|
}
|