import gradio as gr import json from leaderboard_utils import ( get_mario_leaderboard, get_sokoban_leaderboard, get_2048_leaderboard, get_candy_leaderboard, get_tetris_leaderboard, get_tetris_planning_leaderboard, get_combined_leaderboard, GAME_ORDER ) from data_visualization import ( get_combined_leaderboard_with_group_bar, create_horizontal_bar_chart, get_combined_leaderboard_with_single_radar ) import pandas as pd # Define time points and their corresponding data files TIME_POINTS = { "03/25/2025": "rank_data_03_25_2025.json", # Add more time points here as they become available } # Load the initial JSON file with rank data with open(TIME_POINTS["03/25/2025"], "r") as f: rank_data = json.load(f) # Add leaderboard state at the top level leaderboard_state = { "current_game": None, "previous_overall": { "Super Mario Bros": True, "Sokoban": True, "2048": True, "Candy Crash": True, "Tetris (complete)": True, "Tetris (planning only)": True }, "previous_details": { "Super Mario Bros": False, "Sokoban": False, "2048": False, "Candy Crash": False, "Tetris (complete)": False, "Tetris (planning only)": False } } def load_rank_data(time_point): """Load rank data for a specific time point""" if time_point in TIME_POINTS: try: with open(TIME_POINTS[time_point], "r") as f: return json.load(f) except FileNotFoundError: return None return None def update_leaderboard(mario_overall, mario_details, sokoban_overall, sokoban_details, _2048_overall, _2048_details, candy_overall, candy_details, tetris_overall, tetris_details, tetris_plan_overall, tetris_plan_details): global leaderboard_state # Convert current checkbox states to dictionary for easier comparison current_overall = { "Super Mario Bros": mario_overall, "Sokoban": sokoban_overall, "2048": _2048_overall, "Candy Crash": candy_overall, "Tetris (complete)": tetris_overall, "Tetris (planning only)": tetris_plan_overall } current_details = { "Super Mario Bros": mario_details, "Sokoban": sokoban_details, "2048": _2048_details, "Candy Crash": candy_details, "Tetris (complete)": tetris_details, "Tetris (planning only)": tetris_plan_details } # Find which game's state changed changed_game = None for game in current_overall.keys(): if (current_overall[game] != leaderboard_state["previous_overall"][game] or current_details[game] != leaderboard_state["previous_details"][game]): changed_game = game break if changed_game: # If a game's details checkbox was checked if current_details[changed_game] and not leaderboard_state["previous_details"][changed_game]: # Reset all other games' states for game in current_overall.keys(): if game != changed_game: current_overall[game] = False current_details[game] = False leaderboard_state["previous_overall"][game] = False leaderboard_state["previous_details"][game] = False # Update state for the selected game leaderboard_state["current_game"] = changed_game leaderboard_state["previous_overall"][changed_game] = True leaderboard_state["previous_details"][changed_game] = True current_overall[changed_game] = True # If a game's overall checkbox was checked elif current_overall[changed_game] and not leaderboard_state["previous_overall"][changed_game]: # If we were in details view for another game, switch to overall view if leaderboard_state["current_game"] and leaderboard_state["previous_details"][leaderboard_state["current_game"]]: # Reset previous game's details leaderboard_state["previous_details"][leaderboard_state["current_game"]] = False current_details[leaderboard_state["current_game"]] = False leaderboard_state["current_game"] = None # Update state leaderboard_state["previous_overall"][changed_game] = True leaderboard_state["previous_details"][changed_game] = False # If a game's overall checkbox was unchecked elif not current_overall[changed_game] and leaderboard_state["previous_overall"][changed_game]: # If we're in details view, don't allow unchecking the overall checkbox if leaderboard_state["current_game"] == changed_game: current_overall[changed_game] = True else: leaderboard_state["previous_overall"][changed_game] = False if leaderboard_state["current_game"] == changed_game: leaderboard_state["current_game"] = None # If a game's details checkbox was unchecked elif not current_details[changed_game] and leaderboard_state["previous_details"][changed_game]: leaderboard_state["previous_details"][changed_game] = False if leaderboard_state["current_game"] == changed_game: leaderboard_state["current_game"] = None # When exiting details view, reset to show all games for game in current_overall.keys(): current_overall[game] = True current_details[game] = False leaderboard_state["previous_overall"][game] = True leaderboard_state["previous_details"][game] = False # Special case: If all games are selected and we're trying to view details all_games_selected = all(current_overall.values()) and not any(current_details.values()) if all_games_selected and changed_game and current_details[changed_game]: # Reset all other games' states for game in current_overall.keys(): if game != changed_game: current_overall[game] = False current_details[game] = False leaderboard_state["previous_overall"][game] = False leaderboard_state["previous_details"][game] = False # Update state for the selected game leaderboard_state["current_game"] = changed_game leaderboard_state["previous_overall"][changed_game] = True leaderboard_state["previous_details"][changed_game] = True current_overall[changed_game] = True # Build dictionary for selected games selected_games = { "Super Mario Bros": current_overall["Super Mario Bros"], "Sokoban": current_overall["Sokoban"], "2048": current_overall["2048"], "Candy Crash": current_overall["Candy Crash"], "Tetris (complete)": current_overall["Tetris (complete)"], "Tetris (planning only)": current_overall["Tetris (planning only)"] } # Get the appropriate DataFrame and charts based on current state if leaderboard_state["current_game"]: # For detailed view if leaderboard_state["current_game"] == "Super Mario Bros": df = get_mario_leaderboard(rank_data) elif leaderboard_state["current_game"] == "Sokoban": df = get_sokoban_leaderboard(rank_data) elif leaderboard_state["current_game"] == "2048": df = get_2048_leaderboard(rank_data) elif leaderboard_state["current_game"] == "Candy Crash": df = get_candy_leaderboard(rank_data) elif leaderboard_state["current_game"] == "Tetris (complete)": df = get_tetris_leaderboard(rank_data) else: # Tetris (planning only) df = get_tetris_planning_leaderboard(rank_data) # Always create a new chart for detailed view chart = create_horizontal_bar_chart(df, leaderboard_state["current_game"]) # For detailed view, we'll use the same chart for all visualizations radar_chart = chart group_bar_chart = chart else: # For overall view df, group_bar_chart = get_combined_leaderboard_with_group_bar(rank_data, selected_games) # Use the same selected_games for radar chart _, radar_chart = get_combined_leaderboard_with_single_radar(rank_data, selected_games) chart = group_bar_chart # Return exactly 16 values to match the expected outputs return (df, chart, radar_chart, group_bar_chart, current_overall["Super Mario Bros"], current_details["Super Mario Bros"], current_overall["Sokoban"], current_details["Sokoban"], current_overall["2048"], current_details["2048"], current_overall["Candy Crash"], current_details["Candy Crash"], current_overall["Tetris (complete)"], current_details["Tetris (complete)"], current_overall["Tetris (planning only)"], current_details["Tetris (planning only)"]) def update_leaderboard_with_time(time_point, mario_overall, mario_details, sokoban_overall, sokoban_details, _2048_overall, _2048_details, candy_overall, candy_details, tetris_overall, tetris_details, tetris_plan_overall, tetris_plan_details): # Load rank data for the selected time point global rank_data new_rank_data = load_rank_data(time_point) if new_rank_data is not None: rank_data = new_rank_data # Use the existing update_leaderboard function return update_leaderboard(mario_overall, mario_details, sokoban_overall, sokoban_details, _2048_overall, _2048_details, candy_overall, candy_details, tetris_overall, tetris_details, tetris_plan_overall, tetris_plan_details) def get_initial_state(): """Get the initial state for the leaderboard""" return { "current_game": None, "previous_overall": { "Super Mario Bros": True, "Sokoban": True, "2048": True, "Candy Crash": True, "Tetris (complete)": True, "Tetris (planning only)": True }, "previous_details": { "Super Mario Bros": False, "Sokoban": False, "2048": False, "Candy Crash": False, "Tetris (complete)": False, "Tetris (planning only)": False } } def clear_filters(): global leaderboard_state # Reset all checkboxes to default state selected_games = { "Super Mario Bros": True, "Sokoban": True, "2048": True, "Candy Crash": True, "Tetris (complete)": True, "Tetris (planning only)": True } # Get the combined leaderboard and group bar chart df, group_bar_chart = get_combined_leaderboard_with_group_bar(rank_data, selected_games) # Get the radar chart using the same selected games _, radar_chart = get_combined_leaderboard_with_single_radar(rank_data, selected_games) # Reset the leaderboard state to match the default checkbox states leaderboard_state = get_initial_state() # Return exactly 16 values to match the expected outputs return (df, group_bar_chart, radar_chart, group_bar_chart, True, False, # mario True, False, # sokoban True, False, # 2048 True, False, # candy True, False, # tetris True, False) # tetris plan def create_timeline_slider(): """Create a custom timeline slider component""" timeline_html = """
03/25/2025
""" return gr.HTML(timeline_html) def create_leaderboard_tab(): """Create and return the leaderboard tab component""" with gr.Tab("🏆 Leaderboard") as leaderboard_tab: # Leaderboard header with gr.Row(): gr.Markdown("### 📊 Leaderboard Overview") # Get initial data df = get_combined_leaderboard(rank_data, {game: True for game in GAME_ORDER}) # Create interactive DataFrame component leaderboard_df = gr.DataFrame( value=df, label="Leaderboard", interactive=True, # Enable sorting and filtering wrap=True, # Enable text wrapping column_widths=["200px", "150px"] + ["100px"] * len(GAME_ORDER), # Set column widths headers=["Model", "Organization"] + GAME_ORDER, # Set column headers datatype=["str", "str"] + ["number"] * len(GAME_ORDER) # Set column types ) # Game selection section with gr.Row(): gr.Markdown("### 🎮 Game Selection") with gr.Row(): with gr.Column(): gr.Markdown("**🎮 Super Mario Bros**") mario_overall = gr.Checkbox(label="Super Mario Bros Score", value=True) mario_details = gr.Checkbox(label="Super Mario Bros Details", value=False) with gr.Column(): gr.Markdown("**📦 Sokoban**") sokoban_overall = gr.Checkbox(label="Sokoban Score", value=True) sokoban_details = gr.Checkbox(label="Sokoban Details", value=False) with gr.Column(): gr.Markdown("**🔢 2048**") _2048_overall = gr.Checkbox(label="2048 Score", value=True) _2048_details = gr.Checkbox(label="2048 Details", value=False) with gr.Column(): gr.Markdown("**🍬 Candy Crash**") candy_overall = gr.Checkbox(label="Candy Crash Score", value=True) candy_details = gr.Checkbox(label="Candy Crash Details", value=False) with gr.Column(): gr.Markdown("**🎯 Tetris (complete)**") tetris_overall = gr.Checkbox(label="Tetris (complete) Score", value=True) tetris_details = gr.Checkbox(label="Tetris (complete) Details", value=False) with gr.Column(): gr.Markdown("**📋 Tetris (planning)**") tetris_plan_overall = gr.Checkbox(label="Tetris (planning) Score", value=True) tetris_plan_details = gr.Checkbox(label="Tetris (planning) Details", value=False) # Controls with gr.Row(): with gr.Column(scale=2): gr.Markdown("**⏰ Time Tracker**") timeline = create_timeline_slider() with gr.Column(scale=1): gr.Markdown("**🔄 Controls**") clear_btn = gr.Button("Reset Filters", variant="secondary") # List of all checkboxes checkbox_list = [ mario_overall, mario_details, sokoban_overall, sokoban_details, _2048_overall, _2048_details, candy_overall, candy_details, tetris_overall, tetris_details, tetris_plan_overall, tetris_plan_details ] def update_leaderboard(*checkbox_states): # Convert checkbox states to selected games dictionary selected_games = { "Super Mario Bros": checkbox_states[0], "Sokoban": checkbox_states[2], "2048": checkbox_states[4], "Candy Crash": checkbox_states[6], "Tetris (complete)": checkbox_states[8], "Tetris (planning only)": checkbox_states[10] } # Get updated DataFrame df = get_combined_leaderboard(rank_data, selected_games) # Format scores for game in GAME_ORDER: score_col = f"{game} Score" if score_col in df.columns: df[score_col] = df[score_col].apply(lambda x: float(x) if x != '_' else 0) return df # Update leaderboard when checkboxes change for checkbox in checkbox_list: checkbox.change( update_leaderboard, inputs=checkbox_list, outputs=[leaderboard_df] ) # Reset filters when clear button is clicked def reset_filters(): # Reset all checkboxes to default state checkbox_states = [True, False] * len(GAME_ORDER) # Get DataFrame with all games selected df = get_combined_leaderboard(rank_data, {game: True for game in GAME_ORDER}) return [df] + checkbox_states clear_btn.click( reset_filters, inputs=[], outputs=[leaderboard_df] + checkbox_list ) return leaderboard_tab def make_leaderboard_md(df, last_updated_time): """ Create markdown for the gaming leaderboard """ total_models = len(df) space = "   " # Calculate total games played total_games = sum(1 for col in df.columns if col.endswith(' Score')) leaderboard_md = f""" # 🎮 Gaming Performance Leaderboard Total #models: **{total_models}**.{space} Total #games: **{total_games}**.{space} Last updated: {last_updated_time}. """ return leaderboard_md def make_category_leaderboard_md(df, game_name): """ Create markdown for a specific game category """ # Filter for models that participated in this game score_col = f"{game_name} Score" game_df = df[df[score_col] != '_'] total_models = len(game_df) # Calculate average score avg_score = game_df[score_col].astype(float).mean() space = "   " leaderboard_md = f""" ### {game_name} #### {space} #models: **{total_models}** {space} Average Score: **{avg_score:.1f}**{space} """ return leaderboard_md def make_full_leaderboard_md(): """ Create markdown explaining the leaderboard metrics """ leaderboard_md = """ The leaderboard displays performance across multiple games: - **Super Mario Bros**: Platform game performance - **Sokoban**: Puzzle-solving ability - **2048**: Number puzzle game - **Candy Crash**: Matching game - **Tetris**: Classic block-stacking game Scores are normalized within each game for fair comparison. Higher values indicate better performance. """ return leaderboard_md def create_leaderboard_table(df): """ Create a formatted table of the leaderboard """ # Select relevant columns columns = ['Player', 'Organization'] for game in GAME_ORDER: columns.append(f"{game} Score") # Create table table = df[columns].copy() # Format scores for game in GAME_ORDER: score_col = f"{game} Score" table[score_col] = table[score_col].apply(lambda x: f"{float(x):.1f}" if x != '_' else '-') return table def update_leaderboard(rank_data, selected_games): """ Update the leaderboard with new data """ # Get the combined leaderboard data df = get_combined_leaderboard(rank_data, selected_games) # Create markdown sections last_updated = pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S") leaderboard_md = make_leaderboard_md(df, last_updated) # Add category sections for game in GAME_ORDER: if selected_games.get(game, False): leaderboard_md += make_category_leaderboard_md(df, game) # Add explanation leaderboard_md += make_full_leaderboard_md() # Create table table = create_leaderboard_table(df) return leaderboard_md, table