Yuxuan-Zhang-Dexter commited on
Commit
342518d
·
1 Parent(s): 1263816

update leaderboard

Browse files
Files changed (4) hide show
  1. app.py +36 -13
  2. data_visualization.py +75 -23
  3. gallery_tab.py +753 -231
  4. requirements.txt +2 -1
app.py CHANGED
@@ -33,7 +33,6 @@ from gallery_tab import create_video_gallery
33
  # Try to import enhanced leaderboard, use standard DataFrame if not available
34
 
35
  from gradio_leaderboard import Leaderboard, SelectColumns, ColumnFilter
36
-
37
  HAS_ENHANCED_LEADERBOARD = True
38
 
39
 
@@ -86,6 +85,10 @@ def load_rank_data(time_point):
86
  return None
87
  return None
88
 
 
 
 
 
89
  # Function to prepare DataFrame for display
90
  def prepare_dataframe_for_display(df, for_game=None):
91
  """Format DataFrame for better display in the UI"""
@@ -623,11 +626,14 @@ def build_app():
623
 
624
  # Add leaderboard search box in its own row
625
  with gr.Row():
626
- search_box = gr.Textbox(
627
- label="🔍 Search by Player or Organization",
628
- placeholder="Type to filter the table...",
629
- show_label=True
630
- )
 
 
 
631
 
632
  # Get initial leaderboard dataframe
633
  initial_df = get_combined_leaderboard(rank_data, {
@@ -654,16 +660,23 @@ def build_app():
654
  height=800
655
  )
656
 
 
 
 
 
657
  # Add search functionality
658
  def filter_table(search_term, current_df):
659
- if not search_term:
660
- return current_df
 
 
 
 
 
 
 
 
661
 
662
- # Filter the DataFrame by Player or Organization
663
- filtered_df = current_df[
664
- current_df["Player"].str.contains(search_term, case=False) |
665
- current_df["Organization"].str.contains(search_term, case=False)
666
- ]
667
  return filtered_df
668
 
669
  # Connect search box to the table
@@ -673,6 +686,16 @@ def build_app():
673
  outputs=[leaderboard_df]
674
  )
675
 
 
 
 
 
 
 
 
 
 
 
676
  # List of all checkboxes
677
  checkbox_list = [
678
  mario_overall, mario_details,
 
33
  # Try to import enhanced leaderboard, use standard DataFrame if not available
34
 
35
  from gradio_leaderboard import Leaderboard, SelectColumns, ColumnFilter
 
36
  HAS_ENHANCED_LEADERBOARD = True
37
 
38
 
 
85
  return None
86
  return None
87
 
88
+ # Add a note about score values
89
+ def add_score_note():
90
+ return gr.Markdown("*Note: '-1' in the table indicates no data point for that model.*", elem_classes="score-note")
91
+
92
  # Function to prepare DataFrame for display
93
  def prepare_dataframe_for_display(df, for_game=None):
94
  """Format DataFrame for better display in the UI"""
 
626
 
627
  # Add leaderboard search box in its own row
628
  with gr.Row():
629
+ with gr.Column(scale=8):
630
+ search_box = gr.Textbox(
631
+ label="🔍 Search by Player or Organization",
632
+ placeholder="Type to filter the table...",
633
+ show_label=True
634
+ )
635
+ with gr.Column(scale=1):
636
+ search_clear_btn = gr.Button("Clear", variant="secondary")
637
 
638
  # Get initial leaderboard dataframe
639
  initial_df = get_combined_leaderboard(rank_data, {
 
660
  height=800
661
  )
662
 
663
+ # Add the score note below the table
664
+ with gr.Row():
665
+ score_note = add_score_note()
666
+
667
  # Add search functionality
668
  def filter_table(search_term, current_df):
669
+ # Create a fresh copy of the DataFrame to avoid modifying the original
670
+ filtered_df = current_df.copy()
671
+
672
+ # Only apply search if a term is provided
673
+ if search_term:
674
+ # Filter the DataFrame by Player or Organization
675
+ filtered_df = filtered_df[
676
+ filtered_df["Player"].str.contains(search_term, case=False) |
677
+ filtered_df["Organization"].str.contains(search_term, case=False)
678
+ ]
679
 
 
 
 
 
 
680
  return filtered_df
681
 
682
  # Connect search box to the table
 
686
  outputs=[leaderboard_df]
687
  )
688
 
689
+ # Clear search button functionality
690
+ def clear_search():
691
+ return "", initial_display_df
692
+
693
+ search_clear_btn.click(
694
+ clear_search,
695
+ inputs=[],
696
+ outputs=[search_box, leaderboard_df]
697
+ )
698
+
699
  # List of all checkboxes
700
  checkbox_list = [
701
  mario_overall, mario_details,
data_visualization.py CHANGED
@@ -30,11 +30,12 @@ def get_model_prefix(name):
30
  return name.split('-')[0]
31
 
32
 
33
- def normalize_values(values, mean, std):
34
- if std == 0:
35
- return [50 if v > 0 else 0 for v in values]
36
- z_scores = [(v - mean) / std for v in values]
37
- return [max(0, min(100, (z * 30) + 50)) for z in z_scores]
 
38
 
39
  def simplify_model_name(name):
40
  if name == "claude-3-7-sonnet-20250219(thinking)":
@@ -174,37 +175,85 @@ def get_combined_leaderboard_with_radar(rank_data, selected_games):
174
  return df, create_radar_charts(df_viz)
175
 
176
  def create_group_bar_chart(df):
177
- active_games = [g for g in GAME_ORDER if f"{g} Score" in df.columns]
178
- game_cols = [f"{g} Score" for g in active_games]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
- for col in game_cols:
181
- vals = df[col].replace("_", 0).astype(float)
182
- mean, std = vals.mean(), vals.std()
183
- df[f"norm_{col}"] = normalize_values(vals, mean, std)
184
 
 
185
  fig = go.Figure()
186
- for _, row in df.iterrows():
187
- player = row["Player"]
188
- color = MODEL_COLORS.get(player, '#808080') # Default to gray if missing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  fig.add_trace(go.Bar(
191
- name=simplify_model_name(row["Player"]),
192
- x=active_games,
193
- y=[row[f"norm_{g} Score"] for g in active_games],
194
- marker_color=color
 
195
  ))
196
 
