awacke1 commited on
Commit
ecd8001
Β·
verified Β·
1 Parent(s): cb19e9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -90
app.py CHANGED
@@ -55,11 +55,11 @@ EDGE_TTS_VOICES = [
55
  ]
56
  FILE_EMOJIS = {"md": "πŸ“œ", "mp3": "🎡"}
57
  WORLD_OBJECTS = [
58
- {"type": "🌿 Plants", "emoji": "🌱🌿🌾", "color": 0x228B22, "shape": "cylinder", "count": 10},
59
- {"type": "🐦 Animal Flocks", "emoji": "πŸ•ŠοΈπŸ¦πŸ€", "color": 0x87CEEB, "shape": "sphere", "count": 5},
60
- {"type": "πŸ”οΈ Mountains", "emoji": "πŸ”οΈβ›°οΈ", "color": 0x808080, "shape": "cone", "count": 3},
61
- {"type": "🌳 Trees", "emoji": "🌲🌳🌴", "color": 0x006400, "shape": "cylinder", "count": 8},
62
- {"type": "🐾 Animals", "emoji": "🐻🐺🦌", "color": 0x8B4513, "shape": "cube", "count": 6}
63
  ]
64
  PRAIRIE_LOCATIONS = {
65
  "Deadwood, SD": (44.3769, -103.7298),
@@ -88,11 +88,8 @@ def load_game_state(_timestamp):
88
  state = {
89
  "players": {},
90
  "treasures": [
91
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
92
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
93
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
94
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
95
  {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)}
 
96
  ],
97
  "world_objects": [],
98
  "history": []
@@ -114,59 +111,17 @@ def reset_game_state():
114
  state = {
115
  "players": {},
116
  "treasures": [
117
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
118
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
119
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
120
- {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)},
121
  {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)}
 
122
  ],
123
  "world_objects": [],
124
  "history": []
125
  }
126
  return update_game_state(state)
127
 
128
- # Agent Class for Players
129
- class PlayerAgent:
130
- def __init__(self, username, char_data):
131
- self.username = username
132
- self.x = random.uniform(-20, 20)
133
- self.z = random.uniform(-40, 40)
134
- self.color = char_data["color"]
135
- self.shape = char_data["shape"]
136
- self.score = 0
137
- self.treasures = 0
138
- self.last_active = time.time()
139
- self.voice = char_data["voice"]
140
-
141
- def to_dict(self):
142
- return {
143
- "username": self.username,
144
- "x": self.x,
145
- "z": self.z,
146
- "color": self.color,
147
- "shape": self.shape,
148
- "score": self.score,
149
- "treasures": self.treasures,
150
- "last_active": self.last_active
151
- }
152
-
153
- def update_from_message(self, message):
154
- if '|' in message:
155
- _, content = message.split('|', 1)
156
- if content.startswith("MOVE:"):
157
- _, x, z = content.split(":")
158
- self.x, self.z = float(x), float(z)
159
- self.last_active = time.time()
160
- elif content.startswith("SCORE:"):
161
- self.score = int(content.split(":")[1])
162
- self.last_active = time.time()
163
- elif content.startswith("TREASURE:"):
164
- self.treasures = int(content.split(":")[1])
165
- self.last_active = time.time()
166
-
167
- # Helpers
168
  def format_timestamp(username=""):
169
- now = datetime.now(pytz.timezone('US/Central')).strftime("%Y%m%d_%H%M%S")
170
  return f"{now}-by-{username}"
171
 
172
  def clean_text_for_tts(text):
@@ -199,6 +154,26 @@ def load_username():
199
  return f.read().strip()
200
  return None
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  # Audio Processing
203
  async def async_edge_tts_generate(text, voice, username):
204
  cache_key = f"{text[:100]}_{voice}"
@@ -218,29 +193,14 @@ def play_and_download_audio(file_path):
218
  st.audio(file_path, start_time=0)
219
  st.markdown(get_download_link(file_path), unsafe_allow_html=True)
220
 
221
- # WebSocket Broadcast
222
- async def broadcast_message(message, room_id):
223
- if room_id in st.session_state.active_connections:
224
- disconnected = []
225
- for client_id, ws in st.session_state.active_connections[room_id].items():
226
- try:
227
- await ws.send(message)
228
- except websockets.ConnectionClosed:
229
- disconnected.append(client_id)
230
- for client_id in disconnected:
231
- if client_id in st.session_state.active_connections[room_id]:
232
- del st.session_state.active_connections[room_id][client_id]
233
-
234
- # Chat and Quest Log
235
  async def save_chat_entry(username, message, voice, is_markdown=False):
