Yuxuan Zhang commited on
Commit
b15c343
·
verified ·
1 Parent(s): 304f3d5

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ 2048_performance.png filter=lfs diff=lfs merge=lfs -text
37
+ assets/2048/2048.gif filter=lfs diff=lfs merge=lfs -text
38
+ assets/candy/candy.gif filter=lfs diff=lfs merge=lfs -text
39
+ assets/sokoban/sokoban.gif filter=lfs diff=lfs merge=lfs -text
40
+ assets/super_mario_bros/super_mario.gif filter=lfs diff=lfs merge=lfs -text
41
+ assets/tetris/tetris.gif filter=lfs diff=lfs merge=lfs -text
.github/workflows/update_space.yml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Run Python script
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - gradio_app_v2_dev
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v2
15
+
16
+ - name: Set up Python
17
+ uses: actions/setup-python@v2
18
+ with:
19
+ python-version: '3.9'
20
+
21
+ - name: Install Gradio
22
+ run: python -m pip install gradio
23
+
24
+ - name: Log in to Hugging Face
25
+ run: python -c 'import huggingface_hub; huggingface_hub.login(token="${{ secrets.hf_token }}")'
26
+
27
+ - name: Deploy to Spaces
28
+ run: gradio deploy
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
3
+ TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
4
+ cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
5
+ WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
6
+ ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
7
+ MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
8
+ h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
9
+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
10
+ A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
11
+ T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
12
+ B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
13
+ B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
14
+ KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
15
+ OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
16
+ jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
17
+ qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
18
+ rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
19
+ HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
20
+ hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
21
+ ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
22
+ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
23
+ NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
24
+ ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
25
+ TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
26
+ jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
27
+ oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
28
+ 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
29
+ mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
30
+ emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
31
+ -----END CERTIFICATE-----
2048_performance.png ADDED

Git LFS Details

  • SHA256: d64fd43d5dc926789b07336bb6b04e2c0ac8078aed8da878489259d8785936aa
  • Pointer size: 131 Bytes
  • Size of remote file: 201 kB
README.md CHANGED
@@ -1,12 +1,6 @@
1
  ---
2
- title: Lmgame
3
- emoji: 🦀
4
- colorFrom: yellow
5
- colorTo: gray
6
  sdk: gradio
7
- sdk_version: 5.23.3
8
- app_file: app.py
9
- pinned: false
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: lmgame
3
+ app_file: gradio_app_v2.py
 
 
4
  sdk: gradio
5
+ sdk_version: 5.23.1
 
 
6
  ---
 
 
__pycache__/data_analysis.cpython-310.pyc ADDED
Binary file (5.34 kB). View file
 
__pycache__/data_visualization.cpython-310.pyc ADDED
Binary file (10.5 kB). View file
 
__pycache__/leaderboard_utils.cpython-310.pyc ADDED
Binary file (4.46 kB). View file
 
assets/2048/2048.gif ADDED

Git LFS Details

  • SHA256: 2cedc93fcf83a942603e9362fc0da36c9249e71dc6f527b0521644eb6e4b1787
  • Pointer size: 132 Bytes
  • Size of remote file: 4.97 MB
assets/candy/candy.gif ADDED

Git LFS Details

  • SHA256: 1c52a686dbf5331c64a0fb8015a9fd39b0779d3c7f77d865373f9d210f2dca98
  • Pointer size: 133 Bytes
  • Size of remote file: 17.7 MB
assets/sokoban/sokoban.gif ADDED

Git LFS Details

  • SHA256: ab9e7aa7f5bebf52c79f1409f6bfda43edbbf314d6c71110813ad66b7db90efd
  • Pointer size: 132 Bytes
  • Size of remote file: 4.55 MB
assets/super_mario_bros/super_mario.gif ADDED

Git LFS Details

  • SHA256: d1a5b6a176013a6ca37d17573dde021c58796100df1a93db35c2f21051231a65
  • Pointer size: 133 Bytes
  • Size of remote file: 74.5 MB
assets/tetris/tetris.gif ADDED

Git LFS Details

  • SHA256: c0b4d57eb156bc4ed253000948ebe9c926271311cb5a52f5506b0274d812b47b
  • Pointer size: 133 Bytes
  • Size of remote file: 11.1 MB