197
  fig.update_layout(
198
  autosize=False,
199
- width=800,
200
  height=600,
201
  margin=dict(l=80, r=150, t=40, b=200),
202
- title=dict(
203
- text="Grouped Bar Chart of AI Models",
204
- pad=dict(t=10)
205
- ),
206
  xaxis_title="Games",
207
  yaxis_title="Normalized Score",
 
 
 
 
208
  barmode='group',
209
  legend=dict(
210
  font=dict(size=9),
@@ -218,8 +267,11 @@ def create_group_bar_chart(df):
218
  borderwidth=1
219
  )
220
  )
 
221
  return fig
222
 
 
 
223
  def get_combined_leaderboard_with_group_bar(rank_data, selected_games):
224
  df = get_combined_leaderboard(rank_data, selected_games)
225
  # Create a copy for visualization to avoid modifying the original
 
30
  return name.split('-')[0]
31
 
32
 
33
+ def normalize_values(values, mean=None, std=None):
34
+ min_val = min(values)
35
+ max_val = max(values)
36
+ if max_val == min_val:
37
+ return [100 for _ in values] # or 50
38
+ return [(v - min_val) / (max_val - min_val) * 100 for v in values]
39
 
40
  def simplify_model_name(name):
41
  if name == "claude-3-7-sonnet-20250219(thinking)":
 
175
  return df, create_radar_charts(df_viz)
176
 
177
  def create_group_bar_chart(df):
178
+ game_cols = {}
179
+ for game in GAME_ORDER:
180
+ col = f"{game} Score"
181
+ if col in df.columns:
182
+ df[col] = df[col].replace("_", np.nan).astype(float)
183
+ if df[col].notna().any():
184
+ game_cols[game] = col
185
+
186
+ if not game_cols:
187
+ return go.Figure().update_layout(title="No data available")
188
+
189
+ # Drop players with no data
190
+ df = df.dropna(subset=game_cols.values(), how='all')
191
+
192
+ # Normalize scores per game
193
+ for game, col in game_cols.items():
194
+ valid = df[col].dropna()
195
+ norm_col = f"norm_{col}"
196
+ if valid.empty:
197
+ df[norm_col] = np.nan
198
+ else:
199
+ mean, std = valid.mean(), valid.std()
200
+ normalized = normalize_values(valid, mean, std)
201
+ df[norm_col] = np.nan
202
+ df.loc[valid.index, norm_col] = normalized
203
+
204
+ # Build consistent game order (X-axis)
205
+ sorted_games = [game for game in GAME_ORDER if f"norm_{game} Score" in df.columns]
206
+
207
+ # Group models by prefix, then sort alphabetically
208
+ model_groups = {}
209
+ for player in df["Player"].unique():
210
+ prefix = player.split('-')[0]
211
+ model_groups.setdefault(prefix, []).append(player)
212
 
213
+ ordered_players = []
214
+ for prefix in sorted(model_groups):
215
+ ordered_players.extend(sorted(model_groups[prefix]))
 
216
 
217
+ # Create one trace per player
218
  fig = go.Figure()
219
+ for player in ordered_players:
220
+ row = df[df["Player"] == player]
221
+ if row.empty:
222
+ continue
223
+ row = row.iloc[0]
224
+
225
+ y_vals = []
226
+ has_data = False
227
+ for game in sorted_games:
228
+ col = f"norm_{game} Score"
229
+ val = row.get(col, np.nan)
230
+ if not np.isnan(val):
231
+ has_data = True
232
+ y_vals.append(val if not np.isnan(val) else 0)
233
+
234
+ if not has_data:
235
+ continue
236
 
237
  fig.add_trace(go.Bar(
238
+ name=simplify_model_name(player),
239
+ x=sorted_games,
240
+ y=y_vals,
241
+ marker_color=MODEL_COLORS.get(player, '#808080'),
242
+ hovertemplate="%{x}<br>%{y:.1f}<extra></extra>"
243
  ))
244
 
245
  fig.update_layout(
246
  autosize=False,
247
+ width=1000,
248
  height=600,
249
  margin=dict(l=80, r=150, t=40, b=200),
250
+ title=dict(text="Grouped Bar Chart of AI Models (Consistent Trace Grouping)", pad=dict(t=10)),
 
 
 
251
  xaxis_title="Games",
252
  yaxis_title="Normalized Score",
253
+ xaxis=dict(
254
+ categoryorder='array',
255
+ categoryarray=sorted_games
256
+ ),
257
  barmode='group',
258
  legend=dict(
259
  font=dict(size=9),
 
267
  borderwidth=1
268
  )
269
  )
270
+
271
  return fig
272
 
273
+
274
+
275
  def get_combined_leaderboard_with_group_bar(rank_data, selected_games):
276
  df = get_combined_leaderboard(rank_data, selected_games)
277
  # Create a copy for visualization to avoid modifying the original
gallery_tab.py CHANGED
@@ -1,6 +1,73 @@
1
  import gradio as gr
2
- from datetime import datetime
 
3
  import json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  # Load video links and news data
6
  with open('assets/game_video_link.json', 'r') as f:
@@ -9,247 +76,702 @@ with open('assets/game_video_link.json', 'r') as f:
9
  with open('assets/news.json', 'r') as f:
10
  NEWS_DATA = json.load(f)
11
 
12
- def create_video_gallery():
13
- """Create a custom HTML/JS component for video gallery"""
14
- # Extract video IDs
15
- mario_id = VIDEO_LINKS["super_mario"].split("?v=")[1]
16
- sokoban_id = VIDEO_LINKS["sokoban"].split("?v=")[1]
17
- game_2048_id = VIDEO_LINKS["2048"].split("?v=")[1]
18
- candy_id = VIDEO_LINKS["candy"].split("?v=")[1]
19
-
20
- # Get the latest video from news data
21
- latest_news = NEWS_DATA["news"][0] # First item is the latest
22
- latest_video_id = latest_news["video_link"].split("?v=")[1]
23
- latest_date = datetime.strptime(latest_news["date"], "%Y-%m-%d")
24
- formatted_latest_date = latest_date.strftime("%B %d, %Y")
25
-
26
- # Generate news HTML
27
- news_items = []
28
- for item in NEWS_DATA["news"]:
29
- video_id = item["video_link"].split("?v=")[1]
30
- date_obj = datetime.strptime(item["date"], "%Y-%m-%d")
31
- formatted_date = date_obj.strftime("%B %d, %Y")
32
- news_items.append(f'''
33
- <div class="news-item">
34
- <div class="news-date">{formatted_date}</div>
35
- <div class="news-content">
36
- <div class="news-video">
37
- <div class="video-wrapper">
38
- <iframe src="https://www.youtube.com/embed/{video_id}"></iframe>
39
- </div>
40
- </div>
41
- <div class="news-text">
42
- <a href="{item["twitter_link"]}" target="_blank" class="twitter-link">
43
- <span class="twitter-icon">📢</span>
44
- {item["twitter_text"]}
45
- </a>
46
- </div>
47
- </div>
48
- </div>
49
- ''')
50
 