236
  if not message.strip() or message == st.session_state.get('last_transcript', ''):
237
  return None, None
238
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
239
- entry = f"[{timestamp}] {username}: {message}" if not is_markdown else f"[{timestamp}] {username}:\n```markdown\n{message}\n```"
240
- md_file = create_file(entry, username, "md")
241
- with open(CHAT_FILE, 'a') as f:
242
- f.write(f"{entry}\n")
243
- audio_file = await async_edge_tts_generate(message, voice, username)
244
  await broadcast_message(f"{username}|{message}", "quest")
245
  st.session_state.chat_history.append({"username": username, "message": message, "audio": audio_file})
246
  st.session_state.last_transcript = message
@@ -249,7 +209,7 @@ async def save_chat_entry(username, message, voice, is_markdown=False):
249
  game_state["players"][username]["score"] += 10
250
  game_state["players"][username]["treasures"] += 1
251
  game_state["players"][username]["last_active"] = time.time()
252
- game_state["history"].append(entry)
253
  if message.lower() in ["plants", "animal flocks", "mountains", "trees", "animals"]:
254
  obj_type = next(o for o in WORLD_OBJECTS if message.lower() in o["type"].lower())
255
  for _ in range(obj_type["count"]):
@@ -303,7 +263,63 @@ def init_session_state():
303
 
304
  init_session_state()
305
 
306
- # WebSocket for Multiplayer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  async def websocket_handler(websocket, path):
308
  client_id = str(uuid.uuid4())
309
  room_id = "quest"
@@ -311,7 +327,7 @@ async def websocket_handler(websocket, path):
311
  st.session_state.active_connections[room_id] = {}
312
  st.session_state.active_connections[room_id][client_id] = websocket
313
  username = st.session_state.username
314
-
315
  game_state = load_game_state(st.session_state.game_state_timestamp)
316
  if "prairie" in path:
317
  char = next(c for c in EDGE_TTS_VOICES if c["name"] == username)
@@ -330,7 +346,7 @@ async def websocket_handler(websocket, path):
330
  update_game_state(game_state)
331
  st.session_state.players[client_id] = game_state["players"][username]
332
  await broadcast_message(f"System|{username} joins the quest!", room_id)
333
-
334
  try:
335
  async for message in websocket:
336
  if '|' in message:
@@ -349,10 +365,9 @@ async def websocket_handler(websocket, path):
349
  target = PRAIRIE_LOCATIONS.get(value, PRAIRIE_LOCATIONS["Deadwood, SD"])
350
  st.session_state.prairie_players[client_id]["location"] = target
351
  action_msg = f"{sender} ({st.session_state.prairie_players[client_id]['animal']}) moves to {value}"
352
- await save_chat_entry(sender, action_msg, voice)
353
  else:
354
- await save_chat_entry(sender, content, voice)
355
- await perform_arxiv_search(content, sender)
356
  update_game_state(game_state)
357
  except websockets.ConnectionClosed:
358
  await broadcast_message(f"System|{username} leaves the quest!", room_id)
@@ -374,7 +389,7 @@ async def periodic_update():
374
  del game_state["players"][player]
375
  await broadcast_message(f"System|{player} timed out after 60 seconds!", "quest")
376
  for username in game_state["players"]:
377
- game_state["players"][username]["score"] += int((current_time - game_state["players"][username]["last_active"]) * 60) # $1 per second
378
  update_game_state(game_state)
379
 
380
  player_list = ", ".join([p for p in game_state["players"].keys()]) or "No adventurers yet!"
@@ -390,7 +405,7 @@ async def periodic_update():
390
  chat_content = await load_chat()
391
  await broadcast_message(f"CHAT_UPDATE:{json.dumps(chat_content[-10:])}", "quest")
392
  await broadcast_message(f"GAME_STATE:{json.dumps(game_state)}", "quest")
393
- await save_chat_entry("System", f"{message}\n{prairie_message}", "en-US-AriaNeural")
394
  await asyncio.sleep(st.session_state.update_interval)
395
 
396
  async def run_websocket_server():
@@ -405,7 +420,7 @@ def start_websocket_server():
405
  asyncio.set_event_loop(loop)
406
  loop.run_until_complete(run_websocket_server())
407
 
408
- # ArXiv Integration
409
  async def perform_arxiv_search(query, username):
410
  gradio_client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
411
  refs = gradio_client.predict(
@@ -413,7 +428,7 @@ async def perform_arxiv_search(query, username):
413
  )[0]