data_analysis.ipynb ADDED
@@ -0,0 +1 @@
 
 
1
+
data_visualization.py ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import matplotlib
2
+ matplotlib.use('Agg') # Use Agg backend for thread safety
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ import pandas as pd
6
+ from leaderboard_utils import (
7
+ get_organization,
8
+ get_mario_leaderboard,
9
+ get_sokoban_leaderboard,
10
+ get_2048_leaderboard,
11
+ get_candy_leaderboard,
12
+ get_tetris_leaderboard,
13
+ get_tetris_planning_leaderboard,
14
+ get_combined_leaderboard,
15
+ GAME_ORDER
16
+ )
17
+
18
+ # Define game score columns mapping
19
+ GAME_SCORE_COLUMNS = {
20
+ "Super Mario Bros": "Score",
21
+ "Sokoban": "Levels Cracked",
22
+ "2048": "Score",
23
+ "Candy Crash": "Average Score",
24
+ "Tetris (complete)": "Score",
25
+ "Tetris (planning only)": "Score"
26
+ }
27
+
28
+ def simplify_model_name(model_name):
29
+ """
30
+ Simplify model name by either taking first 11 chars or string before third '-'
31
+ """
32
+ hyphen_parts = model_name.split('-')
33
+ return '-'.join(hyphen_parts[:3]) if len(hyphen_parts) >= 3 else model_name[:11]
34
+
35
+ def create_horizontal_bar_chart(df, game_name):
36
+ """
37
+ Create horizontal bar chart for detailed game view
38
+
39
+ Args:
40
+ df (pd.DataFrame): DataFrame containing game data
41
+ game_name (str): Name of the game to display
42
+
43
+ Returns:
44
+ matplotlib.figure.Figure: The generated bar chart figure
45
+ """
46
+ # Close any existing figures to prevent memory leaks
47
+ plt.close('all')
48
+
49
+ # Set style
50
+ plt.style.use('default')
51
+ # Increase figure width to accommodate long model names
52
+ fig, ax = plt.subplots(figsize=(10, 6))
53
+
54
+ # Sort by score
55
+ if game_name == "Super Mario Bros":
56
+ score_col = "Score"
57
+ df_sorted = df.sort_values(by=score_col, ascending=True)
58
+ elif game_name == "Sokoban":
59
+ # Process Sokoban scores by splitting and getting max level
60
+ def get_max_level(levels_str):
61
+ try:
62
+ # Split by semicolon, strip whitespace, filter empty strings, convert to integers
63
+ levels = [int(x.strip()) for x in levels_str.split(";") if x.strip()]
64
+ return max(levels) if levels else 0
65
+ except:
66
+ return 0
67
+
68
+ # Create a temporary column with max levels
69
+ df['Max Level'] = df['Levels Cracked'].apply(get_max_level)
70
+ df_sorted = df.sort_values(by='Max Level', ascending=True)
71
+ score_col = 'Max Level'
72
+ elif game_name == "2048":
73
+ score_col = "Score"
74
+ df_sorted = df.sort_values(by=score_col, ascending=True)
75
+ elif game_name == "Candy Crash":
76
+ score_col = "Average Score"
77
+ df_sorted = df.sort_values(by=score_col, ascending=True)
78
+ elif game_name in ["Tetris (complete)", "Tetris (planning only)"]:
79
+ score_col = "Score"
80
+ df_sorted = df.sort_values(by=score_col, ascending=True)
81
+ else:
82
+ return None
83
+
84
+ # Create color gradient
85
+ colors = plt.cm.viridis(np.linspace(0.2, 0.8, len(df_sorted)))
86
+
87
+ # Create horizontal bars
88
+ bars = ax.barh(range(len(df_sorted)), df_sorted[score_col], color=colors)
89
+
90
+ # Add more space for labels on the left
91
+ plt.subplots_adjust(left=0.3)
92
+
93
+ # Customize the chart
94
+ ax.set_yticks(range(len(df_sorted)))
95
+
96
+ # Format player names: keep organization info and truncate the rest if too long
97
+ def format_player_name(player, org):
98
+ max_length = 40 # Maximum length for player name
99
+ if len(player) > max_length:
100
+ # Keep the first part and last part of the name
101
+ parts = player.split('-')
102
+ if len(parts) > 3:
103
+ formatted = f"{parts[0]}-{parts[1]}-...{parts[-1]}"
104
+ else:
105
+ formatted = player[:max_length-3] + "..."
106
+ else:
107
+ formatted = player
108
+ return f"{formatted} [{org}]"
109
+
110
+ player_labels = [format_player_name(row['Player'], row['Organization'])
111
+ for _, row in df_sorted.iterrows()]
112
+ ax.set_yticklabels(player_labels, fontsize=9)
113
+
114
+ # Add value labels on the bars
115
+ for i, bar in enumerate(bars):
116
+ width = bar.get_width()
117
+ if game_name == "Candy Crash":
118
+ score_text = f'{width:.1f}'
119
+ else:
120
+ score_text = f'{width:.0f}'
121
+
122
+ ax.text(width, bar.get_y() + bar.get_height()/2,
123
+ score_text,
124
+ ha='left', va='center',
125
+ fontsize=10,
126
+ fontweight='bold',
127
+ color='white',
128
+ bbox=dict(facecolor=(0, 0, 0, 0.3),
129
+ edgecolor='none',
130
+ alpha=0.5,
131
+ pad=2))
132
+
133
+ # Set title and labels
134
+ ax.set_title(f"{game_name} Performance",
135
+ pad=20,
136
+ fontsize=14,
137
+ fontweight='bold',
138
+ color='#2c3e50')
139
+
140
+ if game_name == "Sokoban":
141
+ ax.set_xlabel("Maximum Level Reached",
142
+ fontsize=12,
143
+ fontweight='bold',
144
+ color='#2c3e50',
145
+ labelpad=10)
146
+ else:
147
+ ax.set_xlabel(score_col,
148
+ fontsize=12,
149
+ fontweight='bold',
150
+ color='#2c3e50',
151
+ labelpad=10)
152
+
153
+ # Add grid lines
154
+ ax.grid(True, axis='x', linestyle='--', alpha=0.3)
155
+
156
+ # Remove top and right spines
157
+ ax.spines['top'].set_visible(False)
158
+ ax.spines['right'].set_visible(False)
159
+
160
+ # Adjust layout
161
+ plt.tight_layout()
162
+
163
+ return fig
164
+
165
+ def create_radar_charts(df):
166
+ """
167
+ Create two radar charts with improved normalization using z-scores
168
+ """
169
+ # Close any existing figures to prevent memory leaks
170
+ plt.close('all')
171
+
172
+ # Define reasoning models
173
+ reasoning_models = [
174
+ 'claude-3-7-sonnet-20250219(thinking)',
175
+ 'o1-2024-12-17',
176
+ 'gemini-2.0-flash-thinking-exp-1219',
177
+ 'o3-mini-2025-01-31(medium)',
178
+ 'gemini-2.5-pro-exp-03-25',
179
+ 'o1-mini-2024-09-12',
180
+ 'deepseek-r1'
181
+ ]
182
+
183
+ # Split dataframe into reasoning and non-reasoning models
184
+ df_reasoning = df[df['Player'].isin(reasoning_models)]
185
+ df_others = df[~df['Player'].isin(reasoning_models)]
186
+
187
+ # Get game columns
188
+ game_columns = [col for col in df.columns if col.endswith(' Score')]
189
+ categories = [col.replace(' Score', '') for col in game_columns]
190
+
191
+ # Create figure with two subplots - adjusted size for new layout
192
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 6), subplot_kw=dict(projection='polar'))
193
+ fig.patch.set_facecolor('white') # Set figure background to white
194
+
195
+ def normalize_values(values, mean, std):
196
+ """
197
+ Normalize values using z-score and scale to 0-100 range
198
+ """
199
+ if std == 0:
200
+ return [50 if v > 0 else 0 for v in values] # Handle zero std case
201
+ z_scores = [(v - mean) / std for v in values]
202
+ # Scale z-scores to 0-100 range, with mean at 50
203
+ scaled_values = [max(0, min(100, (z * 30) + 30)) for z in z_scores]
204
+ return scaled_values
205
+
206
+ def get_game_stats(df, game_col):
207
+ """
208
+ Get mean and std for a game column, handling missing values
209
+ """
210
+ values = []
211
+ for val in df[game_col]:
212
+ if isinstance(val, str) and val == '_':
213
+ values.append(0)
214
+ else:
215
+ try:
216
+ values.append(float(val))
217
+ except:
218
+ values.append(0)
219
+ return np.mean(values), np.std(values)
220
+
221
+ def setup_radar_plot(ax, data, title):
222
+ ax.set_facecolor('white') # Set subplot background to white
223
+
224
+ num_vars = len(categories)
225
+ angles = np.linspace(0, 2*np.pi, num_vars, endpoint=False)
226
+ angles = np.concatenate((angles, [angles[0]]))
227
+
228
+ # Plot grid lines with darker color
229
+ grid_values = [10, 30, 50, 70, 90]
230
+ ax.set_rgrids(grid_values,
231
+ labels=grid_values,
232
+ angle=45,
233
+ fontsize=6,
234
+ alpha=0.7, # Increased alpha for better visibility
235
+ color='#404040') # Darker color for grid labels
236
+
237
+ # Make grid lines darker but still subtle
238
+ ax.grid(True, color='#404040', alpha=0.3) # Darker grid lines
239
+
240
+ # Define darker, more vibrant colors for the radar plots
241
+ colors = ['#1f77b4', '#d62728', '#2ca02c', '#ff7f0e', '#9467bd', '#8c564b']
242
+
243
+ # Calculate game statistics once
244
+ game_stats = {col: get_game_stats(df, col) for col in game_columns}
245
+
246
+ # Plot data with darker lines and higher opacity for fills
247
+ for idx, (_, row) in enumerate(data.iterrows()):
248
+ values = []
249
+ for col in game_columns:
250
+ val = row[col]
251
+ if isinstance(val, str) and val == '_':
252
+ values.append(0)
253
+ else:
254
+ try:
255
+ values.append(float(val))
256
+ except:
257
+ values.append(0)
258
+
259
+ # Normalize values using game statistics
260
+ normalized_values = []
261
+ for i, v in enumerate(values):
262
+ mean, std = game_stats[game_columns[i]]
263
+ normalized_value = normalize_values([v], mean, std)[0]
264
+ normalized_values.append(normalized_value)
265
+
266
+ # Complete the circular plot
267
+ normalized_values = np.concatenate((normalized_values, [normalized_values[0]]))
268
+
269
+ model_name = simplify_model_name(row['Player'])
270
+ ax.plot(angles, normalized_values, 'o-', linewidth=2.0, # Increased line width
271
+ label=model_name,
272
+ color=colors[idx % len(colors)],
273
+ markersize=4) # Increased marker size
274
+ ax.fill(angles, normalized_values,
275
+ alpha=0.3, # Increased fill opacity
276
+ color=colors[idx % len(colors)])
277
+
278
+ # Format categories
279
+ formatted_categories = []
280
+ for game in categories:
281
+ if game == "Tetris (planning only)":
282
+ game = "Tetris\n(planning)"
283
+ elif game == "Tetris (complete)":
284
+ game = "Tetris\n(complete)"
285
+ elif game == "Super Mario Bros":
286
+ game = "Super\nMario"
287
+ elif game == "Candy Crash":
288
+ game = "Candy\nCrash"
289
+ formatted_categories.append(game)
290
+
291
+ ax.set_xticks(angles[:-1])
292
+ ax.set_xticklabels(formatted_categories,
293
+ fontsize=8, # Slightly larger font
294
+ color='#202020', # Darker text
295
+ fontweight='bold') # Bold text
296
+ ax.tick_params(pad=10, colors='#202020') # Darker tick colors
297
+
298
+ ax.set_title(title,
299
+ pad=20,
300
+ fontsize=11, # Slightly larger title
301
+ color='#202020', # Darker title
302
+ fontweight='bold') # Bold title
303
+
304
+ legend = ax.legend(loc='upper right',
305
+ bbox_to_anchor=(1.3, 1.1),
306
+ fontsize=7, # Slightly larger legend
307
+ framealpha=0.9, # More opaque legend
308
+ edgecolor='#404040', # Darker edge
309
+ ncol=1)
310
+
311
+ ax.set_ylim(0, 105)
312
+ ax.spines['polar'].set_color('#404040') # Darker spine
313
+ ax.spines['polar'].set_alpha(0.5) # More visible spine
314
+
315
+ # Setup both plots
316
+ setup_radar_plot(ax1, df_reasoning, "Reasoning Models")
317
+ setup_radar_plot(ax2, df_others, "Non-Reasoning Models")
318
+
319
+ plt.subplots_adjust(right=0.85, wspace=0.3)
320
+
321
+ return fig
322
+
323
+ def get_combined_leaderboard_with_radar(rank_data, selected_games):
324
+ """
325
+ Get combined leaderboard and create radar charts
326
+ """
327
+ df = get_combined_leaderboard(rank_data, selected_games)
328
+ radar_fig = create_radar_charts(df)
329
+ return df, radar_fig
330
+
331
+ def create_organization_radar_chart(rank_data):
332
+ """
333
+ Create radar chart comparing organizations
334
+ """
335
+ # Get combined leaderboard with all games
336
+ df = get_combined_leaderboard(rank_data, {game: True for game in GAME_ORDER})
337
+
338
+ # Group by organization and calculate average scores
339
+ org_performance = {}
340
+ for org in df["Organization"].unique():
341
+ org_df = df[df["Organization"] == org]
342
+ scores = {}
343
+ for game in GAME_ORDER:
344
+ game_scores = org_df[f"{game} Score"].apply(lambda x: float(x) if x != "_" else 0)
345
+ scores[game] = game_scores.mean()
346
+ org_performance[org] = scores
347
+
348
+ # Create radar chart
349
+ return create_radar_charts(pd.DataFrame([org_performance]))
350
+
351
+ def create_top_players_radar_chart(rank_data, n=5):
352
+ """
353
+ Create radar chart for top N players
354
+ """
355
+ # Get combined leaderboard with all games
356
+ df = get_combined_leaderboard(rank_data, {game: True for game in GAME_ORDER})
357
+
358
+ # Get top N players
359
+ top_players = df["Player"].head(n).tolist()
360
+
361
+ # Create radar chart for top players
362
+ return create_radar_charts(df[df["Player"].isin(top_players)])
363
+
364
+ def create_player_radar_chart(rank_data, player_name):
365
+ """
366
+ Create radar chart for a specific player
367
+ """
368
+ # Get combined leaderboard with all games
369
+ df = get_combined_leaderboard(rank_data, {game: True for game in GAME_ORDER})
370
+
371
+ # Get player's data
372
+ player_df = df[df["Player"] == player_name]
373
+
374
+ if player_df.empty:
375
+ return None
376
+
377
+ # Create radar chart for the player
378
+ return create_radar_charts(player_df)
379
+
380
+ def save_visualization(fig, filename):
381
+ """
382
+ Save visualization to file
383
+ """
384
+ fig.savefig(filename, bbox_inches='tight', dpi=300)
gradio_app.py ADDED
@@ -0,0 +1,505 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import pandas as pd
4
+ from PIL import Image, ImageSequence
5
+ import io
6
+
7
+ #######################################################
8
+ # Red Baron Game HTML
9
+ #######################################################
10
+ test_html = """
11
+ <!DOCTYPE html>
12
+ <html lang="en">
13
+ <head>
14
+ <meta charset="UTF-8">
15
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
16
+ <title>Simple Test Page</title>
17
+ <style>
18
+ body {
19
+ font-family: Arial, sans-serif;
20
+ margin: 0;
21
+ padding: 20px;
22
+ background-color: #f0f8ff;
23
+ color: #333;
24
+ }
25
+ .container {
26
+ max-width: 800px;
27
+ margin: 0 auto;
28
+ padding: 20px;
29
+ background-color: white;
30
+ border-radius: 8px;
31
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
32
+ }
33
+ h1 {
34
+ color: #4169e1;
35
+ text-align: center;
36
+ }
37
+ button {
38
+ background-color: #4169e1;
39
+ color: white;
40
+ border: none;
41
+ padding: 10px 15px;
42
+ border-radius: 4px;
43
+ cursor: pointer;
44
+ font-size: 16px;
45
+ margin: 10px 0;
46
+ }
47
+ button:hover {
48
+ background-color: #3158d3;
49
+ }
50
+ #canvas {
51
+ border: 1px solid #ddd;
52
+ display: block;
53
+ margin: 20px auto;
54
+ }
55
+ </style>
56
+ </head>
57
+ <body>
58
+ <div class="container">
59
+ <h1>Gradio HTML Test</h1>
60
+ <p>This is a simple HTML test page to verify that HTML rendering works in your Gradio application.</p>
61
+
62
+ <h2>Interactive Elements</h2>
63
+ <button id="testButton">Click Me!</button>
64
+ <p id="result">Button not clicked yet.</p>
65
+
66
+ <h2>Canvas Test</h2>
67
+ <canvas id="canvas" width="300" height="200"></canvas>
68
+
69
+ <h2>HTML Elements</h2>
70
+ <ul>
71
+ <li>List item 1</li>
72
+ <li>List item 2</li>
73
+ <li>List item 3</li>
74
+ </ul>
75
+ </div>
76
+
77
+ <script>
78
+ // Button click event
79
+ document.getElementById('testButton').addEventListener('click', function() {
80
+ document.getElementById('result').textContent = 'Button clicked at: ' + new Date().toLocaleTimeString();
81
+ });
82
+
83
+ // Canvas drawing
84
+ const canvas = document.getElementById('canvas');
85
+ const ctx = canvas.getContext('2d');
86
+
87
+ // Draw a simple scene
88
+ ctx.fillStyle = '#e0f0ff';
89
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
90
+
91
+ // Draw a circle
92
+ ctx.beginPath();
93
+ ctx.arc(150, 100, 50, 0, Math.PI * 2);
94
+ ctx.fillStyle = '#4169e1';
95
+ ctx.fill();
96
+
97
+ // Draw a rectangle
98
+ ctx.fillStyle = '#ff6347';
99
+ ctx.fillRect(50, 50, 40, 40);
100
+ </script>
101
+ </body>
102
+ </html>
103
+ """
104
+
105
+ game_html = """
106
+ <iframe id="gameFrame" style="width:660px; height:520px; border:none; display:block; margin:0 auto;" srcdoc='
107
+ <!DOCTYPE html>
108
+ <html>
109
+ <head>
110
+ <style>
111
+ body {
112
+ margin: 0;
113
+ padding: 0;
114
+ overflow: hidden;
115
+ font-family: sans-serif;
116
+ background: #1e1e1e;
117
+ }
118
+ #gameCanvas {
119
+ background: #70c5ce; /* sky-like */
120
+ display: block;
121
+ margin: 0 auto;
122
+ border: 2px solid black;
123
+ }
124
+ </style>
125
+ </head>
126
+ <body>
127
+ <canvas id="gameCanvas" width="640" height="480"></canvas>
128
+ <script>
129
+ // --- Simple "Red Baron"-style Game in Plain JS ---
130
+
131
+ // Grab the canvas and its context
132
+ const canvas = document.getElementById("gameCanvas");
133
+ const ctx = canvas.getContext("2d");
134
+
135
+ // Plane properties
136
+ let planeX = 50;
137
+ let planeY = canvas.height / 2;
138
+ const planeWidth = 40;
139
+ const planeHeight = 20;
140
+ const planeSpeed = 4;
141
+
142
+ // Bullet properties
143
+ let bullets = [];
144
+ const bulletSpeed = 6;
145
+ const bulletWidth = 6;
146
+ const bulletHeight = 2;
147
+
148
+ // Enemy properties
149
+ let enemies = [];
150
+ const enemyWidth = 40;
151
+ const enemyHeight = 20;
152
+ const enemySpeed = 2;
153
+ const spawnInterval = 100; // frames between spawns
154
+ let spawnCounter = 0;
155
+
156
+ // Key states
157
+ let keys = {
158
+ ArrowUp: false,
159
+ ArrowDown: false,
160
+ ArrowLeft: false,
161
+ ArrowRight: false,
162
+ Space: false
163
+ };
164
+
165
+ // Listen for keydown / keyup
166
+ document.addEventListener("keydown", (e) => {
167
+ if (keys.hasOwnProperty(e.code)) {
168
+ keys[e.code] = true;
169
+ }
170
+ });
171
+ document.addEventListener("keyup", (e) => {
172
+ if (keys.hasOwnProperty(e.code)) {
173
+ keys[e.code] = false;
174
+ }
175
+ });
176
+
177
+ // Main update loop
178
+ function update() {
179
+ // Move plane
180
+ if (keys["ArrowUp"] && planeY > 0) {
181
+ planeY -= planeSpeed;
182
+ }
183
+ if (keys["ArrowDown"] && planeY + planeHeight < canvas.height) {
184
+ planeY += planeSpeed;
185
+ }
186
+ if (keys["ArrowLeft"] && planeX > 0) {
187
+ planeX -= planeSpeed;
188
+ }
189
+ if (keys["ArrowRight"] && planeX + planeWidth < canvas.width) {
190
+ planeX += planeSpeed;
191
+ }
192
+
193
+ // Fire bullet (on every frame while space is held)
194
+ if (keys["Space"]) {
195
+ bullets.push({
196
+ x: planeX + planeWidth,
197
+ y: planeY + planeHeight / 2 - bulletHeight / 2,
198
+ w: bulletWidth,
199
+ h: bulletHeight
200
+ });
201
+ }
202
+
203
+ // Update bullets
204
+ for (let i = 0; i < bullets.length; i++) {
205
+ bullets[i].x += bulletSpeed;
206
+ }
207
+ // Remove bullets offscreen
208
+ bullets = bullets.filter((b) => b.x < canvas.width + 20);
209
+
210
+ // Spawn enemies periodically
211
+ spawnCounter++;
212
+ if (spawnCounter > spawnInterval) {
213
+ spawnCounter = 0;
214
+ enemies.push({
215
+ x: canvas.width,
216
+ y: Math.random() * (canvas.height - enemyHeight),
217
+ w: enemyWidth,
218
+ h: enemyHeight
219
+ });
220
+ }
221
+
222
+ // Update enemies
223
+ for (let i = 0; i < enemies.length; i++) {
224
+ enemies[i].x -= enemySpeed;
225
+ }
226
+ // Remove enemies offscreen
227
+ enemies = enemies.filter((e) => e.x > -enemyWidth);
228
+
229
+ // Check collisions bullets vs enemies
230
+ for (let e = enemies.length - 1; e >= 0; e--) {
231
+ let enemy = enemies[e];
232
+ for (let b = bullets.length - 1; b >= 0; b--) {
233
+ let bullet = bullets[b];
234
+ if (
235
+ bullet.x < enemy.x + enemy.w &&
236
+ bullet.x + bullet.w > enemy.x &&
237
+ bullet.y < enemy.y + enemy.h &&
238
+ bullet.y + bullet.h > enemy.y
239
+ ) {
240
+ // collision
241
+ enemies.splice(e, 1);
242
+ bullets.splice(b, 1);
243
+ break;
244
+ }
245
+ }
246
+ }
247
+
248
+ // Draw everything
249
+ draw();
250
+ requestAnimationFrame(update);
251
+ }
252
+
253
+ // Render the scene
254
+ function draw() {
255
+ // Clear
256
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
257
+
258
+ // Draw plane (red rectangle for demonstration)
259
+ ctx.fillStyle = "red";
260
+ ctx.fillRect(planeX, planeY, planeWidth, planeHeight);
261
+
262
+ // Draw bullets (small black rectangles)
263
+ ctx.fillStyle = "black";
264
+ bullets.forEach((b) => {
265
+ ctx.fillRect(b.x, b.y, b.w, b.h);
266
+ });
267
+
268
+ // Draw enemies (simple gray rectangles)
269
+ ctx.fillStyle = "gray";
270
+ enemies.forEach((en) => {
271
+ ctx.fillRect(en.x, en.y, en.w, en.h);
272
+ });
273
+ }
274
+
275
+ // Start game loop
276
+ window.onload = function() {
277
+ // Make sure canvas is fully loaded
278
+ update();
279
+ };
280
+ </script>
281
+ </body>
282
+ </html>
283
+ '></iframe>
284
+ <div style="text-align:center; margin-top:10px;">
285
+ <p>Controls: Arrow keys to move, Space to shoot</p>
286
+ </div>
287
+ """
288
+
289
+ #######################################################
290
+ # Dictionary of game -> directory paths
291
+ #######################################################
292
+ GAMES = {
293
+ "Super Mario Bros": "assets/super_mario_bros",
294
+ "Sokoban": "assets/sokoban",
295
+ "Tetris": "assets/tetris",
296
+ "2048": "assets/2048",
297
+ "Candy Crash": "assets/candy"
298
+ }
299
+ # Ensure each directory exists
300
+ for path in GAMES.values():
301
+ os.makedirs(path, exist_ok=True)
302
+
303
+ #######################################################
304
+ # Scoreboard data for each game
305
+ #######################################################
306
+
307
+ # TODO (lanxiang): read actual data here
308
+ mario_scores = [
309
+ ["Alice", 9000, "3/5", "00:35"],
310
+ ["Bob", 2500, "2/5", "00:12"],
311
+ ["Carol", 500, "1/5", "00:05"]
312
+ ]
313
+ sokoban_scores = [
314
+ ["Alice", 100, 3],
315
+ ["Bob", 350, 2],
316
+ ["Carol", 500, 1]
317
+ ]
318
+
319
+ tetris_scores = [
320
+ ["Alice", 15000, 120],
321
+ ["Bob", 8000, 60],
322
+ ["Carol", 4000, 45]
323
+ ]
324
+
325
+ candy_scores = [
326
+ ["Alice", 12000, 10],
327
+ ["Bob", 9500, 8],
328
+ ["Carol", 3000, 5]
329
+ ]
330
+
331
+ # 2048 columns: [Player, Scores, #Steps]
332
+ game_2048_scores = [
333
+ ["Alice", 8192, 300],
334
+ ["Bob", 4096, 200],
335
+ ["Carol", 2048, 150]
336
+ ]
337
+
338
+ #######################################################
339
+ # Functions to return the scoreboard as DataFrames
340
+ #######################################################
341
+ def get_mario_leaderboard():
342
+ return pd.DataFrame(
343
+ mario_scores,
344
+ columns=["Player", "Progress (current/total)", "Score", "Time"]
345
+ )
346
+
347
+ def get_sokoban_leaderboard():
348
+ return pd.DataFrame(
349
+ sokoban_scores,
350
+ columns=["Player", "Levels Cracked", "Steps"]
351
+ )
352
+
353
+ def get_tetris_leaderboard():
354
+ return pd.DataFrame(
355
+ tetris_scores,
356
+ columns=["Player", "Scores", "Steps"]
357
+ )
358
+
359
+ def get_candy_leaderboard():
360
+ return pd.DataFrame(
361
+ candy_scores,
362
+ columns=["Player", "Levels Cracked", "Scores"]
363
+ )
364
+
365
+ def get_2048_leaderboard():
366
+ return pd.DataFrame(
367
+ game_2048_scores,
368
+ columns=["Player", "Scores", "Steps"]
369
+ )
370
+
371
+ #######################################################
372
+ # GIF Handling
373
+ #######################################################
374
+ def create_or_update_resized_gif(original_path, max_dim=600):
375
+ base, ext = os.path.splitext(original_path)
376
+ resized_path = f"{base}_resized{ext}"
377
+ if os.path.exists(resized_path):
378
+ return resized_path
379
+
380
+ with Image.open(original_path) as im:
381
+ w, h = im.size
382
+ needs_resize = (w > max_dim or h > max_dim)
383
+
384
+ frames = []
385
+ for frame in ImageSequence.Iterator(im):
386
+ frame_rgba = frame.convert("RGBA")
387
+ if needs_resize:
388
+ ratio = min(max_dim / w, max_dim / h)
389
+ new_w, new_h = int(w * ratio), int(h * ratio)
390
+ frame_rgba = frame_rgba.resize((new_w, new_h), Image.LANCZOS)
391
+ frames.append(frame_rgba.convert("P"))
392
+
393
+ output_bytes = io.BytesIO()
394
+ frames[0].save(
395
+ output_bytes,
396
+ format="GIF",
397
+ save_all=True,
398
+ append_images=frames[1:],
399
+ loop=0,
400
+ disposal=2,
401
+ optimize=False
402
+ )
403
+ output_bytes.seek(0)
404
+
405
+ with open(resized_path, "wb") as f_out:
406
+ f_out.write(output_bytes.read())
407
+ return resized_path
408
+
409
+ def list_gifs(game_name):
410
+ gif_dir = GAMES[game_name]
411
+ all_gifs = [
412
+ os.path.join(gif_dir, f)
413
+ for f in os.listdir(gif_dir)
414
+ if f.lower().endswith(".gif") and not f.lower().endswith("_resized.gif")
415
+ ]
416
+ resized_paths = []
417
+ for gif_path in all_gifs:
418
+ resized_gif_path = create_or_update_resized_gif(gif_path, max_dim=600)
419
+ resized_paths.append(resized_gif_path)
420
+
421
+ previously_resized = [
422
+ os.path.join(gif_dir, f)
423
+ for f in os.listdir(gif_dir)
424
+ if f.lower().endswith("_resized.gif")
425
+ ]
426
+ all_resized = list(set(resized_paths + previously_resized))
427
+ return sorted(all_resized)
428
+
429
+ #######################################################
430
+ # Custom CSS
431
+ #######################################################
432
+ fancy_css = """
433
+ body {
434
+ font-family: 'Trebuchet MS', sans-serif;
435
+ background: #f0f8ff;
436
+ color: #333333;
437
+ }
438
+ h1 {
439
+ color: #4b9cd3;
440
+ text-align: center;
441
+ margin-top: 20px;
442
+ }
443
+ .gradio-container {
444
+ max-width: 800px;
445
+ margin: 0 auto;
446
+ padding: 20px;
447
+ }
448
+ """
449
+
450
+ #######################################################
451
+ # Build the App
452
+ #######################################################
453
+ def build_app():
454
+ with gr.Blocks(css=fancy_css) as demo:
455
+ gr.Markdown("# Game Arena: Gaming Agent")
456
+
457
+ with gr.Tabs():
458
+ # tab: "Gallery"
459
+ with gr.Tab("Gallery"):
460
+ with gr.Tabs():
461
+ for game_name in GAMES:
462
+ with gr.Tab(game_name):
463
+ gr.Markdown(f"### {game_name} Gallery")
464
+ gr.Gallery(
465
+ label="GIFs",
466
+ value=list_gifs(game_name)
467
+ )
468
+
469
+ # tab: "Leaderboard"
470
+ with gr.Tab("Leaderboard"):
471
+ gr.Markdown("## Game Leaderboards")
472
+
473
+ # Sub-tabs for each game
474
+ with gr.Tabs():
475
+ with gr.Tab("Mario"):
476
+ gr.Markdown("### Mario Leaderboard")
477
+ gr.DataFrame(value=get_mario_leaderboard(), interactive=False)
478
+
479
+ with gr.Tab("Sokoban"):
480
+ gr.Markdown("### Sokoban Leaderboard")
481
+ gr.DataFrame(value=get_sokoban_leaderboard(), interactive=False)
482
+
483
+ with gr.Tab("Tetris"):
484
+ gr.Markdown("### Tetris Leaderboard")
485
+ gr.DataFrame(value=get_tetris_leaderboard(), interactive=False)
486
+
487
+ with gr.Tab("2048"):
488
+ gr.Markdown("### 2048 Leaderboard")
489
+ gr.DataFrame(value=get_2048_leaderboard(), interactive=False)
490
+
491
+ with gr.Tab("Candy Crash"):
492
+ gr.Markdown("### Candy Crash Leaderboard")
493
+ gr.DataFrame(value=get_candy_leaderboard(), interactive=False)
494
+
495
+ # Top-level tab: "Red Baron" game demo
496
+ with gr.Tab("Red Baron"):
497
+ gr.Markdown("## Red Baron Game Demo")
498
+ gr.HTML(game_html)
499
+
500
+ return demo
501
+
502
+ if __name__ == "__main__":
503
+ demo_app = build_app()
504
+ # demo_app.launch(server_name="0.0.0.0", server_port=7860)
505
+ demo_app.launch(server_name="127.0.0.1", server_port=7860, debug=True)
gradio_app_v2.py ADDED
@@ -0,0 +1,540 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_radar,
24
+ create_organization_radar_chart,
25
+ create_top_players_radar_chart,
26
+ create_player_radar_chart,
27
+ create_horizontal_bar_chart
28
+ )
29
+
30
+ # Define time points and their corresponding data files
31
+ TIME_POINTS = {
32
+ "03/25/2025": "rank_data_03_25_2025.json",
33
+ # Add more time points here as they become available
34
+ }
35
+
36
+ # Load the initial JSON file with rank data
37
+ with open(TIME_POINTS["03/25/2025"], "r") as f:
38
+ rank_data = json.load(f)
39
+
40
+ # Add leaderboard state at the top level
41
+ leaderboard_state = {
42
+ "current_game": None,
43
+ "previous_overall": {
44
+ "Super Mario Bros": True,
45
+ "Sokoban": True,
46
+ "2048": True,
47
+ "Candy Crash": True,
48
+ "Tetris (complete)": True,
49
+ "Tetris (planning only)": True
50
+ },
51
+ "previous_details": {
52
+ "Super Mario Bros": False,
53
+ "Sokoban": False,
54
+ "2048": False,
55
+ "Candy Crash": False,
56
+ "Tetris (complete)": False,
57
+ "Tetris (planning only)": False
58
+ }
59
+ }
60
+
61
+ # Define GIF paths for the carousel
62
+ GIF_PATHS = [
63
+ "assets/super_mario_bros/super_mario.gif",
64
+ "assets/sokoban/sokoban.gif",
65
+ "assets/2048/2048.gif",
66
+ "assets/candy/candy.gif",
67
+ "assets/tetris/tetris.gif"
68
+ ]
69
+
70
+ # Print and verify GIF paths
71
+ print("\nChecking GIF paths:")
72
+ for gif_path in GIF_PATHS:
73
+ if os.path.exists(gif_path):
74
+ print(f"✓ Found: {gif_path}")
75
+ # Print file size
76
+ size = os.path.getsize(gif_path)
77
+ print(f" Size: {size / (1024*1024):.2f} MB")
78
+ else:
79
+ print(f"✗ Missing: {gif_path}")
80
+
81
+ def load_gif(gif_path):
82
+ """Load a GIF file and return it as a PIL Image"""
83
+ try:
84
+ img = Image.open(gif_path)
85
+ print(f"Successfully loaded GIF: {gif_path}")
86
+ return img
87
+ except Exception as e:
88
+ print(f"Error loading GIF {gif_path}: {e}")
89
+ return None
90
+
91
+ def create_gif_carousel():
92
+ """Create a custom HTML/JS component for GIF carousel"""
93
+ print("\nCreating GIF carousel with paths:", GIF_PATHS)
94
+ html = f"""
95
+ <div id="gif-carousel" style="width: 100%; height: 300px; position: relative; background-color: #f0f0f0;">
96
+ <img id="current-gif" style="width: 100%; height: 100%; object-fit: contain;" onerror="console.error('Failed to load GIF:', this.src);">
97
+ </div>
98
+ <script>
99
+ const gifs = {json.dumps(GIF_PATHS)};
100
+ let currentIndex = 0;
101
+
102
+ function updateGif() {{
103
+ const img = document.getElementById('current-gif');
104
+ console.log('Loading GIF:', gifs[currentIndex]);
105
+ img.src = gifs[currentIndex];
106
+ currentIndex = (currentIndex + 1) % gifs.length;
107
+ }}
108
+
109
+ // Update GIF every 5 seconds
110
+ setInterval(updateGif, 5000);
111
+ // Initial load
112
+ updateGif();
113
+ </script>
114
+ """
115
+ return gr.HTML(html)
116
+
117
+ def load_rank_data(time_point):
118
+ """Load rank data for a specific time point"""
119
+ if time_point in TIME_POINTS:
120
+ try:
121
+ with open(TIME_POINTS[time_point], "r") as f:
122
+ return json.load(f)
123
+ except FileNotFoundError:
124
+ return None
125
+ return None
126
+
127
+ def update_leaderboard(mario_overall, mario_details,
128
+ sokoban_overall, sokoban_details,
129
+ _2048_overall, _2048_details,
130
+ candy_overall, candy_details,
131
+ tetris_overall, tetris_details,
132
+ tetris_plan_overall, tetris_plan_details):
133
+ global leaderboard_state
134
+
135
+ # Convert current checkbox states to dictionary for easier comparison
136
+ current_overall = {
137
+ "Super Mario Bros": mario_overall,
138
+ "Sokoban": sokoban_overall,
139
+ "2048": _2048_overall,
140
+ "Candy Crash": candy_overall,
141
+ "Tetris (complete)": tetris_overall,
142
+ "Tetris (planning only)": tetris_plan_overall
143
+ }
144
+
145
+ current_details = {
146
+ "Super Mario Bros": mario_details,
147
+ "Sokoban": sokoban_details,
148
+ "2048": _2048_details,
149
+ "Candy Crash": candy_details,
150
+ "Tetris (complete)": tetris_details,
151
+ "Tetris (planning only)": tetris_plan_details
152
+ }
153
+
154
+ # Find which game's state changed
155
+ changed_game = None
156
+ for game in current_overall.keys():
157
+ if (current_overall[game] != leaderboard_state["previous_overall"][game] or
158
+ current_details[game] != leaderboard_state["previous_details"][game]):
159
+ changed_game = game
160
+ break
161
+
162
+ if changed_game:
163
+ # If a game's details checkbox was checked
164
+ if current_details[changed_game] and not leaderboard_state["previous_details"][changed_game]:
165
+ # Reset all other games' states
166
+ for game in current_overall.keys():
167
+ if game != changed_game:
168
+ current_overall[game] = False
169
+ current_details[game] = False
170
+ leaderboard_state["previous_overall"][game] = False
171
+ leaderboard_state["previous_details"][game] = False
172
+
173
+ # Update state for the selected game
174
+ leaderboard_state["current_game"] = changed_game
175
+ leaderboard_state["previous_overall"][changed_game] = True # Set overall to True when details is checked
176
+ leaderboard_state["previous_details"][changed_game] = True
177
+ current_overall[changed_game] = True # Ensure the overall checkbox is checked
178
+
179
+ # If a game's overall checkbox was checked
180
+ elif current_overall[changed_game] and not leaderboard_state["previous_overall"][changed_game]:
181
+ # If we were in details view for another game, switch to overall view
182
+ if leaderboard_state["current_game"] and leaderboard_state["previous_details"][leaderboard_state["current_game"]]:
183
+ # Reset previous game's details
184
+ leaderboard_state["previous_details"][leaderboard_state["current_game"]] = False
185
+ current_details[leaderboard_state["current_game"]] = False
186
+ leaderboard_state["current_game"] = None
187
+
188
+ # Update state
189
+ leaderboard_state["previous_overall"][changed_game] = True
190
+ leaderboard_state["previous_details"][changed_game] = False
191
+
192
+ # If a game's overall checkbox was unchecked
193
+ elif not current_overall[changed_game] and leaderboard_state["previous_overall"][changed_game]:
194
+ # If we're in details view, don't allow unchecking the overall checkbox
195
+ if leaderboard_state["current_game"] == changed_game:
196
+ current_overall[changed_game] = True
197
+ else:
198
+ leaderboard_state["previous_overall"][changed_game] = False
199
+ if leaderboard_state["current_game"] == changed_game:
200
+ leaderboard_state["current_game"] = None
201
+
202
+ # If a game's details checkbox was unchecked
203
+ elif not current_details[changed_game] and leaderboard_state["previous_details"][changed_game]:
204
+ leaderboard_state["previous_details"][changed_game] = False
205
+ if leaderboard_state["current_game"] == changed_game:
206
+ leaderboard_state["current_game"] = None
207
+
208
+ # Build dictionary for selected games
209
+ selected_games = {
210
+ "Super Mario Bros": current_overall["Super Mario Bros"],
211
+ "Sokoban": current_overall["Sokoban"],
212
+ "2048": current_overall["2048"],
213
+ "Candy Crash": current_overall["Candy Crash"],
214
+ "Tetris (complete)": current_overall["Tetris (complete)"],
215
+ "Tetris (planning only)": current_overall["Tetris (planning only)"]
216
+ }
217
+
218
+ # Filter GIF paths based on selected games
219
+ filtered_gifs = []
220
+ if current_overall["Super Mario Bros"]:
221
+ filtered_gifs.append(GIF_PATHS[0])
222
+ if current_overall["Sokoban"]:
223
+ filtered_gifs.append(GIF_PATHS[1])
224
+ if current_overall["2048"]:
225
+ filtered_gifs.append(GIF_PATHS[2])
226
+ if current_overall["Candy Crash"]:
227
+ filtered_gifs.append(GIF_PATHS[3])
228
+ if current_overall["Tetris (complete)"] or current_overall["Tetris (planning only)"]:
229
+ filtered_gifs.append(GIF_PATHS[4])
230
+
231
+ # Get the appropriate DataFrame and chart based on current state
232
+ if leaderboard_state["current_game"]:
233
+ # For detailed view
234
+ if leaderboard_state["current_game"] == "Super Mario Bros":
235
+ df = get_mario_leaderboard(rank_data)
236
+ elif leaderboard_state["current_game"] == "Sokoban":
237
+ df = get_sokoban_leaderboard(rank_data)
238
+ elif leaderboard_state["current_game"] == "2048":
239
+ df = get_2048_leaderboard(rank_data)
240
+ elif leaderboard_state["current_game"] == "Candy Crash":
241
+ df = get_candy_leaderboard(rank_data)
242
+ elif leaderboard_state["current_game"] == "Tetris (complete)":
243
+ df = get_tetris_leaderboard(rank_data)
244
+ else: # Tetris (planning only)
245
+ df = get_tetris_planning_leaderboard(rank_data)
246
+
247
+ # Always create a new chart for detailed view
248
+ chart = create_horizontal_bar_chart(df, leaderboard_state["current_game"])
249
+ else:
250
+ # For overall view
251
+ df = get_combined_leaderboard(rank_data, selected_games)
252
+ _, chart = get_combined_leaderboard_with_radar(rank_data, selected_games)
253
+
254
+ return (df, chart, filtered_gifs,
255
+ current_overall["Super Mario Bros"], current_details["Super Mario Bros"],
256
+ current_overall["Sokoban"], current_details["Sokoban"],
257
+ current_overall["2048"], current_details["2048"],
258
+ current_overall["Candy Crash"], current_details["Candy Crash"],
259
+ current_overall["Tetris (complete)"], current_details["Tetris (complete)"],
260
+ current_overall["Tetris (planning only)"], current_details["Tetris (planning only)"])
261
+
262
+ def update_leaderboard_with_time(time_point, mario_overall, mario_details,
263
+ sokoban_overall, sokoban_details,
264
+ _2048_overall, _2048_details,
265
+ candy_overall, candy_details,
266
+ tetris_overall, tetris_details,
267
+ tetris_plan_overall, tetris_plan_details):
268
+ # Load rank data for the selected time point
269
+ global rank_data
270
+ new_rank_data = load_rank_data(time_point)
271
+ if new_rank_data is not None:
272
+ rank_data = new_rank_data
273
+
274
+ # Use the existing update_leaderboard function
275
+ return update_leaderboard(mario_overall, mario_details,
276
+ sokoban_overall, sokoban_details,
277
+ _2048_overall, _2048_details,
278
+ candy_overall, candy_details,
279
+ tetris_overall, tetris_details,
280
+ tetris_plan_overall, tetris_plan_details)
281
+
282
+ def clear_filters():
283
+ global leaderboard_state
284
+
285
+ # Reset all checkboxes to default state and get fresh data
286
+ df = get_combined_leaderboard(rank_data, {
287
+ "Super Mario Bros": True,
288
+ "Sokoban": True,
289
+ "2048": True,
290
+ "Candy Crash": True,
291
+ "Tetris (complete)": True,
292
+ "Tetris (planning only)": True
293
+ })
294
+
295
+ # Get the radar chart visualization
296
+ _, chart = get_combined_leaderboard_with_radar(rank_data, {
297
+ "Super Mario Bros": True,
298
+ "Sokoban": True,
299
+ "2048": True,
300
+ "Candy Crash": True,
301
+ "Tetris (complete)": True,
302
+ "Tetris (planning only)": True
303
+ })
304
+
305
+ # Reset the leaderboard state to match the default checkbox states
306
+ leaderboard_state = {
307
+ "current_game": None,
308
+ "previous_overall": {
309
+ "Super Mario Bros": True,
310
+ "Sokoban": True,
311
+ "2048": True,
312
+ "Candy Crash": True,
313
+ "Tetris (complete)": True,
314
+ "Tetris (planning only)": True
315
+ },
316
+ "previous_details": {
317
+ "Super Mario Bros": False,
318
+ "Sokoban": False,
319
+ "2048": False,
320
+ "Candy Crash": False,
321
+ "Tetris (complete)": False,
322
+ "Tetris (planning only)": False
323
+ }
324
+ }
325
+
326
+ # Return both the DataFrame and the visualization
327
+ return (df, chart, GIF_PATHS,
328
+ True, False, # mario
329
+ True, False, # sokoban
330
+ True, False, # 2048
331
+ True, False, # candy
332
+ True, False, # tetris
333
+ True, False) # tetris plan
334
+
335
+ def build_app():
336
+ with gr.Blocks(css="""
337
+ .gallery-container {
338
+ height: 50vh !important;
339
+ max-height: 600px !important;
340
+ min-height: 300px !important;
341
+ background-color: #f8f9fa;
342
+ border-radius: 10px;
343
+ padding: 5px !important;
344
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
345
+ overflow: hidden;
346
+ aspect-ratio: 1 !important;
347
+ display: flex !important;
348
+ align-items: center !important;
349
+ justify-content: center !important;
350
+ }
351
+ .gallery-container .gallery-item {
352
+ height: 100% !important;
353
+ width: 100% !important;
354
+ border-radius: 8px;
355
+ overflow: hidden;
356
+ margin: 0 !important;
357
+ padding: 0 !important;
358
+ aspect-ratio: 1 !important;
359
+ display: flex !important;
360
+ align-items: center !important;
361
+ justify-content: center !important;
362
+ }
363
+ .gallery-container .gallery-item img {
364
+ height: 100% !important;
365
+ width: 100% !important;
366
+ object-fit: contain !important;
367
+ aspect-ratio: 1 !important;
368
+ }
369
+ .visualization-container {
370
+ height: 50vh !important;
371
+ max-height: 600px !important;
372
+ min-height: 300px !important;
373
+ background-color: #f8f9fa;
374
+ border-radius: 10px;
375
+ padding: 15px;
376
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
377
+ overflow: hidden;
378
+ margin-left: 10px !important; /* Add small gap between gallery and visualization */
379
+ }
380
+ .visualization-container .plot {
381
+ height: 100% !important;
382
+ width: 100% !important;
383
+ }
384
+ .section-title {
385
+ font-size: 1.5em;
386
+ font-weight: bold;
387
+ color: #2c3e50;
388
+ margin-bottom: 15px;
389
+ padding-bottom: 10px;
390
+ border-bottom: 2px solid #e9ecef;
391
+ }
392
+ /* Add container for the entire app */
393
+ .container {
394
+ max-width: 1400px;
395
+ margin: 0 auto;
396
+ padding: 0 20px;
397
+ }
398
+ /* Add flex layout for the row containing gallery and visualization */
399
+ .gallery-viz-row {
400
+ display: flex !important;
401
+ align-items: center !important;
402
+ gap: 20px !important; /* Add consistent gap between components */
403
+ }
404
+ """) as demo:
405
+ gr.Markdown("# 🎮 Game Arena: Gaming Agent 🎲")
406
+
407
+ with gr.Tabs():
408
+ with gr.Tab("🏆 Leaderboard"):
409
+ # Visualization section at the very top
410
+ with gr.Row():
411
+ gr.Markdown("### 📊 Gallery")
412
+ with gr.Row(elem_classes="gallery-viz-row"):
413
+ # Split into two columns
414
+ with gr.Column(scale=3):
415
+ gallery = gr.Gallery(
416
+ value=GIF_PATHS,
417
+ label="Game Demos",
418
+ show_label=True,
419
+ elem_id="gallery",
420
+ elem_classes="gallery-container",
421
+ columns=1,
422
+ rows=1,
423
+ min_width=100,
424
+ container=True,
425
+ allow_preview=True,
426
+ object_fit='contain',
427
+ show_download_button=False,
428
+ show_share_button=False,
429
+ show_fullscreen_button=True
430
+ )
431
+ with gr.Column(scale=4):
432
+ visualization = gr.Plot(
433
+ value=get_combined_leaderboard_with_radar(rank_data, {
434
+ "Super Mario Bros": True,
435
+ "Sokoban": True,
436
+ "2048": True,
437
+ "Candy Crash": True,
438
+ "Tetris (complete)": True,
439
+ "Tetris (planning only)": True
440
+ })[1],
441
+ label="Performance Visualization",
442
+ elem_classes="visualization-container"
443
+ )
444
+
445
+ # Game selection section
446
+ with gr.Row():
447
+ gr.Markdown("### 🎮 Game Selection")
448
+ with gr.Row():
449
+ # For each game, we have two checkboxes: one for overall and one for detailed view.
450
+ with gr.Column():
451
+ gr.Markdown("**🎮 Super Mario Bros**")
452
+ mario_overall = gr.Checkbox(label="Super Mario Bros Score", value=True)
453
+ mario_details = gr.Checkbox(label="Super Mario Bros Details", value=False)
454
+ with gr.Column():
455
+ gr.Markdown("**📦 Sokoban**")
456
+ sokoban_overall = gr.Checkbox(label="Sokoban Score", value=True)
457
+ sokoban_details = gr.Checkbox(label="Sokoban Details", value=False)
458
+ with gr.Column():
459
+ gr.Markdown("**🔢 2048**")
460
+ _2048_overall = gr.Checkbox(label="2048 Score", value=True)
461
+ _2048_details = gr.Checkbox(label="2048 Details", value=False)
462
+ with gr.Column():
463
+ gr.Markdown("**🍬 Candy Crash**")
464
+ candy_overall = gr.Checkbox(label="Candy Crash Score", value=True)
465
+ candy_details = gr.Checkbox(label="Candy Crash Details", value=False)
466
+ with gr.Column():
467
+ gr.Markdown("**🎯 Tetris (complete)**")
468
+ tetris_overall = gr.Checkbox(label="Tetris (complete) Score", value=True)
469
+ tetris_details = gr.Checkbox(label="Tetris (complete) Details", value=False)
470
+ with gr.Column():
471
+ gr.Markdown("**📋 Tetris (planning)**")
472
+ tetris_plan_overall = gr.Checkbox(label="Tetris (planning) Score", value=True)
473
+ tetris_plan_details = gr.Checkbox(label="Tetris (planning) Details", value=False)
474
+
475
+ # Time progression display and control buttons - Moved below game selection
476
+ with gr.Row():
477
+ with gr.Column(scale=2):
478
+ gr.Markdown("**⏰ Time Tracker**")
479
+ time_slider = gr.Slider(
480
+ minimum=0,
481
+ maximum=1,
482
+ value=1,
483
+ step=1,
484
+ label="Model Time Point",
485
+ info="Current Time: 03/25/2025"
486
+ )
487
+ with gr.Column(scale=1):
488
+ gr.Markdown("**🔄 Controls**")
489
+ clear_btn = gr.Button("Reset Filters", variant="secondary")
490
+
491
+ # Leaderboard table section
492
+ with gr.Row():
493
+ gr.Markdown("### 📋 Detailed Results")
494
+ with gr.Row():
495
+ leaderboard_board = gr.DataFrame(
496
+ value=get_combined_leaderboard(rank_data, {
497
+ "Super Mario Bros": True,
498
+ "Sokoban": True,
499
+ "2048": True,
500
+ "Candy Crash": True,
501
+ "Tetris (complete)": True,
502
+ "Tetris (planning only)": True
503
+ }),
504
+ interactive=True,
505
+ wrap=True,
506
+ label="Leaderboard"
507
+ )
508
+
509
+ # List of all checkboxes (in order)
510
+ checkbox_list = [mario_overall, mario_details,
511
+ sokoban_overall, sokoban_details,
512
+ _2048_overall, _2048_details,
513
+ candy_overall, candy_details,
514
+ tetris_overall, tetris_details,
515
+ tetris_plan_overall, tetris_plan_details]
516
+
517
+ # Initialize the leaderboard state when the app starts
518
+ clear_filters()
519
+
520
+ # Update both the leaderboard and visualization when checkboxes change
521
+ for checkbox in checkbox_list:
522
+ checkbox.change(
523
+ fn=update_leaderboard,
524
+ inputs=checkbox_list,
525
+ outputs=[leaderboard_board, visualization, gallery] + checkbox_list
526
+ )
527
+
528
+ # Update both when clear button is clicked
529
+ clear_btn.click(
530
+ fn=clear_filters,
531
+ inputs=[],
532
+ outputs=[leaderboard_board, visualization, gallery] + checkbox_list
533
+ )
534
+
535
+ return demo
536
+
537
+ if __name__ == "__main__":
538
+ demo_app = build_app()
539
+ # Add file serving configuration
540
+ demo_app.launch(debug=True, show_error=True, share=True)
leaderboard_utils.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import json
3
+ import numpy as np
4
+
5
+ # Define game order
6
+ GAME_ORDER = [
7
+ "Super Mario Bros",
8
+ "Sokoban",
9
+ "2048",
10
+ "Candy Crash",
11
+ "Tetris (complete)",
12
+ "Tetris (planning only)"
13
+ ]
14
+
15
+ def get_organization(model_name):
16
+ m = model_name.lower()
17
+ if "claude" in m:
18
+ return "anthropic"
19
+ elif "gemini" in m:
20
+ return "google"
21
+ elif "o1" in m or "gpt" in m or "o3" in m:
22
+ return "openai"
23
+ elif "deepseek" in m:
24
+ return "deepseek"
25
+ else:
26
+ return "unknown"
27
+
28
+ def get_mario_leaderboard(rank_data):
29
+ data = rank_data.get("Super Mario Bros", {}).get("results", [])
30
+ df = pd.DataFrame(data)
31
+ df = df.rename(columns={
32
+ "model": "Player",
33
+ "progress": "Progress (current/total)",
34
+ "score": "Score",
35
+ "time_s": "Time (s)"
36
+ })
37
+ df["Organization"] = df["Player"].apply(get_organization)
38
+ df = df[["Player", "Organization", "Progress (current/total)", "Score", "Time (s)"]]
39
+ return df
40
+
41
+ def get_sokoban_leaderboard(rank_data):
42
+ data = rank_data.get("Sokoban", {}).get("results", [])
43
+ df = pd.DataFrame(data)
44
+ df = df.rename(columns={
45
+ "model": "Player",
46
+ "levels_cracked": "Levels Cracked",
47
+ "steps": "Steps"
48
+ })
49
+ df["Organization"] = df["Player"].apply(get_organization)
50
+ df = df[["Player", "Organization", "Levels Cracked", "Steps"]]
51
+ return df
52
+
53
+ def get_2048_leaderboard(rank_data):
54
+ data = rank_data.get("2048", {}).get("results", [])
55
+ df = pd.DataFrame(data)
56
+ df = df.rename(columns={
57
+ "model": "Player",
58
+ "score": "Score",
59
+ "steps": "Steps",
60
+ "time": "Time"
61
+ })
62
+ df["Organization"] = df["Player"].apply(get_organization)
63
+ df = df[["Player", "Organization", "Score", "Steps", "Time"]]
64
+ return df
65
+
66
+ def get_candy_leaderboard(rank_data):
67
+ data = rank_data.get("Candy Crash", {}).get("results", [])
68
+ df = pd.DataFrame(data)
69
+ df = df.rename(columns={
70
+ "model": "Player",
71
+ "score_runs": "Score Runs",
72
+ "average_score": "Average Score",
73
+ "steps": "Steps"
74
+ })
75
+ df["Organization"] = df["Player"].apply(get_organization)
76
+ df = df[["Player", "Organization", "Score Runs", "Average Score", "Steps"]]
77
+ return df
78
+
79
+ def get_tetris_leaderboard(rank_data):
80
+ data = rank_data.get("Tetris (complete)", {}).get("results", [])
81
+ df = pd.DataFrame(data)
82
+ df = df.rename(columns={
83
+ "model": "Player",
84
+ "score": "Score",
85
+ "steps_blocks": "Steps"
86
+ })
87
+ df["Organization"] = df["Player"].apply(get_organization)
88
+ df = df[["Player", "Organization", "Score", "Steps"]]
89
+ return df
90
+
91
+ def get_tetris_planning_leaderboard(rank_data):
92
+ data = rank_data.get("Tetris (planning only)", {}).get("results", [])
93
+ df = pd.DataFrame(data)
94
+ df = df.rename(columns={
95
+ "model": "Player",
96
+ "score": "Score",
97
+ "steps_blocks": "Steps"
98
+ })
99
+ df["Organization"] = df["Player"].apply(get_organization)
100
+ df = df[["Player", "Organization", "Score", "Steps"]]
101
+ return df
102
+
103
+ def calculate_rank_and_completeness(rank_data, selected_games):
104
+ # Dictionary to store DataFrames for each game
105
+ game_dfs = {}
106
+
107
+ # Get DataFrames for selected games
108
+ if selected_games.get("Super Mario Bros"):
109
+ game_dfs["Super Mario Bros"] = get_mario_leaderboard(rank_data)
110
+ if selected_games.get("Sokoban"):
111
+ game_dfs["Sokoban"] = get_sokoban_leaderboard(rank_data)
112
+ if selected_games.get("2048"):
113
+ game_dfs["2048"] = get_2048_leaderboard(rank_data)
114
+ if selected_games.get("Candy Crash"):
115
+ game_dfs["Candy Crash"] = get_candy_leaderboard(rank_data)
116
+ if selected_games.get("Tetris (complete)"):
117
+ game_dfs["Tetris (complete)"] = get_tetris_leaderboard(rank_data)
118
+ if selected_games.get("Tetris (planning only)"):
119
+ game_dfs["Tetris (planning only)"] = get_tetris_planning_leaderboard(rank_data)
120
+
121
+ # Get all unique players
122
+ all_players = set()
123
+ for df in game_dfs.values():
124
+ all_players.update(df["Player"].unique())
125
+ all_players = sorted(list(all_players))
126
+
127
+ # Create results DataFrame
128
+ results = []
129
+ for player in all_players:
130
+ player_data = {
131
+ "Player": player,
132
+ "Organization": get_organization(player)
133
+ }
134
+ ranks = []
135
+ games_played = 0
136
+
137
+ # Calculate rank and completeness for each game
138
+ for game in GAME_ORDER:
139
+ if game in game_dfs:
140
+ df = game_dfs[game]
141
+ if player in df["Player"].values:
142
+ games_played += 1
143
+ # Get player's score based on game type
144
+ if game == "Super Mario Bros":
145
+ player_score = df[df["Player"] == player]["Score"].iloc[0]
146
+ rank = len(df[df["Score"] > player_score]) + 1
147
+ elif game == "Sokoban":
148
+ # Parse Sokoban score string and get maximum level
149
+ levels_str = df[df["Player"] == player]["Levels Cracked"].iloc[0]
150
+ try:
151
+ # Split by semicolon, strip whitespace, filter empty strings, convert to integers
152
+ levels = [int(x.strip()) for x in levels_str.split(";") if x.strip()]
153
+ player_score = max(levels) if levels else 0
154
+ except:
155
+ player_score = 0
156
+ # Calculate rank based on maximum level
157
+ rank = len(df[df["Levels Cracked"].apply(
158
+ lambda x: max([int(y.strip()) for y in x.split(";") if y.strip()]) > player_score
159
+ )]) + 1
160
+ elif game == "2048":
161
+ player_score = df[df["Player"] == player]["Score"].iloc[0]
162
+ rank = len(df[df["Score"] > player_score]) + 1
163
+ elif game == "Candy Crash":
164
+ player_score = df[df["Player"] == player]["Average Score"].iloc[0]
165
+ rank = len(df[df["Average Score"] > player_score]) + 1
166
+ elif game == "Tetris (complete)":
167
+ player_score = df[df["Player"] == player]["Score"].iloc[0]
168
+ rank = len(df[df["Score"] > player_score]) + 1
169
+ elif game == "Tetris (planning only)":
170
+ player_score = df[df["Player"] == player]["Score"].iloc[0]
171
+ rank = len(df[df["Score"] > player_score]) + 1
172
+
173
+ ranks.append(rank)
174
+ player_data[f"{game} Score"] = player_score
175
+ else:
176
+ player_data[f"{game} Score"] = "_"
177
+
178
+ # Calculate average rank and completeness for sorting only
179
+ if ranks:
180
+ player_data["Sort Rank"] = round(np.mean(ranks), 2)
181
+ player_data["Games Played"] = games_played
182
+ else:
183
+ player_data["Sort Rank"] = float('inf')
184
+ player_data["Games Played"] = 0
185
+
186
+ results.append(player_data)
187
+
188
+ # Create DataFrame and sort by average rank and completeness
189
+ df_results = pd.DataFrame(results)
190
+ if not df_results.empty:
191
+ # Sort by average rank (ascending) and completeness (descending)
192
+ df_results = df_results.sort_values(
193
+ by=["Sort Rank", "Games Played"],
194
+ ascending=[True, False]
195
+ )
196
+ # Drop the sorting columns
197
+ df_results = df_results.drop(["Sort Rank", "Games Played"], axis=1)
198
+
199
+ return df_results
200
+
201
+ def get_combined_leaderboard(rank_data, selected_games):
202
+ return calculate_rank_and_completeness(rank_data, selected_games)
rank_data_03_25_2025.json ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Super Mario Bros": {
3
+ "runs": 5,
4
+ "results": [
5
+ {
6
+ "model": "claude-3-7-sonnet-20250219",
7
+ "score": 710,
8
+ "progress": "1-1",
9
+ "time_s": 64.2,
10
+ "rank": 1
11
+ },
12
+ {
13
+ "model": "gpt-4o-2024-11-20",
14
+ "score": 560,
15
+ "progress": "1-1",
16
+ "time_s": 58.6,
17
+ "rank": 2
18
+ },
19
+ {
20
+ "model": "gemini-2.0-flash",
21
+ "score": 320,
22
+ "progress": "1-1",
23
+ "time_s": 51.8,
24
+ "rank": 3
25
+ },
26
+ {
27
+ "model": "claude-3-5-haiku-20241022",
28
+ "score": 140,
29
+ "progress": "1-1",
30
+ "time_s": 76.4,
31
+ "rank": 4
32
+ },
33
+ {
34
+ "model": "gpt-4.5-preview-2025-02-27",
35
+ "score": 160,
36
+ "progress": "1-1",
37
+ "time_s": 62.8,
38
+ "rank": 5
39
+ }
40
+ ]
41
+ },
42
+ "2048": {
43
+ "runs": 1,
44
+ "results": [
45
+ {
46
+ "model": "claude-3-7-sonnet-20250219(thinking)",
47
+ "score": 256,
48
+ "steps": 114,
49
+ "time": ">200",
50
+ "rank": 1
51
+ },
52
+ {
53
+ "model": "o1-2024-12-17",
54
+ "score": 256,
55
+ "steps": 116,
56
+ "time": ">200",
57
+ "rank": 2
58
+ },
59
+ {
60
+ "model": "claude-3-7-sonnet-20250219",
61
+ "score": 256,
62
+ "steps": 130,
63
+ "time": "20:36",
64
+ "rank": 3
65
+ },
66
+ {
67
+ "model": "deepseek-v3",
68
+ "score": 256,
69
+ "steps": 216,
70
+ "time": "54.02",
71
+ "rank": 4
72
+ },
73
+ {
74
+ "model": "gemini-2.0-flash",
75
+ "score": 128,
76
+ "steps": 111,
77
+ "time": "18:43",
78
+ "rank": 5
79
+ },
80
+ {
81
+ "model": "gemini-2.0-flash-thinking-exp-1219",
82
+ "score": 128,
83
+ "steps": 132,
84
+ "time": ">100",
85
+ "rank": 6
86
+ },
87
+ {
88
+ "model": "gemini-2.5-pro-exp-03-25",
89
+ "score": 128,
90
+ "steps": 138,
91
+ "time": "169",
92
+ "rank": 7
93
+ },
94
+ {
95
+ "model": "claude-3-5-sonnet-20241022",
96
+ "score": 64,
97
+ "steps": 92,
98
+ "time": "9:2",
99
+ "rank": 9
100
+ },
101
+ {
102
+ "model": "gpt-4.5-preview-2025-02-27",
103
+ "score": 34,
104
+ "steps": 34,
105
+ "time": "8:25",
106
+ "rank": 10
107
+ },
108
+ {
109
+ "model": "gpt-4o-2024-11-20",
110
+ "score": 16,
111
+ "steps": 21,
112
+ "time": "1:17",
113
+ "rank": 11
114
+ },
115
+ {
116
+ "model": "Llama-4-Maverick-17B-128E-Instruct-FP8",
117
+ "score": 128,
118
+ "steps": 145,
119
+ "time": ">100",
120
+ "rank": 8
121
+ }
122
+ ]
123
+ },
124
+ "Tetris (complete)": {
125
+ "runs": 3,
126
+ "results": [
127
+ {
128
+ "model": "claude-3-7-sonnet-20250219",
129
+ "score": 95,
130
+ "steps_blocks": 27,
131
+ "rank": 1
132
+ },
133
+ {
134
+ "model": "claude-3-5-haiku-20241022",
135
+ "score": 90,
136
+ "steps_blocks": 25,
137
+ "rank": 2
138
+ },
139
+ {
140
+ "model": "gemini-2.0-flash",
141
+ "score": 82,
142
+ "steps_blocks": 23,
143
+ "rank": 3
144
+ },
145
+ {
146
+ "model": "gpt-4o-2024-11-20",
147
+ "score": 54,
148
+ "steps_blocks": 19,
149
+ "rank": 4
150
+ }
151
+ ]
152
+ },
153
+ "Tetris (planning only)": {
154
+ "runs": 3,
155
+ "results": [
156
+ {
157
+ "model": "claude-3-7-sonnet-20250219",
158
+ "score": 110,
159
+ "steps_blocks": 29,
160
+ "rank": 1
161
+ },
162
+ {
163
+ "model": "claude-3-5-haiku-20241022",
164
+ "score": 92,
165
+ "steps_blocks": 25,
166
+ "rank": 2
167
+ },
168
+ {
169
+ "model": "gemini-2.0-flash",
170
+ "score": 87,
171
+ "steps_blocks": 24,
172
+ "rank": 3
173
+ },
174
+ {
175
+ "model": "gpt-4o-2024-11-20",
176
+ "score": 56,
177
+ "steps_blocks": 20,
178
+ "rank": 4
179
+ }
180
+ ]
181
+ },
182
+ "Candy Crash": {
183
+ "runs": 3,
184
+ "results": [
185
+ {
186
+ "model": "o3-mini-2025-01-31(medium)",
187
+ "score_runs": "90;109;120",
188
+ "average_score": 106.33,
189
+ "steps": 25,
190
+ "rank": 1
191
+ },
192
+ {
193
+ "model": "o1-2024-12-17",
194
+ "score_runs": "96;114;83",
195
+ "average_score": 97.67,
196
+ "steps": 25,
197
+ "rank": 2
198
+ },
199
+ {
200
+ "model": "deepseek-r1",
201
+ "score_runs": "62;108;105",
202
+ "average_score": 91.67,
203
+ "steps": 25,
204
+ "rank": 3
205
+ },
206
+ {
207
+ "model": "gemini-2.5-pro-exp-03-25",
208
+ "score_runs": "50;36;68",
209
+ "average_score": 51.33,
210
+ "steps": 25,
211
+ "rank": 4
212
+ },
213
+ {
214
+ "model": "claude-3-7-sonnet-20250219(thinking)",
215
+ "score_runs": "36;46;24",
216
+ "average_score": 35.33,
217
+ "steps": 25,
218
+ "rank": 5
219
+ },
220
+ {
221
+ "model": "gemini-2.0-flash-thinking-exp-1219",
222
+ "score_runs": "0;15;39",
223
+ "average_score": 18,
224
+ "steps": 25,
225
+ "rank": 6
226
+ },
227
+ {
228
+ "model": "claude-3-5-sonnet-20241022",
229
+ "score_runs": "3;0;0",
230
+ "average_score": 1,
231
+ "steps": 25,
232
+ "rank": 7
233
+ },
234
+ {
235
+ "model": "deepseek-v3",
236
+ "score_runs": "0;0;0",
237
+ "average_score": 0,
238
+ "steps": 25,
239
+ "rank":9
240
+ },
241
+ {
242
+ "model": "Llama-4-Maverick-17B-128E-Instruct-FP8",
243
+ "score_runs": "6;0;0",
244
+ "average_score": 2,
245
+ "steps": 25,
246
+ "rank": 8
247
+ }
248
+ ]
249
+ },
250
+ "Sokoban": {
251
+ "runs": 3,
252
+ "results": [
253
+ {
254
+ "model": "o3-mini-2025-01-31(medium)",
255
+ "levels_cracked": "2; 3; 2",
256
+ "steps": "[17,52,68];[24,58,78,91];[19,44,64]",
257
+ "rank": 1
258
+ },
259
+ {
260
+ "model": "gemini-2.5-pro-exp-03-25",
261
+ "levels_cracked": "2;2;3",
262
+ "steps": "[23, 46, 79]; [20,50,77]; [26,95,125,175]",
263
+ "rank": 2
264
+ },
265
+ {
266
+ "model": "claude-3-7-sonnet-20250219(thinking)",
267
+ "levels_cracked": "1; 2; 0",
268
+ "steps": "[17,35];[15,40,43];[4]",
269
+ "rank": 3
270
+ },
271
+ {
272
+ "model": "o1-2024-12-17",
273
+ "levels_cracked": "1; 1; 1",
274
+ "steps": null,
275
+ "rank": 4
276
+ },
277
+ {
278
+ "model": "deepseek-r1",
279
+ "levels_cracked": "1; 0; 1",
280
+ "steps": "[19,42];[13];[19,36]",
281
+ "note": "stuck",
282
+ "rank": 5
283
+ },
284
+ {
285
+ "model": "o1-mini-2024-09-12",
286
+ "levels_cracked": "0;1;0",
287
+ "steps": null,
288
+ "rank": 6
289
+ },
290
+ {
291
+ "model": "gemini-2.0-flash-thinking-exp-1219",
292
+ "levels_cracked": "0; 0; 0",
293
+ "steps": "[23]; [14]; [14]",
294
+ "rank": 7
295
+ },
296
+ {
297
+ "model": "gpt-4o-2024-11-20",
298
+ "levels_cracked": "0; 0; 0",
299
+ "steps": "[68];[105];[168]",
300
+ "note": "stuck in a loop",
301
+ "rank": 8
302
+ },
303
+ {
304
+ "model": "claude-3-5-sonnet-20241022",
305
+ "levels_cracked": "0; 0; 0",
306
+ "steps": "[21]; [30]; [51]",
307
+ "note": "stuck in a loop",
308
+ "rank": 9
309
+ },
310
+ {
311
+ "model": "deepseek-v3",
312
+ "levels_cracked": "0; 0; 0",
313
+ "steps": "[9]; [47]; [64]",
314
+ "rank": 10
315
+ },
316
+ {
317
+ "model": "Llama-4-Maverick-17B-128E-Instruct-FP8",
318
+ "levels_cracked": "0;0;0",
319
+ "steps": "[5]",
320
+ "rank": 11
321
+ }
322
+ ]
323
+ }
324
+ }