awacke1 commited on
Commit
05e80fd
ยท
verified ยท
1 Parent(s): 27d644c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +257 -41
app.py CHANGED
@@ -6,6 +6,9 @@ from PIL import Image
6
  import json
7
  from datetime import datetime
8
  from pathlib import Path
 
 
 
9
  import asyncio
10
  import threading
11
  from typing import Optional
@@ -19,6 +22,9 @@ REFRESH_RATE = 5
19
  INTERACTION_RADIUS = 2
20
  POINTS_PER_INTERACTION = 1
21
 
 
 
 
22
  # ๐Ÿ•’ Timer Management
23
  class GameTimer:
24
  def __init__(self):
@@ -48,36 +54,7 @@ class GameTimer:
48
  if self._loop:
49
  self._loop.stop()
50
 
51
- # ๐ŸŽจ Resource Management: Load and cache images, assign randomly but consistently for each (x, y)
52
- @st.cache_resource(show_spinner="Loading game resources...")
53
- def get_game_images():
54
- image_folder = Path('game_images') # Directory containing the .png images
55
- images = []
56
- for file in image_folder.glob('*.png'):
57
- images.append(Image.open(file))
58
- return images
59
-
60
- def initialize_map():
61
- # Path to the cached map configuration
62
- map_path = Path('map.json')
63
-
64
- # Load or initialize the map with consistent image selections for each grid position
65
- if map_path.exists():
66
- with open(map_path, 'r') as f:
67
- map_layout = json.load(f)
68
- else:
69
- # If the map doesn't exist, create a random mapping and save it
70
- map_layout = {}
71
- for y in range(GRID_HEIGHT):
72
- for x in range(GRID_WIDTH):
73
- map_layout[f"{x},{y}"] = random.randint(0, len(get_game_images()) - 1)
74
- with open(map_path, 'w') as f:
75
- json.dump(map_layout, f)
76
-
77
- return map_layout
78
 
79
- # Cache the map layout for consistent use across sessions
80
- map_layout = initialize_map()
81
 
82
  # First define the load_game_state function
83
  def load_game_state():
@@ -131,6 +108,165 @@ if 'initialized' not in st.session_state:
131
  # Now call load_game_state after it's been defined
132
  load_game_state()
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  # ๐ŸŽฒ Game State Initialization
135
  if 'initialized' not in st.session_state:
136
  st.session_state.initialized = False
@@ -161,6 +297,7 @@ def save_player_state():
161
  os.makedirs('players', exist_ok=True)
162
  with open(f"players/{st.session_state.player_name}.json", 'w') as f:
163
  json.dump(player_data, f)
 
164
 
165
  # ๐Ÿ“ Distance Calculation
166
  def calculate_distance(pos1, pos2):
@@ -181,38 +318,85 @@ def update_position(direction):
181
  st.session_state.last_move = time.time()
182
  save_player_state()
183
 
184
- # Modify the create_game_board function to use the map_layout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  def create_game_board():
186
  current_time = time.time()
187
  all_players = load_all_players(current_time)['players']
188
  images = get_game_images()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  for y in range(GRID_HEIGHT):
191
  cols = st.columns(GRID_WIDTH)
192
  for x in range(GRID_WIDTH):
193
  player_here = None
194
  for player_name, player_data in all_players.items():
195
- if player_data['position']['x'] == x and player_data['position']['y'] == y:
 
196
  player_here = player_name
197
-
198
- # Display the player or other players using the preloaded images
199
  if x == st.session_state.position['x'] and y == st.session_state.position['y']:
200
- cols[x].image(images[map_layout[f"{x},{y}"]], use_column_width=True)
 
201
  score = st.session_state.character_stats.get('score', 0)
202
  cols[x].markdown(f"**You** ({score} pts)")
203
  elif player_here:
204
- cols[x].image(images[map_layout[f"{x},{y}"]], use_column_width=True)
205
  player_score = all_players[player_here]['stats'].get('score', 0)
206
  cols[x].markdown(f"**{player_here}** ({player_score} pts)")
207
  else:
208
- cols[x].image(images[map_layout[f"{x},{y}"]], use_column_width=True)
209
 
210
  # ๐ŸŽฎ Main Game Loop
211
  def main():
212
- # Initialize timer in session state
213
- if 'game_timer' not in st.session_state:
214
- st.session_state.game_timer = GameTimer()
215
-
 
 
 
 
 
 
 
 
216
  st.sidebar.title("Player Info")
