// Simple in-memory demo without database for testing use crate::demo::{self, Player, MatchType}; use crate::glicko::{Glicko2Calculator, calculate_weighted_score}; use rand::Rng; pub async fn run_simple_demo() { println!("šŸ“ Pickleball ELO Tracker v2.0 - Simple Demo"); println!("==========================================\n"); // Generate 20 players println!("šŸ‘„ Generating 20 players..."); let mut players: Vec = (0..20) .map(|_| Player::new_random()) .collect(); println!("āœ… Players generated\n"); println!("Session 1: Opening Tournament"); println!("=============================\n"); run_session(&mut players, 1, 50); println!("\nSession 2: Mid-Tournament"); println!("========================\n"); run_session(&mut players, 2, 55); println!("\nSession 3: Finals"); println!("================\n"); run_session(&mut players, 3, 52); // Print final leaderboards println!("\nšŸ“§ Final Leaderboards:\n"); print_leaderboard(&players); println!("\nāœ… Demo Complete!"); println!("\nTotal Players: {}", players.len()); println!("Total Matches Across 3 Sessions: {}", 50 + 55 + 52); } fn run_session(players: &mut [Player], session_num: usize, num_matches: usize) { println!("Starting session {}...", session_num); let mut rng = rand::thread_rng(); let calc = Glicko2Calculator::new(); for i in 0..num_matches { if i % 20 == 0 && i > 0 { println!(" {} matches completed...", i); } // Randomly choose singles or doubles let match_type = if rng.gen_bool(0.5) { MatchType::Singles } else { MatchType::Doubles }; match match_type { MatchType::Singles => { // Pick 2 random players let p1_idx = rng.gen_range(0..players.len()); let mut p2_idx = rng.gen_range(0..players.len()); while p2_idx == p1_idx { p2_idx = rng.gen_range(0..players.len()); } let (team1_score, team2_score) = demo::simulate_match( &[players[p1_idx].true_skill], &[players[p2_idx].true_skill], ); // Calculate outcomes with performance-based weighting let p1_outcome = if team1_score > team2_score { calculate_weighted_score(players[p1_idx].singles.rating, players[p2_idx].singles.rating, team1_score, team2_score) } else { calculate_weighted_score(players[p1_idx].singles.rating, players[p2_idx].singles.rating, team1_score, team2_score) }; let p2_outcome = if team1_score > team2_score { calculate_weighted_score(players[p2_idx].singles.rating, players[p1_idx].singles.rating, team2_score, team1_score) } else { calculate_weighted_score(players[p2_idx].singles.rating, players[p1_idx].singles.rating, team2_score, team1_score) }; // Update ratings players[p1_idx].singles = calc.update_rating( &players[p1_idx].singles, &[(players[p2_idx].singles, p1_outcome)], ); players[p2_idx].singles = calc.update_rating( &players[p2_idx].singles, &[(players[p1_idx].singles, p2_outcome)], ); } MatchType::Doubles => { // Pick 4 random players let mut indices: Vec = (0..players.len()).collect(); use rand::seq::SliceRandom; indices[..].shuffle(&mut rng); let team1_indices = vec![indices[0], indices[1]]; let team2_indices = vec![indices[2], indices[3]]; let team1_skills: Vec = team1_indices.iter() .map(|&i| players[i].true_skill) .collect(); let team2_skills: Vec = team2_indices.iter() .map(|&i| players[i].true_skill) .collect(); let (team1_score, team2_score) = demo::simulate_match(&team1_skills, &team2_skills); let team1_won = team1_score > team2_score; // Update team 1 for &idx in &team1_indices { let outcome = if team1_won { calculate_weighted_score(players[idx].doubles.rating, team2_indices.iter().map(|&i| players[i].doubles.rating).sum::() / 2.0, team1_score, team2_score) } else { calculate_weighted_score(players[idx].doubles.rating, team2_indices.iter().map(|&i| players[i].doubles.rating).sum::() / 2.0, team1_score, team2_score) }; let avg_opponent = crate::glicko::GlickoRating { rating: team2_indices.iter().map(|&i| players[i].doubles.rating).sum::() / 2.0, rd: team2_indices.iter().map(|&i| players[i].doubles.rd).sum::() / 2.0, volatility: 0.06, }; players[idx].doubles = calc.update_rating( &players[idx].doubles, &[(avg_opponent, outcome)], ); } // Update team 2 for &idx in &team2_indices { let outcome = if !team1_won { calculate_weighted_score(players[idx].doubles.rating, team1_indices.iter().map(|&i| players[i].doubles.rating).sum::() / 2.0, team2_score, team1_score) } else { calculate_weighted_score(players[idx].doubles.rating, team1_indices.iter().map(|&i| players[i].doubles.rating).sum::() / 2.0, team2_score, team1_score) }; let avg_opponent = crate::glicko::GlickoRating { rating: team1_indices.iter().map(|&i| players[i].doubles.rating).sum::() / 2.0, rd: team1_indices.iter().map(|&i| players[i].doubles.rd).sum::() / 2.0, volatility: 0.06, }; players[idx].doubles = calc.update_rating( &players[idx].doubles, &[(avg_opponent, outcome)], ); } } } } println!("āœ… Completed {} matches", num_matches); print_leaderboard(players); } fn print_leaderboard(players: &[Player]) { println!("\nšŸ“Š Top 5 Singles:"); let mut singles_sorted = players.to_vec(); singles_sorted.sort_by(|a, b| b.singles.rating.partial_cmp(&a.singles.rating).unwrap()); for (i, p) in singles_sorted.iter().take(5).enumerate() { println!( " {}. {} - {:.1} (RD: {:.1})", i + 1, p.name, p.singles.rating, p.singles.rd, ); } println!("\nšŸ“Š Top 5 Doubles:"); let mut doubles_sorted = players.to_vec(); doubles_sorted.sort_by(|a, b| b.doubles.rating.partial_cmp(&a.doubles.rating).unwrap()); for (i, p) in doubles_sorted.iter().take(5).enumerate() { println!( " {}. {} - {:.1} (RD: {:.1})", i + 1, p.name, p.doubles.rating, p.doubles.rd, ); } }