51
- news_html = '\n'.join(news_items)
 
 
 
52
 
53
- gallery_html = f'''
54
- <div class="video-gallery-container">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  <style>
56
- .video-gallery-container {{
57
- width: 100%;
58
- max-width: 1400px;
59
- margin: 0 auto;
60
- padding: 20px;
61
- }}
62
- .highlight-section {{
63
- margin-bottom: 40px;
64
- }}
65
- .highlight-card {{
66
- background: #ffffff;
67
- border-radius: 10px;
68
- box-shadow: 0 4px 20px rgba(0,0,0,0.15);
69
- overflow: hidden;
70
- transition: transform 0.3s;
71
- border: 2px solid #2196F3;
72
- }}
73
- .highlight-card:hover {{
74
- transform: translateY(-5px);
75
- }}
76
- .highlight-header {{
77
- background: #2196F3;
78
- color: white;
79
- padding: 15px 20px;
80
- font-size: 1.2em;
81
- font-weight: bold;
82
  display: flex;
83
  align-items: center;
84
- gap: 10px;
85
- }}
86
- .highlight-date {{
87
- font-size: 0.9em;
88
- opacity: 0.9;
89
- }}
90
- .highlight-content {{
91
- padding: 20px;
92
- }}
93
- .video-grid {{
94
- display: grid;
95
- grid-template-columns: repeat(2, 1fr);
96
- gap: 20px;
97
- margin-top: 20px;
98
- margin-bottom: 40px;
99
- }}
100
- .video-card {{
101
- background: #ffffff;
102
- border-radius: 10px;
103
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
104
- overflow: hidden;
105
- transition: transform 0.2s;
106
- }}
107
- .video-card:hover {{
108
- transform: translateY(-5px);
109
- }}
110
- .video-wrapper {{
111
  position: relative;
112
- padding-bottom: 56.25%;
113
- height: 0;
114
- overflow: hidden;
115
- }}
116
- .video-wrapper iframe {{
117
- position: absolute;
118
- top: 0;
119
- left: 0;
120
  width: 100%;
 
 
 
121
  height: 100%;
122
- border: none;
123
- }}
124
- .video-title {{
125
- padding: 15px;
126
- font-size: 1.2em;
127
- font-weight: bold;
128
- color: #2c3e50;
129
- text-align: center;
130
- background: #f8f9fa;
131
- border-top: 1px solid #eee;
132
- }}
133
- .news-section {{
134
- margin-top: 40px;
135
- border-top: 2px solid #e9ecef;
136
- padding-top: 20px;
137
- }}
138
- .news-section-title {{
139
- font-size: 1.8em;
140
- font-weight: bold;
141
- color: #2c3e50;
142
- margin-bottom: 20px;
143
- text-align: center;
144
- }}
145
- .news-item {{
146
- background: #ffffff;
147
- border-radius: 10px;
148
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
149
- margin-bottom: 20px;
150
- overflow: hidden;
151
- }}
152
- .news-date {{
153
- padding: 10px 20px;
154
- background: #f8f9fa;
155
- color: #666;
156
- font-size: 0.9em;
157
- border-bottom: 1px solid #eee;
158
- }}
159
- .news-content {{
160
- display: flex;
161
- padding: 20px;
162
- align-items: center;
163
- gap: 30px;
164
- }}
165
- .news-video {{
166
- flex: 0 0 300px;
167
- }}
168
- .news-text {{
169
- flex: 1;
170
- display: flex;
171
- align-items: center;
172
- min-height: 169px;
173
- }}
174
- .twitter-link {{
175
- color: #2c3e50;
176
- text-decoration: none;
177
- display: flex;
178
- align-items: center;
179
- gap: 15px;
180
- font-size: 1.4em;
181
  font-weight: 600;
182
- line-height: 1.4;
183
- }}
184
- .twitter-link:hover {{
185
- color: #1da1f2;
186
- }}
187
- .twitter-icon {{
188
- font-size: 1.5em;
189
- color: #1da1f2;
190
- }}
191
  </style>
192
-
193
- <!-- Highlight Section -->
194
- <div class="highlight-section">
195
- <div class="highlight-card">
196
- <div class="highlight-header">
197
- <span>🌟 Latest Update</span>
198
- <span class="highlight-date">{formatted_latest_date}</span>
199
- </div>
200
- <div class="highlight-content">
201
- <div class="video-wrapper">
202
- <iframe src="https://www.youtube.com/embed/{latest_video_id}"></iframe>
203
- </div>
204
- <div class="video-title">
205
- <a href="{latest_news["twitter_link"]}" target="_blank" class="twitter-link">
206
- <span class="twitter-icon">📢</span>
207
- {latest_news["twitter_text"]}
208
- </a>
209
- </div>
210
- </div>
211
  </div>
212
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
- <!-- Regular Video Grid -->
215
- <div class="video-grid">
216
- <div class="video-card">
217
- <div class="video-wrapper">
218
- <iframe src="https://www.youtube.com/embed/{mario_id}"></iframe>
219
- </div>
220
- <div class="video-title">🎮 Super Mario Bros</div>
221
- </div>
222
- <div class="video-card">
223
- <div class="video-wrapper">
224
- <iframe src="https://www.youtube.com/embed/{sokoban_id}"></iframe>
225
- </div>
226
- <div class="video-title">📦 Sokoban</div>
227
- </div>
228
- <div class="video-card">
229
- <div class="video-wrapper">
230
- <iframe src="https://www.youtube.com/embed/{game_2048_id}"></iframe>
231
- </div>
232
- <div class="video-title">🔢 2048</div>
233
- </div>
234
- <div class="video-card">
235
- <div class="video-wrapper">
236
- <iframe src="https://www.youtube.com/embed/{candy_id}"></iframe>
237
- </div>
238
- <div class="video-title">🍬 Candy Crash</div>
239
- </div>
240
- </div>
241
 
242
- <!-- News Section -->
243
- <div class="news-section">
244
- <div class="news-section-title">📰 Latest News</div>
245
- {news_html}
246
- </div>
247
- </div>
248
- '''
249
- return gr.HTML(gallery_html)
250
-
251
- def create_gallery_tab():
252
- """Create and return the gallery tab component"""
253
- with gr.Tab("🎥 Gallery") as gallery_tab:
254
- video_gallery = create_video_gallery()
255
- return gallery_tab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import os
3
+ import pandas as pd
4
  import json
