awacke1 commited on
Commit
2804ca9
·
verified ·
1 Parent(s): 6aaf265

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -170
app.py CHANGED
@@ -10,27 +10,22 @@ 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'],
@@ -38,9 +33,8 @@ def get_name_components():
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'):
@@ -54,166 +48,90 @@ def load_all_players(timestamp):
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 = {
72
- 'x': int(st.query_params.get('x', random.randint(0, GRID_WIDTH - 1))),
73
- 'y': int(st.query_params.get('y', random.randint(0, GRID_HEIGHT - 1)))
74
- }
75
- st.session_state.character_stats = {
76
- 'STR': int(st.query_params.get('STR', 10)),
77
- 'DEX': int(st.query_params.get('DEX', 10)),
78
- 'CON': int(st.query_params.get('CON', 10)),
79
- 'INT': int(st.query_params.get('INT', 10)),
80
- 'WIS': int(st.query_params.get('WIS', 10)),
81
- 'CHA': int(st.query_params.get('CHA', 10)),
82
- 'HP': int(st.query_params.get('HP', 20)),
83
- 'MAX_HP': int(st.query_params.get('MAX_HP', 40)),
84
- 'score': int(st.query_params.get('score', 0)),
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 = {
103
- 'players': {},
104
- 'chat_messages': [],
105
- 'last_sync': time.time()
106
- }
107
- st.session_state.player_name = None
108
- st.session_state.character_stats = None
109
- st.session_state.position = {
110
- 'x': random.randint(0, GRID_WIDTH - 1),
111
- 'y': random.randint(0, GRID_HEIGHT - 1)
112
- }
113
- st.session_state.last_move = time.time()
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,
122
- 'position': st.session_state.position,
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":
143
- st.session_state.position['y'] = (st.session_state.position['y'] + 1) % GRID_HEIGHT
144
- elif direction == "left":
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'])
162
- if distance <= INTERACTION_RADIUS:
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)")
199
- elif player_here:
200
- cols[x].image(images['other_player'], use_column_width=True)
201
- cols[x].markdown(f"**{player_here}** ({all_players[player_here]['stats']['score']} pts)")
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 +150,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,19 +173,14 @@ 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()
 
10
  from io import BytesIO
11
  import numpy as np
12
 
 
13
  GRID_WIDTH = 16
14
  GRID_HEIGHT = 9
15
+ REFRESH_RATE = 10
16
  INTERACTION_RADIUS = 2
17
  POINTS_PER_INTERACTION = 1
18
 
19
+ @st.cache_resource
 
20
  def get_game_images():
 
21
  return {
22
  'tile': Image.new('RGB', (50, 50), color='green'),
23
  'player': Image.new('RGB', (50, 50), color='blue'),
24
  'other_player': Image.new('RGB', (50, 50), color='red')
25
  }
26
 
 
27
  @st.cache_data
28
  def get_name_components():
 
29
  return {
30
  'prefixes': ['Aer', 'Bal', 'Cal', 'Dor', 'El', 'Fae', 'Gor', 'Hel', 'Il', 'Jor',
31
  'Kal', 'Lyr', 'Mel', 'Nym', 'Oro', 'Pyr', 'Qar', 'Ryn', 'Syl', 'Tyr'],
 
33
  'is', 'ax', 'on', 'ir', 'ex', 'az', 'er', 'eth', 'ys', 'ix']
34
  }
35
 
36
+ @st.cache_data(ttl=2)
37
  def load_all_players(timestamp):
 
38
  players = {}
39
  if os.path.exists('players'):
40
  for filename in os.listdir('players'):
 
48
  continue
49
  return {'players': players, 'last_update': time.time()}
50
 