414
  result = f"πŸ“š Ancient Rocky Knowledge:\n{refs}"
415
  voice = next(c["voice"] for c in EDGE_TTS_VOICES if c["name"] == username)
416
- md_file, audio_file = await save_chat_entry(username, result, voice, True)
417
  return md_file, audio_file
418
 
419
  # Sidebar Functions
@@ -455,7 +470,7 @@ def log_performance_metrics():
455
  percentage = (duration / total_time) * 100
456
  st.sidebar.write(f"**{operation}:** {duration:.2f}s ({percentage:.1f}%)")
457
 
458
- # Enhanced 3D Game HTML
459
  rocky_map_html = f"""
460
  <!DOCTYPE html>
461
  <html lang="en">
@@ -648,7 +663,7 @@ rocky_map_html = f"""
648
  const timeout = 60 - (performance.now() / 1000 - lastActive);
649
  document.getElementById('timeout').textContent = `Timeout: ${{Math.max(0, timeout.toFixed(0))}}s`;
650
  document.getElementById('score').textContent = `Score: $${{score}}`;
651
- document.getElementById('treasures').textContent = `Treasures: ${{treasureCount}}`;
652
  }}
653
 
654
  function updatePlayers(playerData) {{
@@ -723,7 +738,6 @@ rocky_map_html = f"""
723
 
724
  # Main Game Loop
725
  def main():
726
- # Top Center Chat and Character Display
727
  st.markdown(f"<h2 style='text-align: center;'>Welcome, {st.session_state.username}!</h2>", unsafe_allow_html=True)
728
  message = st.text_input(f"πŸ—¨οΈ Chat as {st.session_state.username}:", placeholder="Type to chat or add world features! 🌲", key="chat_input")
729
  if st.button("🌟 Send"):
@@ -750,7 +764,6 @@ def main():
750
  st.session_state.game_state_timestamp = time.time()
751
  st.rerun()
752
 
753
- # Demo Buttons
754
  st.sidebar.markdown("### 🌟 World Additions")
755
  if st.sidebar.button("🌿 Add Plants"):
756
  asyncio.run(save_chat_entry(st.session_state.username, "plants", st.session_state.tts_voice))
@@ -848,4 +861,4 @@ def main():
848
  log_performance_metrics()
849
 
850
  if __name__ == "__main__":
851
- main()
 
55
  ]
56
  FILE_EMOJIS = {"md": "πŸ“œ", "mp3": "🎡"}
57
  WORLD_OBJECTS = [
58
+ {"type": "🌿 Plants", "emoji": "🌱", "color": 0x228B22, "shape": "cylinder", "count": 10},
59
+ {"type": "🐦 Animal Flocks", "emoji": "πŸ•ŠοΈ", "color": 0x87CEEB, "shape": "sphere", "count": 5},
60
+ {"type": "πŸ”οΈ Mountains", "emoji": "πŸ”οΈ", "color": 0x808080, "shape": "cone", "count": 3},
61
+ {"type": "🌳 Trees", "emoji": "🌲", "color": 0x006400, "shape": "cylinder", "count": 8},
62
+ {"type": "🐾 Animals", "emoji": "🐻", "color": 0x8B4513, "shape": "cube", "count": 6}
63
  ]
64
  PRAIRIE_LOCATIONS = {
65
  "Deadwood, SD": (44.3769, -103.7298),
 
88
  state = {
89
  "players": {},
90
  "treasures": [
 
 
 
 
91
  {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)}
92
+ for _ in range(5)
93
  ],
94
  "world_objects": [],
95
  "history": []
 
111
  state = {
112
  "players": {},
113
  "treasures": [
 
 
 
 
114
  {"id": str(uuid.uuid4()), "x": random.uniform(-40, 40), "z": random.uniform(-40, 40)}
115
+ for _ in range(5)
116
  ],
117
  "world_objects": [],
118
  "history": []
119
  }
120
  return update_game_state(state)
121
 
122
+ # Timestamp Formatter (Pattern: mmddYYYY-HHMM-AM/PM)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  def format_timestamp(username=""):
124
+ now = datetime.now(pytz.timezone('US/Central')).strftime("%m%d%Y-%I%M-%p")
125
  return f"{now}-by-{username}"
126
 
127
  def clean_text_for_tts(text):
 
154
  return f.read().strip()
155
  return None
156
 
157
+ # ---------------------
158
+ # New Pair of Helper Functions
159
+ # ---------------------
160
+ def log_chat_history(username, message, is_markdown=False):
161
+ """Log the chat entry to a markdown file."""
162
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
163
+ if is_markdown:
164
+ entry = f"[{timestamp}] {username}:\n```markdown\n{message}\n```"
165
+ else:
166
+ entry = f"[{timestamp}] {username}: {message}"
167
+ md_file = create_file(entry, username, "md")
168
+ with open(CHAT_FILE, 'a') as f:
169
+ f.write(f"{entry}\n")
170
+ return md_file
171
+
172
+ async def generate_chat_audio(username, message, voice):
173
+ """Generate TTS audio file for the chat message."""
174
+ audio_file = await async_edge_tts_generate(message, voice, username)
175
+ return audio_file
176
+
177
  # Audio Processing
178
  async def async_edge_tts_generate(text, voice, username):
179
  cache_key = f"{text[:100]}_{voice}"
 
193
  st.audio(file_path, start_time=0)
194
  st.markdown(get_download_link(file_path), unsafe_allow_html=True)
195
 
196
+ # ---------------------
197
+ # Wrapper Function to Save Chat Entry (Using the Two Helpers Above)
198
+ # ---------------------
 
 
 
 
 
 
 
 
 
 
 
199
  async def save_chat_entry(username, message, voice, is_markdown=False):
200
  if not message.strip() or message == st.session_state.get('last_transcript', ''):
201
  return None, None
202
+ md_file = log_chat_history(username, message, is_markdown)
203
+ audio_file = await generate_chat_audio(username, message, voice)
 
 
 
 
204
  await broadcast_message(f"{username}|{message}", "quest")
205
  st.session_state.chat_history.append({"username": username, "message": message, "audio": audio_file})
206
  st.session_state.last_transcript = message
 
209
  game_state["players"][username]["score"] += 10
210
  game_state["players"][username]["treasures"] += 1
211
  game_state["players"][username]["last_active"] = time.time()
212
+ game_state["history"].append(md_file)
213
  if message.lower() in ["plants", "animal flocks", "mountains", "trees", "animals"]:
214
  obj_type = next(o for o in WORLD_OBJECTS if message.lower() in o["type"].lower())
215
  for _ in range(obj_type["count"]):
 
263
 
264
  init_session_state()
265
 
266
+ # Agent Class for Players
267
+ class PlayerAgent:
268
+ def __init__(self, username, char_data):
269
+ self.username = username
270
+ self.x = random.uniform(-20, 20)
271
+ self.z = random.uniform(-40, 40)
272
+ self.color = char_data["color"]
273
+ self.shape = char_data["shape"]
274
+ self.score = 0
275
+ self.treasures = 0
276
+ self.last_active = time.time()
277
+ self.voice = char_data["voice"]
278
+
279
+ def to_dict(self):
280
+ return {
281
+ "username": self.username,
282
+ "x": self.x,
283
+ "z": self.z,
284
+ "color": self.color,
285
+ "shape": self.shape,
286
+ "score": self.score,
287
+ "treasures": self.treasures,
288
+ "last_active": self.last_active
289
+ }
290
+
291
+ def update_from_message(self, message):
292
+ if '|' in message:
293
+ _, content = message.split('|', 1)
294
+ if content.startswith("MOVE:"):
295
+ _, x, z = content.split(":")
296
+ self.x, self.z = float(x), float(z)
297
+ self.last_active = time.time()
298
+ elif content.startswith("SCORE:"):
299
+ self.score = int(content.split(":")[1])
300
+ self.last_active = time.time()
301
+ elif content.startswith("TREASURE:"):
302
+ self.treasures = int(content.split(":")[1])
303
+ self.last_active = time.time()
304
+
305
+ # WebSocket Broadcast
306
+ async def broadcast_message(message, room_id):
307
+ if room_id in st.session_state.active_connections:
308
+ disconnected = []
309
+ for client_id, ws in st.session_state.active_connections[room_id].items():
310
+ try:
311
+ await ws.send(message)
312
+ except websockets.ConnectionClosed:
313
+ disconnected.append(client_id)
314
+ for client_id in disconnected:
315
+ if client_id in st.session_state.active_connections[room_id]:
316
+ del st.session_state.active_connections[room_id][client_id]
317
+
318
+ # Chat and Quest Log (uses the new helper functions)
319
+ async def save_chat_entry_wrapper(username, message, voice, is_markdown=False):
320
+ return await save_chat_entry(username, message, voice, is_markdown)
321
+
322
+ # WebSocket Handler
323
  async def websocket_handler(websocket, path):
324
  client_id = str(uuid.uuid4())
325
  room_id = "quest"
 
327
  st.session_state.active_connections[room_id] = {}
328
  st.session_state.active_connections[room_id][client_id] = websocket
329
  username = st.session_state.username
330
+
331
  game_state = load_game_state(st.session_state.game_state_timestamp)
332
  if "prairie" in path:
333
  char = next(c for c in EDGE_TTS_VOICES if c["name"] == username)
 
346
  update_game_state(game_state)
347
  st.session_state.players[client_id] = game_state["players"][username]
348
  await broadcast_message(f"System|{username} joins the quest!", room_id)
349
+
350
  try:
351
  async for message in websocket:
352
  if '|' in message:
 
365
  target = PRAIRIE_LOCATIONS.get(value, PRAIRIE_LOCATIONS["Deadwood, SD"])
366
  st.session_state.prairie_players[client_id]["location"] = target
367
  action_msg = f"{sender} ({st.session_state.prairie_players[client_id]['animal']}) moves to {value}"
368
+ await save_chat_entry_wrapper(sender, action_msg, voice)
369
  else:
370
+ await save_chat_entry_wrapper(sender, content, voice)
 
371
  update_game_state(game_state)
372
  except websockets.ConnectionClosed:
373
  await broadcast_message(f"System|{username} leaves the quest!", room_id)
 
389
  del game_state["players"][player]
390
  await broadcast_message(f"System|{player} timed out after 60 seconds!", "quest")
391
  for username in game_state["players"]:
392
+ game_state["players"][username]["score"] += int((current_time - game_state["players"][username]["last_active"]) * 60)
393
  update_game_state(game_state)
394
 
395
  player_list = ", ".join([p for p in game_state["players"].keys()]) or "No adventurers yet!"
 
405
  chat_content = await load_chat()
406
  await broadcast_message(f"CHAT_UPDATE:{json.dumps(chat_content[-10:])}", "quest")
407
  await broadcast_message(f"GAME_STATE:{json.dumps(game_state)}", "quest")
408
+ await save_chat_entry_wrapper("System", f"{message}\n{prairie_message}", "en-US-AriaNeural")
409
  await asyncio.sleep(st.session_state.update_interval)
410
 
411
  async def run_websocket_server():
 
420
  asyncio.set_event_loop(loop)
421
  loop.run_until_complete(run_websocket_server())
422
 
423
+ # ArXiv Integration remains unchanged
424
  async def perform_arxiv_search(query, username):
425
  gradio_client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
426
  refs = gradio_client.predict(
 
428
  )[0]
429
  result = f"πŸ“š Ancient Rocky Knowledge:\n{refs}"
430
  voice = next(c["voice"] for c in EDGE_TTS_VOICES if c["name"] == username)
431
+ md_file, audio_file = await save_chat_entry_wrapper(username, result, voice, True)
432
  return md_file, audio_file
433
 
434
  # Sidebar Functions
 
470
  percentage = (duration / total_time) * 100
471
  st.sidebar.write(f"**{operation}:** {duration:.2f}s ({percentage:.1f}%)")
472
 
473
+ # Enhanced 3D Game HTML remains largely unchanged, with dynamic insertion of the username and avatar details
474
  rocky_map_html = f"""
475
  <!DOCTYPE html>
476
  <html lang="en">
 
663
  const timeout = 60 - (performance.now() / 1000 - lastActive);
664
  document.getElementById('timeout').textContent = `Timeout: ${{Math.max(0, timeout.toFixed(0))}}s`;
665
  document.getElementById('score').textContent = `Score: $${{score}}`;
666
+ document.getElementById('treasures').textContent = `Treasures: $${{treasureCount}}`;
667
  }}
668
 
669
  function updatePlayers(playerData) {{
 
738
 
739
  # Main Game Loop
740
  def main():
 
741
  st.markdown(f"<h2 style='text-align: center;'>Welcome, {st.session_state.username}!</h2>", unsafe_allow_html=True)
742
  message = st.text_input(f"πŸ—¨οΈ Chat as {st.session_state.username}:", placeholder="Type to chat or add world features! 🌲", key="chat_input")
743
  if st.button("🌟 Send"):
 
764
  st.session_state.game_state_timestamp = time.time()
765
  st.rerun()
766
 
 
767
  st.sidebar.markdown("### 🌟 World Additions")
768
  if st.sidebar.button("🌿 Add Plants"):
769
  asyncio.run(save_chat_entry(st.session_state.username, "plants", st.session_state.tts_voice))
 
861
  log_performance_metrics()
862
 
863
  if __name__ == "__main__":
864
+ main()