5
+ from PIL import Image, ImageSequence
6
+ import io
7
+ from functools import reduce
8
+ import numpy as np
9
+ from datetime import datetime, timedelta
10
+ import matplotlib.pyplot as plt
11
+ from leaderboard_utils import (
12
+ get_organization,
13
+ get_mario_leaderboard,
14
+ get_sokoban_leaderboard,
15
+ get_2048_leaderboard,
16
+ get_candy_leaderboard,
17
+ get_tetris_leaderboard,
18
+ get_tetris_planning_leaderboard,
19
+ get_combined_leaderboard,
20
+ GAME_ORDER
21
+ )
22
+ from data_visualization import (
23
+ get_combined_leaderboard_with_group_bar,
24
+ create_organization_radar_chart,
25
+ create_top_players_radar_chart,
26
+ create_player_radar_chart,
27
+ create_horizontal_bar_chart,
28
+ normalize_values,
29
+ get_combined_leaderboard_with_single_radar
30
+ )
31
+ from gallery_tab import create_video_gallery
32
+
33
+ # Try to import enhanced leaderboard, use standard DataFrame if not available
34
+
35
+ from gradio_leaderboard import Leaderboard, SelectColumns, ColumnFilter
36
+ from leaderboard_config import ON_LOAD_COLUMNS, TYPES
37
+ HAS_ENHANCED_LEADERBOARD = True
38
+
39
+
40
+ # Define time points and their corresponding data files
41
+ TIME_POINTS = {
42
+ "03/25/2025": "rank_data_03_25_2025.json",
43
+ # Add more time points here as they become available
44
+ }
45
+
46
+ # Load the initial JSON file with rank data
47
+ with open(TIME_POINTS["03/25/2025"], "r") as f:
48
+ rank_data = json.load(f)
49
+
50
+ # Add leaderboard state at the top level
51
+ leaderboard_state = {
52
+ "current_game": None,
53
+ "previous_overall": {
54
+ "Super Mario Bros": True,
55
+ "Sokoban": True,
56
+ "2048": True,
57
+ "Candy Crash": True,
58
+ "Tetris (complete)": True,
59
+ "Tetris (planning only)": True
60
+ },
61
+ "previous_details": {
62
+ "Super Mario Bros": False,
63
+ "Sokoban": False,
64
+ "2048": False,
65
+ "Candy Crash": False,
66
+ "Tetris (complete)": False,
67
+ "Tetris (planning only)": False
68
+ }
69
+ }
70
+
71
 
72
  # Load video links and news data
73
  with open('assets/game_video_link.json', 'r') as f:
 
76
  with open('assets/news.json', 'r') as f:
77
  NEWS_DATA = json.load(f)
78
 
79
+ def load_rank_data(time_point):
80
+ """Load rank data for a specific time point"""
81
+ if time_point in TIME_POINTS:
82
+ try:
83
+ with open(TIME_POINTS[time_point], "r") as f:
84
+ return json.load(f)
85
+ except FileNotFoundError:
86
+ return None
87
+ return None
88
+
89
+ # Add a note about score values
90
+ def add_score_note():
91
+ return gr.Markdown("*Note: '-1' in the table indicates no data point for that model.*", elem_classes="score-note")
92
+
93
+ # Function to prepare DataFrame for display
94
+ def prepare_dataframe_for_display(df, for_game=None):
95
+ """Format DataFrame for better display in the UI"""
96
+ # Clone the DataFrame to avoid modifying the original
97
+ display_df = df.copy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ # Filter out normalized score columns
100
+ norm_columns = [col for col in display_df.columns if col.startswith('norm_')]
101
+ if norm_columns:
102
+ display_df = display_df.drop(columns=norm_columns)
103
 