217
 
218
  if st.session_state.player_name is None:
@@ -258,10 +442,38 @@ def main():
258
  if cols[2].button("โžก๏ธ", key="right"):
259
  update_position("right")
260
  st.rerun()
 
 
 
 
 
 
261
 
262
  st.title("Multiplayer Tile Game")
263
  create_game_board()
264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  # ๐Ÿงน Cleanup on exit
266
  def cleanup():
267
  if 'game_timer' in st.session_state:
@@ -270,5 +482,9 @@ def cleanup():
270
  # Register cleanup
271
  atexit.register(cleanup)
272
 
 
 
 
 
273
  if __name__ == "__main__":
274
- main()
 
6
  import json
7
  from datetime import datetime
8
  from pathlib import Path
9
+ import base64
10
+ from io import BytesIO
11
+ import numpy as np
12
  import asyncio
13
  import threading
14
  from typing import Optional
 
22
  INTERACTION_RADIUS = 2
23
  POINTS_PER_INTERACTION = 1
24
 
25
+
26
+ from streamlit.components.v1 import html
27
+
28
  # ๐Ÿ•’ Timer Management
29
  class GameTimer:
30
  def __init__(self):
 
54
  if self._loop:
55
  self._loop.stop()
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
 
 
58
 
59
  # First define the load_game_state function
60
  def load_game_state():
 
108
  # Now call load_game_state after it's been defined
109
  load_game_state()
110
 
