//! Outcome of a match. //! //! In T2, only `Ranked` is supported; `Scored` will be added together with //! `MarginFactor` in T4. The enum is `#[non_exhaustive]` so adding `Scored` //! is non-breaking for downstream `match` expressions. use smallvec::SmallVec; /// Final outcome of a match. /// /// `Ranked(ranks)`: lower rank = better. Equal ranks mean a tie between those /// teams. `ranks.len()` must equal the number of teams in the event. #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub enum Outcome { Ranked(SmallVec<[u32; 4]>), } impl Outcome { /// `N`-team outcome where team `winner` won and everyone else tied for last. /// /// Panics if `winner >= n`. pub fn winner(winner: u32, n: u32) -> Self { assert!(winner < n, "winner index {winner} out of range 0..{n}"); let ranks: SmallVec<[u32; 4]> = (0..n).map(|i| if i == winner { 0 } else { 1 }).collect(); Self::Ranked(ranks) } /// All `n` teams tied. pub fn draw(n: u32) -> Self { Self::Ranked(SmallVec::from_vec(vec![0; n as usize])) } /// Explicit per-team ranking. pub fn ranking>(ranks: I) -> Self { Self::Ranked(ranks.into_iter().collect()) } pub fn team_count(&self) -> usize { match self { Self::Ranked(r) => r.len(), } } #[allow(dead_code)] pub(crate) fn as_ranks(&self) -> &[u32] { match self { Self::Ranked(r) => r, } } } #[cfg(test)] mod tests { use super::*; #[test] fn winner_two_teams() { let o = Outcome::winner(0, 2); assert_eq!(o.as_ranks(), &[0u32, 1]); assert_eq!(o.team_count(), 2); } #[test] fn winner_three_teams_second_wins() { let o = Outcome::winner(1, 3); assert_eq!(o.as_ranks(), &[1u32, 0, 1]); } #[test] fn draw_three_teams() { let o = Outcome::draw(3); assert_eq!(o.as_ranks(), &[0u32, 0, 0]); } #[test] fn ranking_from_iter() { let o = Outcome::ranking([2, 0, 1]); assert_eq!(o.as_ranks(), &[2u32, 0, 1]); } #[test] #[should_panic(expected = "winner index 2 out of range")] fn winner_out_of_range_panics() { let _ = Outcome::winner(2, 2); } }