awacke1 commited on
Commit
27d644c
·
verified ·
1 Parent(s): 3f0a39d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -257
app.py CHANGED
@@ -6,9 +6,6 @@ from PIL import Image
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,9 +19,6 @@ REFRESH_RATE = 5
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,7 +48,36 @@ class GameTimer:
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,165 +131,6 @@ if 'initialized' not in st.session_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,7 +161,6 @@ def save_player_state():
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,85 +181,38 @@ def update_position(direction):
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,38 +258,10 @@ def main():
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,9 +270,5 @@ def cleanup():
482
  # Register cleanup
483
  atexit.register(cleanup)
484
 
485
-
486
-
487
-
488
-
489
  if __name__ == "__main__":
490
- main()
 
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
  INTERACTION_RADIUS = 2
20
  POINTS_PER_INTERACTION = 1
21
 
 
 
 
22
  # 🕒 Timer Management
23
  class GameTimer:
24
  def __init__(self):
 
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
  # 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
  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
  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
  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
  # Register cleanup
271
  atexit.register(cleanup)
272
 
 
 
 
 
273
  if __name__ == "__main__":
274
+ main()