from collections import defaultdict from datetime import datetime from statistics import median import pandas as pd def calculate_probability(numerator, denominator): """Calculate percentage probability.""" return (numerator / denominator * 100) if denominator else 0 def analyze_streaks(games_sorted, username): """Analyze win/loss streaks within the same hour.""" win_after_win_same_hour = 0 loss_after_loss_same_hour = 0 total_win_streaks_same_hour = 0 total_loss_streaks_same_hour = 0 previous_result = None previous_hour = None for game in games_sorted: end_time = game.get('end_time') if not end_time: continue hour_of_day = datetime.fromtimestamp(end_time).hour current_result = get_game_result(game, username) if not current_result: continue if previous_result and previous_hour == hour_of_day: if previous_result == 'win': total_win_streaks_same_hour += 1 if current_result == 'win': win_after_win_same_hour += 1 elif previous_result == 'loss': total_loss_streaks_same_hour += 1 if current_result == 'loss': loss_after_loss_same_hour += 1 previous_result = current_result previous_hour = hour_of_day print(f"Total win streaks: {total_win_streaks_same_hour}, Wins after win: {win_after_win_same_hour}") print(f"Total loss streaks: {total_loss_streaks_same_hour}, Losses after loss: {loss_after_loss_same_hour}") win_probability = calculate_probability(win_after_win_same_hour, total_win_streaks_same_hour) loss_probability = calculate_probability(loss_after_loss_same_hour, total_loss_streaks_same_hour) return win_probability, loss_probability def analyze_sequences(games_sorted, username): """Analyze 'win-loss' and 'loss-win' sequences within the same hour.""" win_after_win_loss_same_hour = 0 win_after_loss_win_same_hour = 0 total_win_loss_sequences_same_hour = 0 total_loss_win_sequences_same_hour = 0 previous_result = None previous_hour = None for i, game in enumerate(games_sorted): end_time = game.get('end_time') if not end_time: continue hour_of_day = datetime.fromtimestamp(end_time).hour current_result = get_game_result(game, username) if not current_result: continue if previous_result and previous_hour == hour_of_day: if previous_result == 'win' and current_result == 'loss': total_win_loss_sequences_same_hour += 1 next_game_result = get_game_result(games_sorted[i + 1], username) if i + 1 < len(games_sorted) else None if next_game_result == 'win': win_after_win_loss_same_hour += 1 elif previous_result == 'loss' and current_result == 'win': total_loss_win_sequences_same_hour += 1 next_game_result = get_game_result(games_sorted[i + 1], username) if i + 1 < len(games_sorted) else None if next_game_result == 'win': win_after_loss_win_same_hour += 1 previous_result = current_result previous_hour = hour_of_day print(f"Total 'win-loss' sequences: {total_win_loss_sequences_same_hour}, Wins after 'win-loss': {win_after_win_loss_same_hour}") print(f"Total 'loss-win' sequences: {total_loss_win_sequences_same_hour}, Wins after 'loss-win': {win_after_loss_win_same_hour}") win_after_win_loss_probability = calculate_probability(win_after_win_loss_same_hour, total_win_loss_sequences_same_hour) win_after_loss_win_probability = calculate_probability(win_after_loss_win_same_hour, total_loss_win_sequences_same_hour) return win_after_win_loss_probability, win_after_loss_win_probability def analyze_games(games, username): """Analyze games by month and return statistics.""" games_per_month = defaultdict(int) stats_per_month = defaultdict(lambda: defaultdict(int)) total_games = 0 total_wins = 0 total_losses = 0 total_timeouts = 0 for game in games: end_time = game.get('end_time') if not end_time: continue end_datetime = datetime.fromtimestamp(end_time) month = end_datetime.strftime('%Y-%m') result = get_game_result(game, username) if result == 'win': stats_per_month[month]['wins'] += 1 total_wins += 1 elif result == 'timeout': stats_per_month[month]['timeouts'] += 1 total_timeouts += 1 stats_per_month[month]['losses'] += 1 # Count timeout as a loss total_losses += 1 elif result == 'loss': stats_per_month[month]['losses'] += 1 total_losses += 1 games_per_month[month] += 1 total_games += 1 total_months_played = len(games_per_month) return games_per_month, stats_per_month, total_games, total_wins, total_losses, total_timeouts, total_months_played def generate_monthly_report(games_per_month, stats_per_month): """Generate a Pandas DataFrame report for monthly analysis including Timeout Rate.""" data = [] for month, total_games in games_per_month.items(): wins = stats_per_month[month]['wins'] losses = stats_per_month[month]['losses'] timeouts = stats_per_month[month]['timeouts'] win_rate = (wins / total_games * 100) if total_games else 0 loss_rate = (losses / total_games * 100) if total_games else 0 timeout_rate = (timeouts / total_games * 100) if total_games else 0 data.append({ 'Month': month, 'Games Played': total_games, 'Wins': wins, 'Losses': losses, 'Win Rate (%)': round(win_rate, 1), 'Loss Rate (%)': round(loss_rate, 1), 'Timeout Rate (%)': round(timeout_rate, 1) }) return pd.DataFrame(data) def get_game_result(game, username): """Determine if the user won or lost the game.""" result = None if game.get('white', {}).get('username') == username: result = game.get('white', {}).get('result') elif game.get('black', {}).get('username') == username: result = game.get('black', {}).get('result') if result == 'win': return 'win' elif result == 'timeout': return 'timeout' # Explicitly return "timeout" elif result in ['checkmated', 'resigned', 'lose', 'abandoned']: return 'loss' return None def calculate_average_and_median_games(games): """Calculate the average and median number of games played per day.""" games_per_day = defaultdict(int) for game in games: end_time = game.get('end_time') if not end_time: continue end_date = datetime.fromtimestamp(end_time).date() games_per_day[end_date] += 1 total_days = len(games_per_day) total_games = sum(games_per_day.values()) average_games = total_games / total_days if total_days else 0 median_games = median(games_per_day.values()) if total_days else 0 return average_games, median_games def format_duration(total_months): """Format the duration as 'X years, Y months'.""" years = total_months // 12 months = total_months % 12 if years > 0 and months > 0: return f"{years} year(s), {months} month(s)" elif years > 0: return f"{years} year(s)" else: return f"{months} month(s)"