awacke1 commited on
Commit
0a23ed1
ยท
verified ยท
1 Parent(s): 53bbde1

Update backup3.app.py

Browse files
Files changed (1) hide show
  1. backup3.app.py +138 -72
backup3.app.py CHANGED
@@ -9,38 +9,72 @@ from pathlib import Path
9
  import base64
10
  from io import BytesIO
11
  import numpy as np
 
 
 
 
 
12
 
13
- # Constants
14
  GRID_WIDTH = 16
15
  GRID_HEIGHT = 9
16
  REFRESH_RATE = 5
17
  INTERACTION_RADIUS = 2
18
  POINTS_PER_INTERACTION = 1
19
 
20
- # Resource cache for game resources - using cache_resource for mutable objects
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  @st.cache_resource(show_spinner="Loading game resources...")
22
  def get_game_images():
23
- """Cache game images to improve performance"""
24
  return {
25
  'tile': Image.new('RGB', (50, 50), color='green'),
26
  'player': Image.new('RGB', (50, 50), color='blue'),
27
  'other_player': Image.new('RGB', (50, 50), color='red')
28
  }
29
 
30
- # Data cache for immutable data
31
- @st.cache_data
32
- def get_name_components():
33
- """Cache name generation components"""
34
- return {
35
- 'prefixes': ['Aer', 'Bal', 'Cal', 'Dor', 'El', 'Fae', 'Gor', 'Hel', 'Il', 'Jor',
36
- 'Kal', 'Lyr', 'Mel', 'Nym', 'Oro', 'Pyr', 'Qar', 'Ryn', 'Syl', 'Tyr'],
37
- 'suffixes': ['ian', 'or', 'ion', 'us', 'ix', 'ar', 'en', 'yr', 'el', 'an',
38
- 'is', 'ax', 'on', 'ir', 'ex', 'az', 'er', 'eth', 'ys', 'ix']
39
- }
40
-
41
- @st.cache_data(ttl=5) # Cache for 5 seconds to match refresh rate
42
  def load_all_players(timestamp):
43
- """Cache and load all player states with TTL"""
44
  players = {}
45
  if os.path.exists('players'):
46
  for filename in os.listdir('players'):
@@ -52,20 +86,69 @@ def load_all_players(timestamp):
52
  players[player_data['name']] = player_data
53
  except json.JSONDecodeError:
54
  continue
55
- return {'players': players, 'last_update': time.time()}
56
 
57
  def clear_caches():
58
- """Clear all caches properly"""
59
  st.cache_resource.clear()
60
  st.cache_data.clear()
61
 
