use std::{
collections::{BinaryHeap, HashSet},
iter::Sum,
ops::Add,
};
use crate::{
datatypes::Index,
reporting::{AssessReport, BasicReport, Report},
simulation::{Simulation, SwissSystem},
};
#[derive(Debug, Clone, Copy)]
struct Candidate {
index: Index,
probability: f32,
}
impl std::hash::Hash for Candidate {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.index.hash(state);
}
}
impl Eq for Candidate {}
impl PartialEq for Candidate {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
impl Ord for Candidate {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.probability.total_cmp(&other.probability)
}
}
impl PartialOrd for Candidate {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct PicksReport {
pub basic: BasicReport,
}
impl Add for PicksReport {
type Output = Self;
fn add(mut self, rhs: Self) -> Self::Output {
self.basic = self.basic + rhs.basic;
self
}
}
impl Sum for PicksReport {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::default(), |acc, report| acc + report)
}
}
impl Report for PicksReport {
fn update(&mut self, ss: &SwissSystem) {
self.basic.update(ss);
}
fn format(&self, sim: &Simulation) -> String {
let [three_zero, advancing, zero_three] = self.basic.calculate_probabilities(sim);
let candidates = |probabilities: [f32; 16]| -> BinaryHeap<Candidate> {
probabilities
.iter()
.enumerate()
.map(|(i, p)| Candidate {
index: unsafe { Index::from_usize(i) },
probability: *p,
})
.collect::<BinaryHeap<_>>()
};
let mut three_zero_candidates = candidates(three_zero);
let mut advancing_candidates = candidates(advancing);
let mut zero_three_candidates = candidates(zero_three);
let mut advancing_picks = HashSet::new();
for _ in 0..6 {
if let Some(candidate) = advancing_candidates.pop() {
advancing_picks.insert(candidate);
}
}
let mut zero_three_picks = HashSet::new();
for _ in 0..2 {
if let Some(candidate) = zero_three_candidates.pop() {
zero_three_picks.insert(candidate);
}
}
let mut three_zero_picks = HashSet::new();
while three_zero_picks.len() < 2 {
let mut swap_candidates = BinaryHeap::new();
while let Some(candidate) = three_zero_candidates.peek()
&& advancing_picks.contains(candidate)
{
swap_candidates.push(three_zero_candidates.pop().unwrap());
}
match (
three_zero_candidates.pop(),
swap_candidates.pop(),
advancing_candidates.pop(),
) {
(Some(next_three_zero), Some(next_swap), Some(next_advancing))
if advancing_picks.contains(&next_swap) =>
{
let swap_advancing = advancing_picks.get(&next_swap).unwrap();
let cost = next_advancing.probability - swap_advancing.probability;
let reward = next_swap.probability - next_three_zero.probability;
if reward > cost {
three_zero_picks.insert(next_swap);
advancing_picks.remove(&next_swap);
advancing_picks.insert(next_advancing);
three_zero_candidates.push(next_three_zero);
} else {
three_zero_picks.insert(next_three_zero);
advancing_candidates.push(next_advancing);
}
}
(Some(next_three_zero), None, Some(next_advancing)) => {
three_zero_picks.insert(next_three_zero);
advancing_candidates.push(next_advancing);
}
(Some(next_three_zero), None, None) => {
three_zero_picks.insert(next_three_zero);
}
state => {
unreachable!("invalid state for picking 3-0 teams:\n\n{state:#?}");
}
}
}
let assessment = sim.run(AssessReport::new(
three_zero_picks.iter().map(|c| c.index),
advancing_picks.iter().map(|c| c.index),
zero_three_picks.iter().map(|c| c.index),
));
let format_picks = |out: &mut Vec<String>, picks: &HashSet<Candidate>| {
let mut picks = picks
.iter()
.map(|i| (&sim.teams.names[i.index.to_usize()], i.probability * 100.0))
.collect::<Vec<_>>();
picks.sort_by(|(_, a), (_, b)| b.total_cmp(a));
for (i, (name, p)) in picks.into_iter().enumerate() {
out.push(format!(
"{num:<4}{name:<20}{p:>6.1}%",
num = format!("{}.", i + 1),
));
}
};
let mut out = vec![String::from("\n3-0 picks:")];
format_picks(&mut out, &three_zero_picks);
out.push(String::from("\n3-1 or 3-2 picks:"));
format_picks(&mut out, &advancing_picks);
out.push(String::from("\n0-3 picks:"));
format_picks(&mut out, &zero_three_picks);
out.push(assessment.format(sim));
out.join("\n")
}
}