111
+ # Make sure all other functions that use load_game_state are defined after this point
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+ # Initialize timer in session state
124
+ if 'game_timer' not in st.session_state:
125
+ st.session_state.game_timer = GameTimer()
126
+
127
+ # Also modify the initialization section to ensure character_stats is properly initialized
128
+ if 'initialized' not in st.session_state:
129
+ st.session_state.initialized = False
130
+ st.session_state.game_state = {
131
+ 'players': {},
132
+ 'chat_messages': [],
133
+ 'last_sync': time.time()
134
+ }
135
+ st.session_state.player_name = None
136
+ st.session_state.character_stats = {
137
+ 'STR': 10,
138
+ 'DEX': 10,
139
+ 'CON': 10,
140
+ 'INT': 10,
141
+ 'WIS': 10,
142
+ 'CHA': 10,
143
+ 'HP': 20,
144
+ 'MAX_HP': 40,
145
+ 'score': 0,
146
+ 'created_at': time.time()
147
+ }
148
+ st.session_state.position = {
149
+ 'x': random.randint(0, GRID_WIDTH - 1),
150
+ 'y': random.randint(0, GRID_HEIGHT - 1)
151
+ }
152
+ st.session_state.last_move = time.time()
153
+ st.session_state.nearby_players = []
154
+ load_game_state()
155
+
156
+
157
+ # ๐Ÿ“ Name Generation
158
+ def generate_fantasy_name():
159
+ prefixes = ['Aer', 'Bal', 'Cal', 'Dor', 'El', 'Fae', 'Gor', 'Hel', 'Il', 'Jor',
160
+ 'Kal', 'Lyr', 'Mel', 'Nym', 'Oro', 'Pyr', 'Qar', 'Ryn', 'Syl', 'Tyr']
161
+ suffixes = ['ian', 'or', 'ion', 'us', 'ix', 'ar', 'en', 'yr', 'el', 'an',
162
+ 'is', 'ax', 'on', 'ir', 'ex', 'az', 'er', 'eth', 'ys', 'ix']
163
+ return random.choice(prefixes) + random.choice(suffixes)
164
+
165
+ # ๐ŸŽจ Resource Management
166
+ @st.cache_resource(show_spinner="Loading game resources...")
167
+ def get_game_images():
168
+ return {
169
+ 'tile': Image.new('RGB', (50, 50), color='green'),
170
+ 'player': Image.new('RGB', (50, 50), color='blue'),
171
+ 'other_player': Image.new('RGB', (50, 50), color='red')
172
+ }
173
+
174
+ # ๐Ÿ”„ Player Synchronization
175
+ @st.cache_data(ttl=2)
176
+ def load_all_players(timestamp):
177
+ players = {}
178
+ if os.path.exists('players'):
179
+ for filename in os.listdir('players'):
180
+ if filename.endswith('.json'):
181
+ with open(f"players/{filename}", 'r') as f:
182
+ try:
183
+ player_data = json.load(f)
184
+ if time.time() - player_data['last_update'] < 60:
185
+ players[player_data['name']] = player_data
186
+ except json.JSONDecodeError:
187
+ continue
188
+ return {'players': players, 'last_update': timestamp}
189
+
190
+ def clear_caches():
191
+ st.cache_resource.clear()
192
+ st.cache_data.clear()
193
+
194
+ # ๐Ÿ”„ Auto-Refresh Implementation
195
+ def create_autorefresh():
196
+ refresh_html = """
197
+ <script>
198
+ let lastUpdate = Date.now();
199
+ let updateInterval = null;
200
+
201
+ function checkForUpdates() {
202
+ const now = Date.now();
203
+ if (now - lastUpdate >= 5000) {
204
+ lastUpdate = now;
205
+ window.parent.document.querySelector('.stApp').classList.add('refreshing');
206
+ window.location.reload();
207
+ }
208
+ }
209
+
210
+ function startAutoRefresh() {
211
+ if (!updateInterval) {
212
+ updateInterval = setInterval(checkForUpdates, 1000);
213
+ }
214
+
215
+ document.addEventListener('visibilitychange', function() {
216
+ if (!document.hidden) {
217
+ lastUpdate = Date.now() - 4900;
218
+ }
219
+ });
220
+ }
221
+
222
+ if (document.readyState === 'complete') {
223
+ startAutoRefresh();
224
+ } else {
225
+ window.addEventListener('load', startAutoRefresh);
226
+ }
227
+
228
+ // Cleanup on unload
229
+ window.addEventListener('unload', function() {
230
+ if (updateInterval) {
231
+ clearInterval(updateInterval);
232
+ }
233
+ });
234
+ </script>
235
+ """
236
+ st.components.v1.html(refresh_html, height=0)
237
+
238
+ # ๐Ÿ’พ State Management
239
+ def save_game_state():
240
+ if st.session_state.player_name and st.session_state.character_stats:
241
+ params = {
242
+ 'player_name': st.session_state.player_name,
243
+ 'x': str(st.session_state.position['x']),
244
+ 'y': str(st.session_state.position['y']),
245
+ **{k: str(v) for k, v in st.session_state.character_stats.items()},
246
+ 'last_sync': str(time.time())
247
+ }
248
+ st.query_params.from_dict(params)
249
+
250
+ def load_game_state():
251
+ if 'player_name' in st.query_params:
252
+ st.session_state.player_name = st.query_params.player_name
253
+ st.session_state.position = {
254
+ 'x': int(st.query_params.get('x', random.randint(0, GRID_WIDTH - 1))),
255
+ 'y': int(st.query_params.get('y', random.randint(0, GRID_HEIGHT - 1)))
256
+ }
257
+ st.session_state.character_stats = {
258
+ 'STR': int(st.query_params.get('STR', 10)),
259
+ 'DEX': int(st.query_params.get('DEX', 10)),
260
+ 'CON': int(st.query_params.get('CON', 10)),
261
+ 'INT': int(st.query_params.get('INT', 10)),
262
+ 'WIS': int(st.query_params.get('WIS', 10)),
263
+ 'CHA': int(st.query_params.get('CHA', 10)),
264
+ 'HP': int(st.query_params.get('HP', 20)),
265
+ 'MAX_HP': int(st.query_params.get('MAX_HP', 40)),
266
+ 'score': int(st.query_params.get('score', 0)),
267
+ 'created_at': float(st.query_params.get('created_at', time.time()))
268
+ }
269
+
270
  # ๐ŸŽฒ Game State Initialization
271
  if 'initialized' not in st.session_state:
272
  st.session_state.initialized = False
 
297
  os.makedirs('players', exist_ok=True)
298
  with open(f"players/{st.session_state.player_name}.json", 'w') as f:
299
  json.dump(player_data, f)
300
+ save_game_state()
301
 
302
  # ๐Ÿ“ Distance Calculation
303
  def calculate_distance(pos1, pos2):
 
318
  st.session_state.last_move = time.time()
319
  save_player_state()
320
 
