Skip to content

Instantly share code, notes, and snippets.

@outfrost
Created July 20, 2023 16:05
Show Gist options
  • Save outfrost/5e0c73684bc9f739b44ec3080ba48874 to your computer and use it in GitHub Desktop.
Save outfrost/5e0c73684bc9f739b44ec3080ba48874 to your computer and use it in GitHub Desktop.
Some garbage to test out tournament bracket ideas
use std::{collections::BTreeMap, mem};
use rand::prelude::*;
const BIT_INDEX_DEF: [((u32, u32), u32); 28] = [
((0, 1), 0),
((0, 2), 1),
((0, 3), 2),
((0, 4), 3),
((0, 5), 4),
((0, 6), 5),
((0, 7), 6),
((1, 2), 7),
((1, 3), 8),
((1, 4), 9),
((1, 5), 10),
((1, 6), 11),
((1, 7), 12),
((2, 3), 13),
((2, 4), 14),
((2, 5), 15),
((2, 6), 16),
((2, 7), 17),
((3, 4), 18),
((3, 5), 19),
((3, 6), 20),
((3, 7), 21),
((4, 5), 22),
((4, 6), 23),
((4, 7), 24),
((5, 6), 25),
((5, 7), 26),
((6, 7), 27),
];
struct Determiner<'a> {
seed: u32,
bit_index: &'a BTreeMap<(u32, u32), u32>,
}
impl<'a> Determiner<'a> {
fn new(seed: u32, bit_index: &'a BTreeMap<(u32, u32), u32>) -> Self {
Self {
seed,
bit_index,
}
}
fn wins(&self, team_a: u32, team_b: u32) -> bool {
let mut a = team_a - 1;
let mut b = team_b - 1;
let mut swap = false;
if a > b {
mem::swap(&mut a, &mut b);
swap = true;
}
//let f_a = a as f32;
//let bit_index = b - 1 + ((6.5 * f_a) - (0.5 * f_a * f_a)) as u32;
//println!("bit_index: {}", bit_index);
(self.seed & 1 << self.bit_index[&(a, b)] != 0) ^ swap
}
fn winner(&self, pair: (u32, u32)) -> u32 {
if self.wins(pair.0, pair.1) { pair.0 } else { pair.1 }
}
fn loser(&self, pair: (u32, u32)) -> u32 {
if self.wins(pair.0, pair.1) { pair.1 } else { pair.0 }
}
fn superiority(&self, team: u32) -> u32 {
let mut result = 0u32;
for other in 1..=8 {
if team == other { continue; }
if self.wins(team, other) { result += 1; /*println!("wins against {}", other);*/ }
}
result
}
}
fn main() {
// let det = Determiner(u32::MAX);
// println!("{} {} against {}", 1, if det.wins(1, 4) { "wins" } else { "loses" }, 4);
// println!("{} {} against {}", 3, if det.wins(3, 7) { "wins" } else { "loses" }, 7);
// println!("{} {} against {}", 6, if det.wins(6, 8) { "wins" } else { "loses" }, 8);
// println!("{} {} against {}", 5, if det.wins(5, 2) { "wins" } else { "loses" }, 2);
let mut samples = 0usize;
let mut bracket_total_diff = 0u64;
let mut bracket_total_rematches = 0u64;
let mut bracket_rematches_by_round = [0u64; 6];
let bit_index = BTreeMap::from(BIT_INDEX_DEF);
let mut rng = thread_rng();
// seed pattern to account for team 1, 2, 3 always winning against 4, 5, 6, 7, 8
// XXXX XXXXXX 11 111 11111 X 11111 XX
for det in (0u32..0x2000u32)
.map(|seed| (seed & 0b11u32) | (seed & 0b100u32) << 5 | (seed & 0xFFFFFFF8u32) << 15 | 0b111111111101111100)
.map(|seed| Determiner::new(seed, &bit_index))
{
// for det in (0x0FFFFFFFu32..0x10000000u32).map(|seed| { Determiner::new(seed) }) {
// if rng.gen::<f32>() < 0.75 { continue; }
let mut ranking = [1u32, 2, 3, 4, 5, 6, 7, 8];
ranking.sort_by(|a, b| { det.superiority(*b).cmp(&det.superiority(*a)) /* numerically reverse */ });
let rank_lookup: BTreeMap<u32, i32> = ranking.iter().enumerate().map(|(i, team)| (*team, i as i32)).collect();
let round_1 = [(1, 5), (2, 6), (3, 7), (4, 8)];
let round_2 = [
(det.winner(round_1[0]), det.winner(round_1[1])),
(det.loser(round_1[0]), det.loser(round_1[1])),
(det.winner(round_1[2]), det.winner(round_1[3])),
(det.loser(round_1[2]), det.loser(round_1[3])),
];
let round_3 = [
(det.winner(round_2[0]), det.winner(round_2[3])),
(det.winner(round_2[1]), det.winner(round_2[2])),
(det.loser(round_2[1]), det.loser(round_2[2])),
(det.loser(round_2[0]), det.loser(round_2[3])),
];
let round_4 = [
(det.winner(round_3[0]), det.winner(round_3[2])),
(det.winner(round_3[1]), det.winner(round_3[3])),
(det.loser(round_3[0]), det.loser(round_3[2])),
(det.loser(round_3[1]), det.loser(round_3[3])),
];
let round_5 = [
(det.winner(round_4[0]), det.winner(round_4[1])),
(det.loser(round_4[0]), det.loser(round_4[1])),
(det.winner(round_4[2]), det.winner(round_4[3])),
(det.loser(round_4[2]), det.loser(round_4[3])),
];
let round_6 = [
(det.winner(round_5[0]), det.winner(round_5[1])),
(det.loser(round_5[0]), det.winner(round_5[2])),
(det.loser(round_5[1]), det.winner(round_5[3])),
(det.loser(round_5[2]), det.loser(round_5[3])),
];
let all_matches = [round_1, round_2, round_3, round_4, round_5, round_6].concat();
let mut total_diff = 0u32;
let mut rematches = BTreeMap::<(u32, u32), u32>::new();
let mut rematches_by_round = [0u32; 6];
for (i, pair) in all_matches.iter().enumerate() {
total_diff += (rank_lookup[&pair.0] - rank_lookup[&pair.1]).abs() as u32;
let ord_pair = if pair.0 < pair.1 { *pair } else { (pair.1, pair.0) };
if let Some(_) = rematches.get(&ord_pair) {
rematches_by_round[i / 4] += 1;
}
rematches.entry(ord_pair).and_modify(|count| *count += 1).or_insert(1);
}
bracket_total_diff += total_diff as u64;
bracket_total_rematches += rematches.values().fold(0, |acc, v| acc + std::cmp::max(v - 1, 0)) as u64;
for (i, tally) in bracket_rematches_by_round.iter_mut().enumerate() {
*tally += rematches_by_round[i] as u64;
}
samples += 1;
}
let avg_total_diff: f32 = bracket_total_diff as f32 / samples as f32;
let avg_rematches: f32 = bracket_total_rematches as f32 / samples as f32;
let avg_rematches_by_round: [f32; 6] = bracket_rematches_by_round.iter().map(|v| *v as f32 / samples as f32).collect::<Vec<f32>>().try_into().unwrap();
println!("in {} samples:", samples);
println!("{} average accumulated rank difference", avg_total_diff);
println!("{} average rematches", avg_rematches);
println!("average rematches by round: {:?}", avg_rematches_by_round);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment