awacke1 commited on
Commit
e04b366
·
verified ·
1 Parent(s): 6ff178a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +263 -0
app.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
13
+ GRID_WIDTH = 16
14
+ GRID_HEIGHT = 9
15
+ REFRESH_RATE = 5
16
+ INTERACTION_RADIUS = 2
17
+ POINTS_PER_INTERACTION = 1
18
+
19
+ @st.cache_resource(show_spinner="Loading game resources...")
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'],
32
+ 'suffixes': ['ian', 'or', 'ion', 'us', 'ix', 'ar', 'en', 'yr', 'el', 'an',
33
+ 'is', 'ax', 'on', 'ir', 'ex', 'az', 'er', 'eth', 'ys', 'ix']
34
+ }
35
+
36
+ @st.cache_data(ttl=5)
37
+ def load_all_players(timestamp):
38
+ players = {}
39
+ if os.path.exists('players'):
40
+ for filename in os.listdir('players'):
41
+ if filename.endswith('.json'):
42
+ with open(f"players/{filename}", 'r') as f:
43
+ try:
44
+ player_data = json.load(f)
45
+ if time.time() - player_data['last_update'] < 60:
46
+ players[player_data['name']] = player_data
47
+ except json.JSONDecodeError:
48
+ continue
49
+ return {'players': players, 'last_update': time.time()}
50
+
51
+ def clear_caches():
52
+ st.cache_resource.clear()
53
+ st.cache_data.clear()
54
+
55
+ def generate_fantasy_name():
56
+ name_parts = get_name_components()
57
+ return random.choice(name_parts['prefixes']) + random.choice(name_parts['suffixes'])
58
+
59
+ def load_game_state():
60
+ if 'player_name' in st.query_params:
61
+ st.session_state.player_name = st.query_params.player_name
62
+ st.session_state.position = {
63
+ 'x': int(st.query_params.get('x', random.randint(0, GRID_WIDTH - 1))),
64
+ 'y': int(st.query_params.get('y', random.randint(0, GRID_HEIGHT - 1)))
65
+ }
66
+ st.session_state.character_stats = {
67
+ 'STR': int(st.query_params.get('STR', 10)),
68
+ 'DEX': int(st.query_params.get('DEX', 10)),
69
+ 'CON': int(st.query_params.get('CON', 10)),
70
+ 'INT': int(st.query_params.get('INT', 10)),
71
+ 'WIS': int(st.query_params.get('WIS', 10)),
72
+ 'CHA': int(st.query_params.get('CHA', 10)),
73
+ 'HP': int(st.query_params.get('HP', 20)),
74
+ 'MAX_HP': int(st.query_params.get('MAX_HP', 40)),
75
+ 'score': int(st.query_params.get('score', 0)),
76
+ 'created_at': float(st.query_params.get('created_at', time.time()))
77
+ }
78
+
79
+ def save_game_state():
80
+ if st.session_state.player_name and st.session_state.character_stats:
81
+ params = {
82
+ 'player_name': st.session_state.player_name,
83
+ 'x': str(st.session_state.position['x']),
84
+ 'y': str(st.session_state.position['y']),
85
+ **{k: str(v) for k, v in st.session_state.character_stats.items()}
86
+ }
87
+ st.query_params.from_dict(params)
88
+
89
+ if 'initialized' not in st.session_state:
90
+ st.session_state.initialized = False
91
+ st.session_state.game_state = {
92
+ 'players': {},
93
+ 'chat_messages': [],
94
+ 'last_sync': time.time()
95
+ }
96
+ st.session_state.player_name = None
97
+ st.session_state.character_stats = None
98
+ st.session_state.position = {
99
+ 'x': random.randint(0, GRID_WIDTH - 1),
100
+ 'y': random.randint(0, GRID_HEIGHT - 1)
101
+ }
102
+ st.session_state.last_move = time.time()
103
+ st.session_state.nearby_players = []
104
+ load_game_state()
105
+
106
+ def save_player_state():
107
+ if st.session_state.player_name:
108
+ player_data = {
109
+ 'name': st.session_state.player_name,
110
+ 'position': st.session_state.position,
111
+ 'stats': st.session_state.character_stats,
112
+ 'last_update': time.time()
113
+ }
114
+ os.makedirs('players', exist_ok=True)
115
+ with open(f"players/{st.session_state.player_name}.json", 'w') as f:
116
+ json.dump(player_data, f)
117
+ save_game_state()
118
+
119
+ def calculate_distance(pos1, pos2):
120
+ dx = min(abs(pos1['x'] - pos2['x']), GRID_WIDTH - abs(pos1['x'] - pos2['x']))
121
+ dy = min(abs(pos1['y'] - pos2['y']), GRID_HEIGHT - abs(pos1['y'] - pos2['y']))
122
+ return dx + dy
123
+
124
+ def update_position(direction):
125
+ if direction == "up":
126
+ st.session_state.position['y'] = (st.session_state.position['y'] - 1) % GRID_HEIGHT
127
+ elif direction == "down":
128
+ st.session_state.position['y'] = (st.session_state.position['y'] + 1) % GRID_HEIGHT
129
+ elif direction == "left":
130
+ st.session_state.position['x'] = (st.session_state.position['x'] - 1) % GRID_WIDTH
131
+ elif direction == "right":
132
+ st.session_state.position['x'] = (st.session_state.position['x'] + 1) % GRID_WIDTH
133
+ st.session_state.last_move = time.time()
134
+ save_player_state()
135
+
136
+ def update_nearby_players():
137
+ current_time = time.time()
138
+ all_players = load_all_players(current_time)['players']
139
+ nearby = []
140
+ score_gain = 0
141
+ for player_name, player_data in all_players.items():
142
+ if player_name != st.session_state.player_name:
143
+ distance = calculate_distance(st.session_state.position, player_data['position'])
144
+ if distance <= INTERACTION_RADIUS:
145
+ nearby.append({
146
+ 'name': player_name,
147
+ 'distance': distance,
148
+ 'score': player_data['stats']['score']
149
+ })
150
+ score_gain += POINTS_PER_INTERACTION
151
+ if score_gain > 0:
152
+ st.session_state.character_stats['score'] += score_gain
153
+ save_player_state()
154
+ st.session_state.nearby_players = nearby
155
+
156
+ def create_game_board():
157
+ current_time = time.time()
158
+ all_players = load_all_players(current_time)['players']
159
+ images = get_game_images()
160
+ for y in range(GRID_HEIGHT):
161
+ cols = st.columns(GRID_WIDTH)
162
+ for x in range(GRID_WIDTH):
163
+ player_here = None
164
+ for player_name, player_data in all_players.items():
165
+ if (player_data['position']['x'] == x and player_data['position']['y'] == y):
166
+ player_here = player_name
167
+ if x == st.session_state.position['x'] and y == st.session_state.position['y']:
168
+ cols[x].image(images['player'], use_column_width=True)
169
+ cols[x].markdown(f"**You** ({st.session_state.character_stats['score']} pts)")
170
+ elif player_here:
171
+ cols[x].image(images['other_player'], use_column_width=True)
172
+ cols[x].markdown(f"**{player_here}** ({all_players[player_here]['stats']['score']} pts)")
173
+ else:
174
+ cols[x].image(images['tile'], use_column_width=True)
175
+
176
+ def create_auto_refresh_timer():
177
+ refresh_js = """
178
+ <script>
179
+ function setupRefresh() {
180
+ const milliseconds = 5000;
181
+ function refreshPage() {
182
+ window.parent.document.querySelector('.stApp').classList.add('refreshing');
183
+ setTimeout(() => window.location.reload(), 100);
184
+ }
185
+ let refresh = setInterval(refreshPage, milliseconds);
186
+ document.addEventListener('visibilitychange', function() {
187
+ if (document.hidden) {
188
+ clearInterval(refresh);
189
+ } else {
190
+ refresh = setInterval(refreshPage, milliseconds);
191
+ }
192
+ });
193
+ }
194
+ if (document.readyState === 'complete') {
195
+ setupRefresh();
196
+ } else {
197
+ window.addEventListener('load', setupRefresh);
198
+ }
199
+ </script>
200
+ """
201
+ st.components.v1.html(refresh_js, height=0)
202
+
203
+ def main():
204
+ create_auto_refresh_timer()
205
+ update_nearby_players()
206
+ st.sidebar.title("Player Info")
207
+
208
+ if st.session_state.player_name is None:
209
+ default_name = generate_fantasy_name()
210
+ player_name = st.sidebar.text_input("Enter your name or use generated name:", value=default_name)
211
+ if st.sidebar.button("Start Playing"):
212
+ st.session_state.player_name = player_name
213
+ if st.session_state.character_stats is None:
214
+ st.session_state.character_stats = {
215
+ 'STR': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
216
+ 'DEX': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
217
+ 'CON': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
218
+ 'INT': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
219
+ 'WIS': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
220
+ 'CHA': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
221
+ 'HP': random.randint(1, 20) * 2 + random.randint(1, 20),
222
+ 'MAX_HP': 40,
223
+ 'score': 0,
224
+ 'created_at': time.time()
225
+ }
226
+ save_player_state()
227
+ st.rerun()
228
+ else:
229
+ st.sidebar.markdown("### Nearby Players")
230
+ for player in st.session_state.nearby_players:
231
+ st.sidebar.markdown(f"**{player['name']}** - {player['distance']} tiles away - {player['score']} pts")
232
+
233
+ st.sidebar.markdown("### Movement Controls")
234
+ move_cols = st.sidebar.columns(3)
235
+ if move_cols[1].button("⬆️", key="up"):
236
+ update_position("up")
237
+ st.rerun()
238
+ cols = st.sidebar.columns(3)
239
+ if cols[0].button("⬅️", key="left"):
240
+ update_position("left")
241
+ st.rerun()
242
+ if cols[1].button("⬇️", key="down"):
243
+ update_position("down")
244
+ st.rerun()
245
+ if cols[2].button("➡️", key="right"):
246
+ update_position("right")
247
+ st.rerun()
248
+
249
+ if st.sidebar.button("Clear Game State"):
250
+ st.query_params.clear()
251
+ clear_caches()
252
+ st.rerun()
253
+
254
+ st.title("Multiplayer Tile Game")
255
+ create_game_board()
256
+
257
+ if (time.time() - st.session_state.last_move) > REFRESH_RATE:
258
+ st.session_state.last_move = time.time()
259
+ save_player_state()
260
+ st.rerun()
261
+
262
+ if __name__ == "__main__":
263
+ main()