62
- def generate_fantasy_name():
63
- """Generate random fantasy name using cached components"""
64
- name_parts = get_name_components()
65
- return random.choice(name_parts['prefixes']) + random.choice(name_parts['suffixes'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
  def load_game_state():
68
- """Load game state from query parameters"""
69
  if 'player_name' in st.query_params:
70
  st.session_state.player_name = st.query_params.player_name
71
  st.session_state.position = {
@@ -85,18 +168,7 @@ def load_game_state():
85
  'created_at': float(st.query_params.get('created_at', time.time()))
86
  }
87
 
88
- def save_game_state():
89
- """Save game state to query parameters"""
90
- if st.session_state.player_name and st.session_state.character_stats:
91
- params = {
92
- 'player_name': st.session_state.player_name,
93
- 'x': str(st.session_state.position['x']),
94
- 'y': str(st.session_state.position['y']),
95
- **{k: str(v) for k, v in st.session_state.character_stats.items()}
96
- }
97
- st.query_params.from_dict(params)
98
-
99
- # Initialize session state
100
  if 'initialized' not in st.session_state:
101
  st.session_state.initialized = False
102
  st.session_state.game_state = {
@@ -114,8 +186,8 @@ if 'initialized' not in st.session_state:
114
  st.session_state.nearby_players = []
115
  load_game_state()
116
 
 
117
  def save_player_state():
118
- """Save player state to JSON file"""
119
  if st.session_state.player_name:
120
  player_data = {
121
  'name': st.session_state.player_name,
@@ -123,20 +195,19 @@ def save_player_state():
123
  'stats': st.session_state.character_stats,
124
  'last_update': time.time()
125
  }
126
-
127
  os.makedirs('players', exist_ok=True)
128
  with open(f"players/{st.session_state.player_name}.json", 'w') as f:
129
  json.dump(player_data, f)
130
  save_game_state()
131
 
 
132
  def calculate_distance(pos1, pos2):
133
- """Calculate Manhattan distance between two positions with wrapping"""
134
  dx = min(abs(pos1['x'] - pos2['x']), GRID_WIDTH - abs(pos1['x'] - pos2['x']))
135
  dy = min(abs(pos1['y'] - pos2['y']), GRID_HEIGHT - abs(pos1['y'] - pos2['y']))
136
  return dx + dy
137
 
 
138
  def update_position(direction):
139
- """Update player position with wrapping"""
140
  if direction == "up":
141
  st.session_state.position['y'] = (st.session_state.position['y'] - 1) % GRID_HEIGHT
142
  elif direction == "down":
@@ -145,17 +216,15 @@ def update_position(direction):
145
  st.session_state.position['x'] = (st.session_state.position['x'] - 1) % GRID_WIDTH
146
  elif direction == "right":
147
  st.session_state.position['x'] = (st.session_state.position['x'] + 1) % GRID_WIDTH
148
-
149
  st.session_state.last_move = time.time()
150
  save_player_state()
151
 
 
152
  def update_nearby_players():
153
- """Update list of nearby players and calculate score gains"""
154
  current_time = time.time()
155
  all_players = load_all_players(current_time)['players']
156
  nearby = []
157
  score_gain = 0
158
-
159
  for player_name, player_data in all_players.items():
160
  if player_name != st.session_state.player_name:
161
  distance = calculate_distance(st.session_state.position, player_data['position'])
@@ -163,36 +232,28 @@ def update_nearby_players():
163
  nearby.append({
164
  'name': player_name,
165
  'distance': distance,
166
- 'score': player_data['stats']['score']
 
167
  })
168
  score_gain += POINTS_PER_INTERACTION
169
-
170
  if score_gain > 0:
171
  st.session_state.character_stats['score'] += score_gain
172
  save_player_state()
173
-
174
  st.session_state.nearby_players = nearby
175
 
 
176
  def create_game_board():
177
- """Create and display the game board"""
178
  current_time = time.time()
179
  all_players = load_all_players(current_time)['players']
180
  images = get_game_images()
181
-
182
- # Create columns for each row
183
  for y in range(GRID_HEIGHT):
184
  cols = st.columns(GRID_WIDTH)
185
  for x in range(GRID_WIDTH):
186
- current_pos = {'x': x, 'y': y}
187
-
188
- # Check if any player is on this tile
189
  player_here = None
190
  for player_name, player_data in all_players.items():
191
  if (player_data['position']['x'] == x and
192
  player_data['position']['y'] == y):
193
  player_here = player_name
194
-
195
- # Display appropriate image and tooltip
196
  if x == st.session_state.position['x'] and y == st.session_state.position['y']:
197
  cols[x].image(images['player'], use_column_width=True)
198
  cols[x].markdown(f"**You** ({st.session_state.character_stats['score']} pts)")
@@ -202,18 +263,25 @@ def create_game_board():
202
  else:
203
  cols[x].image(images['tile'], use_column_width=True)
204
 
 
205
  def main():
206
- # Update nearby players on refresh
207
- update_nearby_players()
 
 
 
 
 
 
 
 
 
208
 
209
- # Sidebar for player info and controls
210
  st.sidebar.title("Player Info")
211
 
212
- # Player name handling
213
  if st.session_state.player_name is None:
214
  default_name = generate_fantasy_name()
215
- player_name = st.sidebar.text_input("Enter your name or use generated name:",
216
- value=default_name)
217
  if st.sidebar.button("Start Playing"):
218
  st.session_state.player_name = player_name
219
  if st.session_state.character_stats is None:
@@ -232,14 +300,13 @@ def main():
232
  save_player_state()
233
  st.rerun()
234
  else:
235
- # Display nearby players
236
  st.sidebar.markdown("### Nearby Players")
237
  for player in st.session_state.nearby_players:
238
  st.sidebar.markdown(
239
- f"**{player['name']}** - {player['distance']} tiles away - {player['score']} pts"
 
240
  )
241
 
242
- # Movement controls
243
  st.sidebar.markdown("### Movement Controls")
244
  move_cols = st.sidebar.columns(3)
245
  if move_cols[1].button("โฌ†๏ธ", key="up"):
@@ -256,23 +323,22 @@ def main():
256
  update_position("right")
257
  st.rerun()
258
 
259
- # Clear game state button
260
  if st.sidebar.button("Clear Game State"):
 
261
  st.query_params.clear()
262
  clear_caches()
263
  st.rerun()
264
 
265
- # Main game area
266
  st.title("Multiplayer Tile Game")
267
-
268
- # Display game board
269
  create_game_board()
270
-
271
- # Auto-refresh logic
272
- if (time.time() - st.session_state.last_move) > REFRESH_RATE:
273
- st.session_state.last_move = time.time()
274
- save_player_state()
275
- st.rerun()
 
 
276
 
277
  if __name__ == "__main__":
278
  main()
 
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
+ # ๐Ÿ•’ Timer Management
26
+ class GameTimer:
27
+ def __init__(self):
28
+ self.running = True
29
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
30
+ self.update_queue = queue.Queue()
31
+ self.thread = threading.Thread(target=self._run_timer, daemon=True)
32
+ self.thread.start()
33
+
34
+ def _run_timer(self):
35
+ self._loop = asyncio.new_event_loop()
36
+ asyncio.set_event_loop(self._loop)
37
+ self._loop.run_until_complete(self._timer_loop())
38
+
39
+ async def _timer_loop(self):
40
+ while self.running:
41
+ try:
42
+ current_time = time.time()
43
+ self.update_queue.put(('update', current_time))
44
+ await asyncio.sleep(REFRESH_RATE)
45
+ except Exception as e:
46
+ print(f"Timer error: {e}")
47
+ await asyncio.sleep(1)
48
+
49
+ def stop(self):
50
+ self.running = False
51
+ if self._loop:
52
+ self._loop.stop()
53
+
54
+ # Initialize timer in session state
55
+ if 'game_timer' not in st.session_state:
56
+ st.session_state.game_timer = GameTimer()
57
+
58
+ # ๐Ÿ“ Name Generation
59
+ def generate_fantasy_name():
60
+ prefixes = ['Aer', 'Bal', 'Cal', 'Dor', 'El', 'Fae', 'Gor', 'Hel', 'Il', 'Jor',
61
+ 'Kal', 'Lyr', 'Mel', 'Nym', 'Oro', 'Pyr', 'Qar', 'Ryn', 'Syl', 'Tyr']
62
+ suffixes = ['ian', 'or', 'ion', 'us', 'ix', 'ar', 'en', 'yr', 'el', 'an',
63
+ 'is', 'ax', 'on', 'ir', 'ex', 'az', 'er', 'eth', 'ys', 'ix']
64
+ return random.choice(prefixes) + random.choice(suffixes)
65
+
66
+ # ๐ŸŽจ Resource Management
67
  @st.cache_resource(show_spinner="Loading game resources...")
68
  def get_game_images():
 
69
  return {
70
  'tile': Image.new('RGB', (50, 50), color='green'),
71
  'player': Image.new('RGB', (50, 50), color='blue'),
72
  'other_player': Image.new('RGB', (50, 50), color='red')
73
  }
74
 
75
+ # ๐Ÿ”„ Player Synchronization
76
+ @st.cache_data(ttl=2)
 
 
 
 
 
 
 
 
 
 
77
  def load_all_players(timestamp):
 
78
  players = {}
79
  if os.path.exists('players'):
80
  for filename in os.listdir('players'):
 
86
  players[player_data['name']] = player_data
87
  except json.JSONDecodeError:
88
  continue
89
+ return {'players': players, 'last_update': timestamp}
90
 
91
  def clear_caches():
 
92
  st.cache_resource.clear()
93
  st.cache_data.clear()
94
 
95
+ # ๐Ÿ”„ Auto-Refresh Implementation
96
+ def create_autorefresh():
97
+ refresh_html = """
98
+ <script>
99
+ let lastUpdate = Date.now();
100
+ let updateInterval = null;
101
+
102
+ function checkForUpdates() {
103
+ const now = Date.now();
104
+ if (now - lastUpdate >= 5000) {
105
+ lastUpdate = now;
106
+ window.parent.document.querySelector('.stApp').classList.add('refreshing');
107
+ window.location.reload();
108
+ }
109
+ }
110
+
111
+ function startAutoRefresh() {
112
+ if (!updateInterval) {
113
+ updateInterval = setInterval(checkForUpdates, 1000);
114
+ }
115
+
116
+ document.addEventListener('visibilitychange', function() {
117
+ if (!document.hidden) {
118
+ lastUpdate = Date.now() - 4900;
119
+ }
120
+ });
121
+ }
122
+
123
+ if (document.readyState === 'complete') {
124
+ startAutoRefresh();
125
+ } else {
126
+ window.addEventListener('load', startAutoRefresh);
127
+ }
128
+
129
+ // Cleanup on unload
130
+ window.addEventListener('unload', function() {
131
+ if (updateInterval) {
132
+ clearInterval(updateInterval);
133
+ }
134
+ });
135
+ </script>
136
+ """
137
+ st.components.v1.html(refresh_html, height=0)
138
+
139
+ # ๐Ÿ’พ State Management
140
+ def save_game_state():
141
+ if st.session_state.player_name and st.session_state.character_stats:
142
+ params = {
143
+ 'player_name': st.session_state.player_name,
144
+ 'x': str(st.session_state.position['x']),
145
+ 'y': str(st.session_state.position['y']),
146
+ **{k: str(v) for k, v in st.session_state.character_stats.items()},
147
+ 'last_sync': str(time.time())
148
+ }
149
+ st.query_params.from_dict(params)
150
 
151
  def load_game_state():
 
152
  if 'player_name' in st.query_params:
153
  st.session_state.player_name = st.query_params.player_name
154
  st.session_state.position = {
 
168
  'created_at': float(st.query_params.get('created_at', time.time()))
169
  }
170
 
171
+ # ๐ŸŽฒ Game State Initialization
 
 
 
 
 
 
 
 
 
 
 
172
  if 'initialized' not in st.session_state:
173
  st.session_state.initialized = False
174
  st.session_state.game_state = {
 
186
  st.session_state.nearby_players = []
187
  load_game_state()
188
 
189
+ # ๐ŸŽฎ Player State Management
190
  def save_player_state():
 
191
  if st.session_state.player_name:
192
  player_data = {
193
  'name': st.session_state.player_name,
 
195
  'stats': st.session_state.character_stats,
196
  'last_update': time.time()
197
  }
 
198
  os.makedirs('players', exist_ok=True)
199
  with open(f"players/{st.session_state.player_name}.json", 'w') as f:
200
  json.dump(player_data, f)
201
  save_game_state()
202
 
203
+ # ๐Ÿ“ Distance Calculation
204
  def calculate_distance(pos1, pos2):
 
205
  dx = min(abs(pos1['x'] - pos2['x']), GRID_WIDTH - abs(pos1['x'] - pos2['x']))
206
  dy = min(abs(pos1['y'] - pos2['y']), GRID_HEIGHT - abs(pos1['y'] - pos2['y']))
207
  return dx + dy
208
 
209
+ # ๐Ÿƒโ€โ™‚๏ธ Movement System
210
  def update_position(direction):
 
211
  if direction == "up":
212
  st.session_state.position['y'] = (st.session_state.position['y'] - 1) % GRID_HEIGHT
213
  elif direction == "down":
 
216
  st.session_state.position['x'] = (st.session_state.position['x'] - 1) % GRID_WIDTH
217
  elif direction == "right":
218
  st.session_state.position['x'] = (st.session_state.position['x'] + 1) % GRID_WIDTH
 
219
  st.session_state.last_move = time.time()
220
  save_player_state()
221
 
222
+ # ๐Ÿ‘ฅ Nearby Players Update
223
  def update_nearby_players():
 
224
  current_time = time.time()
225
  all_players = load_all_players(current_time)['players']
226
  nearby = []
227
  score_gain = 0
 
228
  for player_name, player_data in all_players.items():
229
  if player_name != st.session_state.player_name:
230
  distance = calculate_distance(st.session_state.position, player_data['position'])
 
232
  nearby.append({
233
  'name': player_name,
234
  'distance': distance,
235
+ 'score': player_data['stats']['score'],
236
+ 'last_seen': current_time - player_data['last_update']
237
  })
238
  score_gain += POINTS_PER_INTERACTION
 
239
  if score_gain > 0:
240
  st.session_state.character_stats['score'] += score_gain
241
  save_player_state()
 
242
  st.session_state.nearby_players = nearby
243
 
244
+ # ๐ŸŽจ Game Board Creation
245
  def create_game_board():
 
246
  current_time = time.time()
247
  all_players = load_all_players(current_time)['players']
248
  images = get_game_images()
 
 
249
  for y in range(GRID_HEIGHT):
250
  cols = st.columns(GRID_WIDTH)
251
  for x in range(GRID_WIDTH):
 
 
 
252
  player_here = None
253
  for player_name, player_data in all_players.items():
254
  if (player_data['position']['x'] == x and
255
  player_data['position']['y'] == y):
256
  player_here = player_name
 
 
257
  if x == st.session_state.position['x'] and y == st.session_state.position['y']:
258
  cols[x].image(images['player'], use_column_width=True)
259
  cols[x].markdown(f"**You** ({st.session_state.character_stats['score']} pts)")
 
263
  else:
264
  cols[x].image(images['tile'], use_column_width=True)
265
 
266
+ # ๐ŸŽฎ Main Game Loop
267
  def main():
268
+ create_autorefresh()
269
+
270
+ # Process any queued updates
271
+ try:
272
+ while not st.session_state.game_timer.update_queue.empty():
273
+ update_type, timestamp = st.session_state.game_timer.update_queue.get_nowait()
274
+ if update_type == 'update':
275
+ update_nearby_players()
276
+ save_player_state()
277
+ except queue.Empty:
278
+ pass
279
 
 
280
  st.sidebar.title("Player Info")
281
 
 
282
  if st.session_state.player_name is None:
283
  default_name = generate_fantasy_name()
284
+ player_name = st.sidebar.text_input("Enter your name or use generated name:", value=default_name)
 
285
  if st.sidebar.button("Start Playing"):
286
  st.session_state.player_name = player_name
287
  if st.session_state.character_stats is None:
 
300
  save_player_state()
301
  st.rerun()
302
  else:
 
303
  st.sidebar.markdown("### Nearby Players")
304
  for player in st.session_state.nearby_players:
305
  st.sidebar.markdown(
306
+ f"**{player['name']}** - {player['distance']} tiles away - {player['score']} pts\n"
307
+ f"Last seen: {player['last_seen']:.1f}s ago"
308
  )
309
 
 
310
  st.sidebar.markdown("### Movement Controls")
311
  move_cols = st.sidebar.columns(3)
312
  if move_cols[1].button("โฌ†๏ธ", key="up"):
 
323
  update_position("right")
324
  st.rerun()
325
 
 
326
  if st.sidebar.button("Clear Game State"):
327
+ st.session_state.game_timer.stop()
328
  st.query_params.clear()
329
  clear_caches()
330
  st.rerun()
331
 
 
332
  st.title("Multiplayer Tile Game")
 
 
333
  create_game_board()
334
+
335
+ # ๐Ÿงน Cleanup on exit
336
+ def cleanup():
337
+ if 'game_timer' in st.session_state:
338
+ st.session_state.game_timer.stop()
339
+
340
+ # Register cleanup
341
+ atexit.register(cleanup)
342
 
343
  if __name__ == "__main__":
344
  main()