51
+ def create_sync_mechanism():
52
+ sync_js = """
53
+ <script>
54
+ function updateGameState() {
55
+ const timestamp = Date.now();
56
+ window.parent.document.querySelector('.stApp').classList.add('refreshing');
57
+ setTimeout(() => window.location.reload(), 100);
58
+ }
59
+
60
+ function displayCharacterSheet(playerData) {
61
+ const sidebar = window.parent.document.querySelector('[data-testid="stSidebar"]');
62
+ if (sidebar && playerData) {
63
+ const statsDiv = document.createElement('div');
64
+ statsDiv.innerHTML = `
65
+ <div style="padding: 10px; margin: 5px; border: 1px solid #ccc; border-radius: 5px;">
66
+ <h3>${playerData.name}</h3>
67
+ <p>Score: ${playerData.stats.score}</p>
68
+ <p>HP: ${playerData.stats.HP}/${playerData.stats.MAX_HP}</p>
69
+ <p>STR: ${playerData.stats.STR}</p>
70
+ <p>DEX: ${playerData.stats.DEX}</p>
71
+ <p>CON: ${playerData.stats.CON}</p>
72
+ <p>INT: ${playerData.stats.INT}</p>
73
+ <p>WIS: ${playerData.stats.WIS}</p>
74
+ <p>CHA: ${playerData.stats.CHA}</p>
75
+ </div>
76
+ `;
77
+ }
78
+ }
79
+
80
+ function startSync() {
81
+ // Update game state every 10 seconds
82
+ setInterval(updateGameState, 10000);
83
+
84
+ // Handle visibility changes
85
+ document.addEventListener('visibilitychange', function() {
86
+ if (!document.hidden) {
87
+ updateGameState();
88
+ }
89
+ });
90
+
91
+ // Handle focus changes
92
+ window.addEventListener('focus', updateGameState);
93
+ }
94
+
95
+ if (document.readyState === 'complete') {
96
+ startSync();
97
+ } else {
98
+ window.addEventListener('load', startSync);
99
+ }
100
+ </script>
101
+ """
102
+ st.components.v1.html(sync_js, height=0)
103
+
104
+ def show_character_sheet(player_data):
105
+ st.sidebar.markdown("### Character Sheet")
106
+ st.sidebar.markdown(f"""
107
+ **Name:** {player_data['name']}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
+ **Stats:**
110
+ - HP: {player_data['stats']['HP']}/{player_data['stats']['MAX_HP']}
111
+ - STR: {player_data['stats']['STR']}
112
+ - DEX: {player_data['stats']['DEX']}
113
+ - CON: {player_data['stats']['CON']}
114
+ - INT: {player_data['stats']['INT']}
115
+ - WIS: {player_data['stats']['WIS']}
116
+ - CHA: {player_data['stats']['CHA']}
117
 
118
+ **Score:** {player_data['stats']['score']}
119
+ """)
120
 
121
+ [Previous functions remain the same: generate_fantasy_name, load_game_state, save_game_state, calculate_distance, update_position, etc.]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  def main():
124
+ create_sync_mechanism()
125
  update_nearby_players()
126
 
 
127
  st.sidebar.title("Player Info")
128
 
129
+ current_time = time.time()
130
+ all_players = load_all_players(current_time)['players']
131
+
132
  if st.session_state.player_name is None:
133
  default_name = generate_fantasy_name()
134
+ player_name = st.sidebar.text_input("Enter your name or use generated name:", value=default_name)
 
135
  if st.sidebar.button("Start Playing"):
136
  st.session_state.player_name = player_name
137
  if st.session_state.character_stats is None:
 
150
  save_player_state()
151
  st.rerun()
152
  else:
153
+ show_character_sheet({'name': st.session_state.player_name, 'stats': st.session_state.character_stats})
154
+
155
+ st.sidebar.markdown("### Other Players")
156
+ for player_name, player_data in all_players.items():
157
+ if player_name != st.session_state.player_name:
158
+ show_character_sheet(player_data)
159
 
 
160
  st.sidebar.markdown("### Movement Controls")
161
  move_cols = st.sidebar.columns(3)
162
  if move_cols[1].button("⬆️", key="up"):
 
173
  update_position("right")
174
  st.rerun()
175
 
 
176
  if st.sidebar.button("Clear Game State"):
177
  st.query_params.clear()
178
  clear_caches()
179
  st.rerun()
180
 
 
181
  st.title("Multiplayer Tile Game")
 
 
182
  create_game_board()
183
 
 
184
  if (time.time() - st.session_state.last_move) > REFRESH_RATE:
185
  st.session_state.last_move = time.time()
186
  save_player_state()