321
+ # ๐Ÿ‘ฅ Nearby Players Update
322
+ def update_nearby_players():
323
+ current_time = time.time()
324
+ all_players = load_all_players(current_time)['players']
325
+ nearby = []
326
+ score_gain = 0
327
+ for player_name, player_data in all_players.items():
328
+ if player_name != st.session_state.player_name:
329
+ distance = calculate_distance(st.session_state.position, player_data['position'])
330
+ if distance <= INTERACTION_RADIUS:
331
+ nearby.append({
332
+ 'name': player_name,
333
+ 'distance': distance,
334
+ 'score': player_data['stats']['score'],
335
+ 'last_seen': current_time - player_data['last_update']
336
+ })
337
+ score_gain += POINTS_PER_INTERACTION
338
+ if score_gain > 0:
339
+ st.session_state.character_stats['score'] += score_gain
340
+ save_player_state()
341
+ st.session_state.nearby_players = nearby
342
+
343
+ # Modify the create_game_board function to add safety checks
344
  def create_game_board():
345
  current_time = time.time()
346
  all_players = load_all_players(current_time)['players']
347
  images = get_game_images()
348
+
349
+ # Add safety check for character_stats
350
+ if not st.session_state.character_stats:
351
+ st.session_state.character_stats = {
352
+ 'STR': 10,
353
+ 'DEX': 10,
354
+ 'CON': 10,
355
+ 'INT': 10,
356
+ 'WIS': 10,
357
+ 'CHA': 10,
358
+ 'HP': 20,
359
+ 'MAX_HP': 40,
360
+ 'score': 0,
361
+ 'created_at': time.time()
362
+ }
363
+ save_player_state()
364
 
365
  for y in range(GRID_HEIGHT):
366
  cols = st.columns(GRID_WIDTH)
367
  for x in range(GRID_WIDTH):
368
  player_here = None
369
  for player_name, player_data in all_players.items():
370
+ if (player_data['position']['x'] == x and
371
+ player_data['position']['y'] == y):
372
  player_here = player_name
373
+
 
374
  if x == st.session_state.position['x'] and y == st.session_state.position['y']:
375
+ cols[x].image(images['player'], use_column_width=True)
376
+ # Add safety check when accessing score
377
  score = st.session_state.character_stats.get('score', 0)
378
  cols[x].markdown(f"**You** ({score} pts)")
379
  elif player_here:
380
+ cols[x].image(images['other_player'], use_column_width=True)
381
  player_score = all_players[player_here]['stats'].get('score', 0)
382
  cols[x].markdown(f"**{player_here}** ({player_score} pts)")
383
  else:
384
+ cols[x].image(images['tile'], use_column_width=True)
385
 
386
  # ๐ŸŽฎ Main Game Loop
387
  def main():
388
+ create_autorefresh()
389
+
390
+ # Process any queued updates
391
+ try:
392
+ while not st.session_state.game_timer.update_queue.empty():
393
+ update_type, timestamp = st.session_state.game_timer.update_queue.get_nowait()
394
+ if update_type == 'update':
395
+ update_nearby_players()
396
+ save_player_state()
397
+ except queue.Empty:
398
+ pass
399
+
400
  st.sidebar.title("Player Info")
401
 
402
  if st.session_state.player_name is None:
 
442
  if cols[2].button("โžก๏ธ", key="right"):
443
  update_position("right")
444
  st.rerun()
445
+
446
+ if st.sidebar.button("Clear Game State"):
447
+ st.session_state.game_timer.stop()
448
+ st.query_params.clear()
449
+ clear_caches()
450
+ st.rerun()
451
 
452
  st.title("Multiplayer Tile Game")
453
  create_game_board()
454
 
455
+
456
+
457
+ # Add this in your main() function, right after create_autorefresh()
458
+ # Add the time display component
459
+ st.markdown("""
460
+ <div id="time-display-root"></div>
461
+ """, unsafe_allow_html=True)
462
+
463
+ # Mount the React component
464
+ html("""
465
+ <div id="time-display-mount"></div>
466
+ <script>
467
+ const root = document.getElementById('time-display-root');
468
+ const mount = document.getElementById('time-display-mount');
469
+ if (root && mount) {
470
+ root.appendChild(mount);
471
+ }
472
+ </script>
473
+ """, height=200)
474
+
475
+
476
+
477
  # ๐Ÿงน Cleanup on exit
478
  def cleanup():
479
  if 'game_timer' in st.session_state:
 
482
  # Register cleanup
483
  atexit.register(cleanup)
484
 
485
+
486
+
487
+
488
+
489
  if __name__ == "__main__":
490
+ main()