awacke1 commited on
Commit
f8184de
·
verified ·
1 Parent(s): 3f8359b

Create backup4.app.py

Browse files
Files changed (1) hide show
  1. backup4.app.py +490 -0
backup4.app.py ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import random
4
+ import time
5
+ 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
15
+ import queue
16
+ import atexit
17
+
18
+ # 🎮 Game Constants
19
+ GRID_WIDTH = 16
20
+ GRID_HEIGHT = 9
21
+ 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):
31
+ self.running = True
32
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
33
+ self.update_queue = queue.Queue()
34
+ self.thread = threading.Thread(target=self._run_timer, daemon=True)
35
+ self.thread.start()
36
+
37
+ def _run_timer(self):
38
+ self._loop = asyncio.new_event_loop()
39
+ asyncio.set_event_loop(self._loop)
40
+ self._loop.run_until_complete(self._timer_loop())
41
+
42
+ async def _timer_loop(self):
43
+ while self.running:
44
+ try:
45
+ current_time = time.time()
46
+ self.update_queue.put(('update', current_time))
47
+ await asyncio.sleep(REFRESH_RATE)
48
+ except Exception as e:
49
+ print(f"Timer error: {e}")
50
+ await asyncio.sleep(1)
51
+
52
+ def stop(self):
53
+ self.running = False
54
+ if self._loop:
55
+ self._loop.stop()
56
+
57
+
58
+
59
+ # First define the load_game_state function
60
+ def load_game_state():
61
+ if 'player_name' in st.query_params:
62
+ st.session_state.player_name = st.query_params.player_name
63
+ st.session_state.position = {
64
+ 'x': int(st.query_params.get('x', random.randint(0, GRID_WIDTH - 1))),
65
+ 'y': int(st.query_params.get('y', random.randint(0, GRID_HEIGHT - 1)))
66
+ }
67
+ st.session_state.character_stats = {
68
+ 'STR': int(st.query_params.get('STR', 10)),
69
+ 'DEX': int(st.query_params.get('DEX', 10)),
70
+ 'CON': int(st.query_params.get('CON', 10)),
71
+ 'INT': int(st.query_params.get('INT', 10)),
72
+ 'WIS': int(st.query_params.get('WIS', 10)),
73
+ 'CHA': int(st.query_params.get('CHA', 10)),
74
+ 'HP': int(st.query_params.get('HP', 20)),
75
+ 'MAX_HP': int(st.query_params.get('MAX_HP', 40)),
76
+ 'score': int(st.query_params.get('score', 0)),
77
+ 'created_at': float(st.query_params.get('created_at', time.time()))
78
+ }
79
+
80
+ # Then initialize session state
81
+ if 'initialized' not in st.session_state:
82
+ st.session_state.initialized = False
83
+ st.session_state.game_state = {
84
+ 'players': {},
85
+ 'chat_messages': [],
86
+ 'last_sync': time.time()
87
+ }
88
+ st.session_state.player_name = None
89
+ st.session_state.character_stats = {
90
+ 'STR': 10,
91
+ 'DEX': 10,
92
+ 'CON': 10,
93
+ 'INT': 10,
94
+ 'WIS': 10,
95
+ 'CHA': 10,
96
+ 'HP': 20,
97
+ 'MAX_HP': 40,
98
+ 'score': 0,
99
+ 'created_at': time.time()
100
+ }
101
+ st.session_state.position = {
102
+ 'x': random.randint(0, GRID_WIDTH - 1),
103
+ 'y': random.randint(0, GRID_HEIGHT - 1)
104
+ }
105
+ st.session_state.last_move = time.time()
106
+ st.session_state.nearby_players = []
107
+
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
273
+ st.session_state.game_state = {
274
+ 'players': {},
275
+ 'chat_messages': [],
276
+ 'last_sync': time.time()
277
+ }
278
+ st.session_state.player_name = None
279
+ st.session_state.character_stats = None
280
+ st.session_state.position = {
281
+ 'x': random.randint(0, GRID_WIDTH - 1),
282
+ 'y': random.randint(0, GRID_HEIGHT - 1)
283
+ }
284
+ st.session_state.last_move = time.time()
285
+ st.session_state.nearby_players = []
286
+ load_game_state()
287
+
288
+ # 🎮 Player State Management
289
+ def save_player_state():
290
+ if st.session_state.player_name:
291
+ player_data = {
292
+ 'name': st.session_state.player_name,
293
+ 'position': st.session_state.position,
294
+ 'stats': st.session_state.character_stats,
295
+ 'last_update': time.time()
296
+ }
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):
304
+ dx = min(abs(pos1['x'] - pos2['x']), GRID_WIDTH - abs(pos1['x'] - pos2['x']))
305
+ dy = min(abs(pos1['y'] - pos2['y']), GRID_HEIGHT - abs(pos1['y'] - pos2['y']))
306
+ return dx + dy
307
+
308
+ # 🏃‍♂️ Movement System
309
+ def update_position(direction):
310
+ if direction == "up":
311
+ st.session_state.position['y'] = (st.session_state.position['y'] - 1) % GRID_HEIGHT
312
+ elif direction == "down":
313
+ st.session_state.position['y'] = (st.session_state.position['y'] + 1) % GRID_HEIGHT
314
+ elif direction == "left":
315
+ st.session_state.position['x'] = (st.session_state.position['x'] - 1) % GRID_WIDTH
316
+ elif direction == "right":
317
+ st.session_state.position['x'] = (st.session_state.position['x'] + 1) % GRID_WIDTH
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:
403
+ default_name = generate_fantasy_name()
404
+ player_name = st.sidebar.text_input("Enter your name or use generated name:", value=default_name)
405
+ if st.sidebar.button("Start Playing"):
406
+ st.session_state.player_name = player_name
407
+ if st.session_state.character_stats is None:
408
+ st.session_state.character_stats = {
409
+ 'STR': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
410
+ 'DEX': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
411
+ 'CON': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
412
+ 'INT': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
413
+ 'WIS': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
414
+ 'CHA': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
415
+ 'HP': random.randint(1, 20) * 2 + random.randint(1, 20),
416
+ 'MAX_HP': 40,
417
+ 'score': 0,
418
+ 'created_at': time.time()
419
+ }
420
+ save_player_state()
421
+ st.rerun()
422
+ else:
423
+ st.sidebar.markdown("### Nearby Players")
424
+ for player in st.session_state.nearby_players:
425
+ st.sidebar.markdown(
426
+ f"**{player['name']}** - {player['distance']} tiles away - {player['score']} pts\n"
427
+ f"Last seen: {player['last_seen']:.1f}s ago"
428
+ )
429
+
430
+ st.sidebar.markdown("### Movement Controls")
431
+ move_cols = st.sidebar.columns(3)
432
+ if move_cols[1].button("⬆️", key="up"):
433
+ update_position("up")
434
+ st.rerun()
435
+ cols = st.sidebar.columns(3)
436
+ if cols[0].button("⬅️", key="left"):
437
+ update_position("left")
438
+ st.rerun()
439
+ if cols[1].button("⬇️", key="down"):
440
+ update_position("down")
441
+ st.rerun()
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:
480
+ st.session_state.game_timer.stop()
481
+
482
+ # Register cleanup
483
+ atexit.register(cleanup)
484
+
485
+
486
+
487
+
488
+
489
+ if __name__ == "__main__":
490
+ main()