104
+ # Replace '_' with '-' for better display
105
+ for col in display_df.columns:
106
+ if col.endswith(' Score'):
107
+ display_df[col] = display_df[col].apply(lambda x: '-' if x == '_' else x)
108
+
109
+ # If we're in detailed view, add a formatted rank column
110
+ if for_game:
111
+ # Sort by relevant score column
112
+ score_col = f"{for_game} Score"
113
+ if score_col in display_df.columns:
114
+ # Convert to numeric for sorting, treating '-' as NaN
115
+ display_df[score_col] = pd.to_numeric(display_df[score_col], errors='coerce')
116
+ # Sort by score in descending order
117
+ display_df = display_df.sort_values(by=score_col, ascending=False)
118
+ # Add rank column based on the sort
119
+ display_df.insert(0, 'Rank', range(1, len(display_df) + 1))
120
+ # Filter out models that didn't participate
121
+ display_df = display_df[~display_df[score_col].isna()]
122
+
123
+ return display_df
124
+
125
+ # Helper function to ensure leaderboard updates maintain consistent height
126
+ def update_df_with_height(df):
127
+ """Update DataFrame with consistent height parameter."""
128
+ return gr.update(value=df, height=800)
129
+
130
+ def update_leaderboard(mario_overall, mario_details,
131
+ sokoban_overall, sokoban_details,
132
+ _2048_overall, _2048_details,
133
+ candy_overall, candy_details,
134
+ tetris_overall, tetris_details,
135
+ tetris_plan_overall, tetris_plan_details):
136
+ global leaderboard_state
137
+
138
+ # Convert current checkbox states to dictionary for easier comparison
139
+ current_overall = {
140
+ "Super Mario Bros": mario_overall,
141
+ "Sokoban": sokoban_overall,
142
+ "2048": _2048_overall,
143
+ "Candy Crash": candy_overall,
144
+ "Tetris (complete)": tetris_overall,
145
+ "Tetris (planning only)": tetris_plan_overall
146
+ }
147
+
148
+ current_details = {
149
+ "Super Mario Bros": mario_details,
150
+ "Sokoban": sokoban_details,
151
+ "2048": _2048_details,
152
+ "Candy Crash": candy_details,
153
+ "Tetris (complete)": tetris_details,
154
+ "Tetris (planning only)": tetris_plan_details
155
+ }
156
+
157
+ # Find which game's state changed
158
+ changed_game = None
159
+ for game in current_overall.keys():
160
+ if (current_overall[game] != leaderboard_state["previous_overall"][game] or
161
+ current_details[game] != leaderboard_state["previous_details"][game]):
162
+ changed_game = game
163
+ break
164
+
165
+ if changed_game:
166
+ # If a game's details checkbox was checked
167
+ if current_details[changed_game] and not leaderboard_state["previous_details"][changed_game]:
168
+ # Reset all other games' states
169
+ for game in current_overall.keys():
170
+ if game != changed_game:
171
+ current_overall[game] = False
172
+ current_details[game] = False
173
+ leaderboard_state["previous_overall"][game] = False
174
+ leaderboard_state["previous_details"][game] = False
175
+
176
+ # Update state for the selected game
177
+ leaderboard_state["current_game"] = changed_game
178
+ leaderboard_state["previous_overall"][changed_game] = True
179
+ leaderboard_state["previous_details"][changed_game] = True
180
+ current_overall[changed_game] = True
181
+
182
+ # If a game's overall checkbox was checked
183
+ elif current_overall[changed_game] and not leaderboard_state["previous_overall"][changed_game]:
184
+ # If we were in details view for another game, switch to overall view
185
+ if leaderboard_state["current_game"] and leaderboard_state["previous_details"][leaderboard_state["current_game"]]:
186
+ # Reset previous game's details
187
+ leaderboard_state["previous_details"][leaderboard_state["current_game"]] = False
188
+ current_details[leaderboard_state["current_game"]] = False
189
+ leaderboard_state["current_game"] = None
190
+
191
+ # Update state
192
+ leaderboard_state["previous_overall"][changed_game] = True
193
+ leaderboard_state["previous_details"][changed_game] = False
194
+
195
+ # If a game's overall checkbox was unchecked
196
+ elif not current_overall[changed_game] and leaderboard_state["previous_overall"][changed_game]:
197
+ # If we're in details view, don't allow unchecking the overall checkbox
198
+ if leaderboard_state["current_game"] == changed_game:
199
+ current_overall[changed_game] = True
200
+ else:
201
+ leaderboard_state["previous_overall"][changed_game] = False
202
+ if leaderboard_state["current_game"] == changed_game:
203
+ leaderboard_state["current_game"] = None
204
+
205
+ # If a game's details checkbox was unchecked
206
+ elif not current_details[changed_game] and leaderboard_state["previous_details"][changed_game]:
207
+ leaderboard_state["previous_details"][changed_game] = False
208
+ if leaderboard_state["current_game"] == changed_game:
209
+ leaderboard_state["current_game"] = None
210
+ # When exiting details view, reset to show all games
211
+ for game in current_overall.keys():
212
+ current_overall[game] = True
213
+ current_details[game] = False
214
+ leaderboard_state["previous_overall"][game] = True
215
+ leaderboard_state["previous_details"][game] = False
216
+
217
+ # Special case: If all games are selected and we're trying to view details
218
+ all_games_selected = all(current_overall.values()) and not any(current_details.values())
219
+ if all_games_selected and changed_game and current_details[changed_game]:
220
+ # Reset all other games' states
221
+ for game in current_overall.keys():
222
+ if game != changed_game:
223
+ current_overall[game] = False
224
+ current_details[game] = False
225
+ leaderboard_state["previous_overall"][game] = False
226
+ leaderboard_state["previous_details"][game] = False
227
+
228
+ # Update state for the selected game
229
+ leaderboard_state["current_game"] = changed_game
230
+ leaderboard_state["previous_overall"][changed_game] = True
231
+ leaderboard_state["previous_details"][changed_game] = True
232
+ current_overall[changed_game] = True
233
+
234
+ # Build dictionary for selected games
235
+ selected_games = {
236
+ "Super Mario Bros": current_overall["Super Mario Bros"],
237
+ "Sokoban": current_overall["Sokoban"],
238
+ "2048": current_overall["2048"],
239
+ "Candy Crash": current_overall["Candy Crash"],
240
+ "Tetris (complete)": current_overall["Tetris (complete)"],
241
+ "Tetris (planning only)": current_overall["Tetris (planning only)"]
242
+ }
243
+
244
+ # Get the appropriate DataFrame and charts based on current state
245
+ if leaderboard_state["current_game"]:
246
+ # For detailed view
247
+ if leaderboard_state["current_game"] == "Super Mario Bros":
248
+ df = get_mario_leaderboard(rank_data)
249
+ elif leaderboard_state["current_game"] == "Sokoban":
250
+ df = get_sokoban_leaderboard(rank_data)
251
+ elif leaderboard_state["current_game"] == "2048":
252
+ df = get_2048_leaderboard(rank_data)
253
+ elif leaderboard_state["current_game"] == "Candy Crash":
254
+ df = get_candy_leaderboard(rank_data)
255
+ elif leaderboard_state["current_game"] == "Tetris (complete)":
256
+ df = get_tetris_leaderboard(rank_data)
257
+ else: # Tetris (planning only)
258
+ df = get_tetris_planning_leaderboard(rank_data)
259
+
260
+ # Format the DataFrame for display
261
+ display_df = prepare_dataframe_for_display(df, leaderboard_state["current_game"])
262
+
263
+ # Always create a new chart for detailed view
264
+ chart = create_horizontal_bar_chart(df, leaderboard_state["current_game"])
265
+ # For detailed view, we'll use the same chart for all visualizations
266
+ radar_chart = chart
267
+ group_bar_chart = chart
268
+ else:
269
+ # For overall view
270
+ df, group_bar_chart = get_combined_leaderboard_with_group_bar(rank_data, selected_games)
271
+ # Format the DataFrame for display
272
+ display_df = prepare_dataframe_for_display(df)
273
+ # Use the same selected_games for radar chart
274
+ _, radar_chart = get_combined_leaderboard_with_single_radar(rank_data, selected_games)
275
+ chart = group_bar_chart
276
+
277
+ # Return exactly 16 values to match the expected outputs
278
+ return (update_df_with_height(display_df), chart, radar_chart, group_bar_chart,
279
+ current_overall["Super Mario Bros"], current_details["Super Mario Bros"],
280
+ current_overall["Sokoban"], current_details["Sokoban"],
281
+ current_overall["2048"], current_details["2048"],
282
+ current_overall["Candy Crash"], current_details["Candy Crash"],
283
+ current_overall["Tetris (complete)"], current_details["Tetris (complete)"],
284
+ current_overall["Tetris (planning only)"], current_details["Tetris (planning only)"])
285
+
286
+ def update_leaderboard_with_time(time_point, mario_overall, mario_details,
287
+ sokoban_overall, sokoban_details,
288
+ _2048_overall, _2048_details,
289
+ candy_overall, candy_details,
290
+ tetris_overall, tetris_details,
291
+ tetris_plan_overall, tetris_plan_details):
292
+ # Load rank data for the selected time point
293
+ global rank_data
294
+ new_rank_data = load_rank_data(time_point)
295
+ if new_rank_data is not None:
296
+ rank_data = new_rank_data
297
+
298
+ # Use the existing update_leaderboard function
299
+ return update_leaderboard(mario_overall, mario_details,
300
+ sokoban_overall, sokoban_details,
301
+ _2048_overall, _2048_details,
302
+ candy_overall, candy_details,
303
+ tetris_overall, tetris_details,
304
+ tetris_plan_overall, tetris_plan_details)
305
+
306
+ def get_initial_state():
307
+ """Get the initial state for the leaderboard"""
308
+ return {
309
+ "current_game": None,
310
+ "previous_overall": {
311
+ "Super Mario Bros": True,
312
+ "Sokoban": True,
313
+ "2048": True,
314
+ "Candy Crash": True,
315
+ "Tetris (complete)": True,
316
+ "Tetris (planning only)": True
317
+ },
318
+ "previous_details": {
319
+ "Super Mario Bros": False,
320
+ "Sokoban": False,
321
+ "2048": False,
322
+ "Candy Crash": False,
323
+ "Tetris (complete)": False,
324
+ "Tetris (planning only)": False
325
+ }
326
+ }
327
+
328
+ def clear_filters():
329
+ global leaderboard_state
330
+
331
+ # Reset all checkboxes to default state
332
+ selected_games = {
333
+ "Super Mario Bros": True,
334
+ "Sokoban": True,
335
+ "2048": True,
336
+ "Candy Crash": True,
337
+ "Tetris (complete)": True,
338
+ "Tetris (planning only)": True
339
+ }
340
+
341
+ # Get the combined leaderboard and group bar chart
342
+ df, group_bar_chart = get_combined_leaderboard_with_group_bar(rank_data, selected_games)
343
+
344
+ # Format the DataFrame for display
345
+ display_df = prepare_dataframe_for_display(df)
346
+
347
+ # Get the radar chart using the same selected games
348
+ _, radar_chart = get_combined_leaderboard_with_single_radar(rank_data, selected_games)
349
+
350
+ # Reset the leaderboard state to match the default checkbox states
351
+ leaderboard_state = get_initial_state()
352
+
353
+ # Return exactly 16 values to match the expected outputs
354
+ return (update_df_with_height(display_df), group_bar_chart, radar_chart, group_bar_chart,
355
+ True, False, # mario
356
+ True, False, # sokoban
357
+ True, False, # 2048
358
+ True, False, # candy
359
+ True, False, # tetris
360
+ True, False) # tetris plan
361
+
362
+ def create_timeline_slider():
363
+ """Create a custom timeline slider component"""
364
+ timeline_html = """
365
+ <div class="timeline-container">
366
  <style>
367
+ .timeline-container {
368
+ width: 85%; /* Increased from 70% to 85% */
369
+ padding: 8px;
370
+ font-family: Arial, sans-serif;
371
+ height: 40px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
  display: flex;
373
  align-items: center;
374
+ }
375
+ .timeline-track {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  position: relative;
377
+ height: 6px;
378
+ background: #e0e0e0;
379
+ border-radius: 3px;
380
+ margin: 0;
 
 
 
 
381
  width: 100%;
382
+ }
383
+ .timeline-progress {
384
+ position: absolute;
385
  height: 100%;
386
+ background: #2196F3;
387
+ border-radius: 3px;
388
+ width: 100%;
389
+ }
390
+ .timeline-handle {
391
+ position: absolute;
392
+ right: 0;
393
+ top: 50%;
394
+ transform: translate(50%, -50%);
395
+ width: 20px;
396
+ height: 20px;
397
+ background: #2196F3;
398
+ border: 3px solid white;
399
+ border-radius: 50%;
400
+ cursor: pointer;
401
+ box-shadow: 0 2px 6px rgba(0,0,0,0.3);
402
+ }
403
+ .timeline-date {
404
+ position: absolute;
405
+ top: -25px;
406
+ transform: translateX(-50%);
407
+ background: #2196F3; /* Changed to match slider blue color */
408
+ color: #ffffff !important;
409
+ padding: 3px 8px;
410
+ border-radius: 4px;
411
+ font-size: 12px;
412
+ white-space: nowrap;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  font-weight: 600;
414
+ box-shadow: 0 2px 6px rgba(0,0,0,0.2);
415
+ letter-spacing: 0.5px;
416
+ text-shadow: 0 1px 2px rgba(0,0,0,0.2);
417
+ }
 
 
 
 
 
418
  </style>
419
+ <div class="timeline-track">
420
+ <div class="timeline-progress"></div>
421
+ <div class="timeline-handle">
422
+ <div class="timeline-date">03/25/2025</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  </div>
424
  </div>
425
+ </div>
426
+ <script>
427
+ (function() {
428
+ const container = document.querySelector('.timeline-container');
429
+ const track = container.querySelector('.timeline-track');
430
+ const handle = container.querySelector('.timeline-handle');
431
+ let isDragging = false;
432
+
433
+ // For now, we only have one time point
434
+ const timePoints = {
435
+ "03/25/2025": 1.0
436
+ };
437
+
438
+ function updatePosition(e) {
439
+ if (!isDragging) return;
440
+
441
+ const rect = track.getBoundingClientRect();
442
+ let x = (e.clientX - rect.left) / rect.width;
443
+ x = Math.max(0, Math.min(1, x));
444
+
445
+ // For now, snap to the only available time point
446
+ x = 1.0;
447
+
448
+ handle.style.right = `${(1 - x) * 100}%`;
449
+ }
450
+
451
+ handle.addEventListener('mousedown', (e) => {
452
+ isDragging = true;
453
+ e.preventDefault();
454
+ });
455
+
456
+ document.addEventListener('mousemove', updatePosition);
457
+ document.addEventListener('mouseup', () => {
458
+ isDragging = false;
459
+ });
460
+
461
+ // Prevent text selection while dragging
462
+ container.addEventListener('selectstart', (e) => {
463
+ if (isDragging) e.preventDefault();
464
+ });
465
+ })();
466
+ </script>
467
+ """
468
+ return gr.HTML(timeline_html)
469
+
470
+ def build_app():
471
+ with gr.Blocks(css="""
472
+ .visualization-container .js-plotly-plot {
473
+ margin-left: auto !important;
474
+ margin-right: auto !important;
475
+ display: block !important;
476
+ }
477
+
478
+ /* Optional: limit width for better layout on large screens */
479
+ .visualization-container .js-plotly-plot {
480
+ max-width: 1000px;
481
+ }
482
+
483
+ .section-title {
484
+ font-size: 1.5em;
485
+ font-weight: bold;
486
+ color: #2c3e50;
487
+ margin-bottom: 15px;
488
+ padding-bottom: 10px;
489
+ border-bottom: 2px solid #e9ecef;
490
+ text-align: center;
491
+ }
492
+ .container {
493
+ max-width: 1400px;
494
+ margin: 0 auto;
495
+ padding: 0 20px;
496
+ }
497
 
498
+ /* Enhanced table styling - SIMPLIFIED */
499
+ .table-container {
500
+ height: 800px !important;
501
+ max-height: 1000px !important;
502
+ overflow-y: auto !important; /* ONLY the outer container gets scrolling */
503
+ border-radius: 8px;
504
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
505
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
 
507
+ /* Prevent inner containers from having scrollbars */
508
+ .table-container > div,
509
+ .table-container > div > div,
510
+ .gradio-dataframe > div,
511
+ [data-testid="dataframe"] > div {
512
+ overflow: visible !important;
513
+ height: auto !important;
514
+ }
515
+
516
+ /* Fix table styling */
517
+ .table-container table {
518
+ width: 100%;
519
+ border-collapse: separate;
520
+ border-spacing: 0;
521
+ }
522
+
523
+ /* Make headers sticky */
524
+ .table-container th {
525
+ position: sticky !important;
526
+ top: 0 !important;
527
+ background-color: #f8f9fa !important;
528
+ z-index: 10 !important;
529
+ font-weight: bold;
530
+ padding: 12px;
531
+ border-bottom: 2px solid #e9ecef;
532
+ }
533
+
534
+ /* Simple cell styling */
535
+ .table-container td {
536
+ padding: 10px 12px;
537
+ border-bottom: 1px solid #e9ecef;
538
+ }
539
+
540
+ /* Visual enhancements */
541
+ .table-container tr:hover {
542
+ background-color: #f1f3f4;
543
+ }
544
+
545
+ .table-container tr:nth-child(even) {
546
+ background-color: #f8fafc;
547
+ }
548
+
549
+ /* Row containing the table */
550
+ .gradio-container .gr-row {
551
+ min-height: auto !important;
552
+ height: auto !important;
553
+ overflow: visible !important;
554
+ margin-bottom: 20px;
555
+ }
556
+ """) as demo:
557
+ gr.Markdown("# 🎮 Game Arena: Gaming Agent 🎲")
558
+
559
+ with gr.Tabs():
560
+ with gr.Tab("🏆 Leaderboard"):
561
+ # Visualization section
562
+ with gr.Row():
563
+ gr.Markdown("### 📊 Data Visualization")
564
+
565
+ # Detailed view visualization (single chart)
566
+ detailed_visualization = gr.Plot(
567
+ label="Performance Visualization",
568
+ visible=False,
569
+ elem_classes="visualization-container"
570
+ )
571
+
572
+ with gr.Column(visible=True) as overall_visualizations:
573
+ with gr.Tabs():
574
+ with gr.Tab("📈 Radar Chart"):
575
+ radar_visualization = gr.Plot(
576
+ label="Comparative Analysis (Radar Chart)",
577
+ elem_classes="visualization-container"
578
+ )
579
+ with gr.Tab("📊 Group Bar Chart"):
580
+ group_bar_visualization = gr.Plot(
581
+ label="Comparative Analysis (Group Bar Chart)",
582
+ elem_classes="visualization-container"
583
+ )
584
+
585
+
586
+ # Game selection section
587
+ with gr.Row():
588
+ gr.Markdown("### 🎮 Game Selection")
589
+ with gr.Row():
590
+ with gr.Column():
591
+ gr.Markdown("**🎮 Super Mario Bros**")
592
+ mario_overall = gr.Checkbox(label="Super Mario Bros Score", value=True)
593
+ mario_details = gr.Checkbox(label="Super Mario Bros Details", value=False)
594
+ with gr.Column():
595
+ gr.Markdown("**📦 Sokoban**")
596
+ sokoban_overall = gr.Checkbox(label="Sokoban Score", value=True)
597
+ sokoban_details = gr.Checkbox(label="Sokoban Details", value=False)
598
+ with gr.Column():
599
+ gr.Markdown("**🔢 2048**")
600
+ _2048_overall = gr.Checkbox(label="2048 Score", value=True)
601
+ _2048_details = gr.Checkbox(label="2048 Details", value=False)
602
+ with gr.Column():
603
+ gr.Markdown("**🍬 Candy Crash**")
604
+ candy_overall = gr.Checkbox(label="Candy Crash Score", value=True)
605
+ candy_details = gr.Checkbox(label="Candy Crash Details", value=False)
606
+ with gr.Column():
607
+ gr.Markdown("**🎯 Tetris (complete)**")
608
+ tetris_overall = gr.Checkbox(label="Tetris (complete) Score", value=True)
609
+ tetris_details = gr.Checkbox(label="Tetris (complete) Details", value=False)
610
+ with gr.Column():
611
+ gr.Markdown("**📋 Tetris (planning)**")
612
+ tetris_plan_overall = gr.Checkbox(label="Tetris (planning) Score", value=True)
613
+ tetris_plan_details = gr.Checkbox(label="Tetris (planning) Details", value=False)
614
+
615
+ # Controls
616
+ with gr.Row():
617
+ with gr.Column(scale=2):
618
+ gr.Markdown("**⏰ Time Tracker**")
619
+ timeline = create_timeline_slider()
620
+ with gr.Column(scale=1):
621
+ gr.Markdown("**🔄 Controls**")
622
+ clear_btn = gr.Button("Reset Filters", variant="secondary")
623
+
624
+ # Leaderboard table
625
+ with gr.Row():
626
+ gr.Markdown("### 📋 Detailed Results")
627
+
628
+ # Add leaderboard search box in its own row
629
+ with gr.Row():
630
+ with gr.Column(scale=8):
631
+ search_box = gr.Textbox(
632
+ label="🔍 Search by Player or Organization",
633
+ placeholder="Type to filter the table...",
634
+ show_label=True
635
+ )
636
+ with gr.Column(scale=1):
637
+ search_clear_btn = gr.Button("Clear", variant="secondary")
638
+
639
+ # Get initial leaderboard dataframe
640
+ initial_df = get_combined_leaderboard(rank_data, {
641
+ "Super Mario Bros": True,
642
+ "Sokoban": True,
643
+ "2048": True,
644
+ "Candy Crash": True,
645
+ "Tetris (complete)": True,
646
+ "Tetris (planning only)": True
647
+ })
648
+
649
+ # Format the DataFrame for display
650
+ initial_display_df = prepare_dataframe_for_display(initial_df)
651
+
652
+ # Create a standard DataFrame component with enhanced styling
653
+ with gr.Row():
654
+ leaderboard_df = gr.DataFrame(
655
+ value=initial_display_df,
656
+ interactive=True,
657
+ elem_id="leaderboard-table",
658
+ elem_classes="table-container",
659
+ wrap=True,
660
+ column_widths={"Player": "25%", "Organization": "20%"},
661
+ height=800
662
+ )
663
+
664
+ # Add the score note below the table
665
+ with gr.Row():
666
+ score_note = add_score_note()
667
+
668
+ # Add search functionality
669
+ def filter_table(search_term, current_df):
670
+ # Create a fresh copy of the DataFrame to avoid modifying the original
671
+ filtered_df = current_df.copy()
672
+
673
+ # Only apply search if a term is provided
674
+ if search_term:
675
+ # Filter the DataFrame by Player or Organization
676
+ filtered_df = filtered_df[
677
+ filtered_df["Player"].str.contains(search_term, case=False) |
678
+ filtered_df["Organization"].str.contains(search_term, case=False)
679
+ ]
680
+
681
+ return filtered_df
682
+
683
+ # Connect search box to the table
684
+ search_box.change(
685
+ filter_table,
686
+ inputs=[search_box, leaderboard_df],
687
+ outputs=[leaderboard_df]
688
+ )
689
+
690
+ # Clear search button functionality
691
+ def clear_search():
692
+ return "", initial_display_df
693
+
694
+ search_clear_btn.click(
695
+ clear_search,
696
+ inputs=[],
697
+ outputs=[search_box, leaderboard_df]
698
+ )
699
+
700
+ # List of all checkboxes
701
+ checkbox_list = [
702
+ mario_overall, mario_details,
703
+ sokoban_overall, sokoban_details,
704
+ _2048_overall, _2048_details,
705
+ candy_overall, candy_details,
706
+ tetris_overall, tetris_details,
707
+ tetris_plan_overall, tetris_plan_details
708
+ ]
709
+
710
+ # Update visualizations when checkboxes change
711
+ def update_visualizations(*checkbox_states):
712
+ # Check if any details checkbox is selected
713
+ is_details_view = any([
714
+ checkbox_states[1], checkbox_states[3], checkbox_states[5],
715
+ checkbox_states[7], checkbox_states[9], checkbox_states[11]
716
+ ])
717
+
718
+ # Update visibility of visualization blocks
719
+ return {
720
+ detailed_visualization: gr.update(visible=is_details_view),
721
+ overall_visualizations: gr.update(visible=not is_details_view)
722
+ }
723
+
724
+ # Add change event to all checkboxes
725
+ for checkbox in checkbox_list:
726
+ checkbox.change(
727
+ update_visualizations,
728
+ inputs=checkbox_list,
729
+ outputs=[detailed_visualization, overall_visualizations]
730
+ )
731
+
732
+ # Update leaderboard and visualizations when checkboxes change
733
+ for checkbox in checkbox_list:
734
+ checkbox.change(
735
+ update_leaderboard,
736
+ inputs=checkbox_list,
737
+ outputs=[
738
+ leaderboard_df,
739
+ detailed_visualization,
740
+ radar_visualization,
741
+ group_bar_visualization
742
+ ] + checkbox_list
743
+ )
744
+
745
+ # Update when clear button is clicked
746
+ clear_btn.click(
747
+ clear_filters,
748
+ inputs=[],
749
+ outputs=[
750
+ leaderboard_df,
751
+ detailed_visualization,
752
+ radar_visualization,
753
+ group_bar_visualization
754
+ ] + checkbox_list
755
+ )
756
+
757
+ # Initialize the app
758
+ demo.load(
759
+ fn=clear_filters,
760
+ inputs=[],
761
+ outputs=[
762
+ leaderboard_df,
763
+ detailed_visualization,
764
+ radar_visualization,
765
+ group_bar_visualization
766
+ ] + checkbox_list
767
+ )
768
+
769
+ with gr.Tab("🎥 Gallery"):
770
+ video_gallery = create_video_gallery()
771
+
772
+ return demo
773
+
774
+ if __name__ == "__main__":
775
+ demo_app = build_app()
776
+ # Add file serving configuration
777
+ demo_app.launch(debug=True, show_error=True, share=True)
requirements.txt CHANGED
@@ -16,4 +16,5 @@ tokenizers>=0.15.0
16
  sentencepiece
17
  seaborn>=0.12.0
18
  Pillow>=10.0.0
19
- plotly>=5.15.0
 
 
16
  sentencepiece
17
  seaborn>=0.12.0
18
  Pillow>=10.0.0
19
+ plotly>=5.15.0
20
+ gradio>=4.0.0