LetsMakeGames2 / backup.app.py
awacke1's picture
Create backup.app.py
93bdd1f verified
raw
history blame
12.8 kB
import streamlit as st
import os
import random
import time
from PIL import Image
import json
from datetime import datetime
from pathlib import Path
import base64
from io import BytesIO
import numpy as np
import asyncio
import threading
from typing import Optional
import queue
import atexit
# ๐ŸŽฎ Game Constants
GRID_WIDTH = 16
GRID_HEIGHT = 9
REFRESH_RATE = 5
INTERACTION_RADIUS = 2
POINTS_PER_INTERACTION = 1
# ๐Ÿ•’ Timer Management
class GameTimer:
def __init__(self):
self.running = True
self._loop: Optional[asyncio.AbstractEventLoop] = None
self.update_queue = queue.Queue()
self.thread = threading.Thread(target=self._run_timer, daemon=True)
self.thread.start()
def _run_timer(self):
self._loop = asyncio.new_event_loop()
asyncio.set_event_loop(self._loop)
self._loop.run_until_complete(self._timer_loop())
async def _timer_loop(self):
while self.running:
try:
current_time = time.time()
self.update_queue.put(('update', current_time))
await asyncio.sleep(REFRESH_RATE)
except Exception as e:
print(f"Timer error: {e}")
await asyncio.sleep(1)
def stop(self):
self.running = False
if self._loop:
self._loop.stop()
# Initialize timer in session state
if 'game_timer' not in st.session_state:
st.session_state.game_timer = GameTimer()
# ๐Ÿ“ Name Generation
def generate_fantasy_name():
prefixes = ['Aer', 'Bal', 'Cal', 'Dor', 'El', 'Fae', 'Gor', 'Hel', 'Il', 'Jor',
'Kal', 'Lyr', 'Mel', 'Nym', 'Oro', 'Pyr', 'Qar', 'Ryn', 'Syl', 'Tyr']
suffixes = ['ian', 'or', 'ion', 'us', 'ix', 'ar', 'en', 'yr', 'el', 'an',
'is', 'ax', 'on', 'ir', 'ex', 'az', 'er', 'eth', 'ys', 'ix']
return random.choice(prefixes) + random.choice(suffixes)
# ๐ŸŽจ Resource Management
@st.cache_resource(show_spinner="Loading game resources...")
def get_game_images():
return {
'tile': Image.new('RGB', (50, 50), color='green'),
'player': Image.new('RGB', (50, 50), color='blue'),
'other_player': Image.new('RGB', (50, 50), color='red')
}
# ๐Ÿ”„ Player Synchronization
@st.cache_data(ttl=2)
def load_all_players(timestamp):
players = {}
if os.path.exists('players'):
for filename in os.listdir('players'):
if filename.endswith('.json'):
with open(f"players/{filename}", 'r') as f:
try:
player_data = json.load(f)
if time.time() - player_data['last_update'] < 60:
players[player_data['name']] = player_data
except json.JSONDecodeError:
continue
return {'players': players, 'last_update': timestamp}
def clear_caches():
st.cache_resource.clear()
st.cache_data.clear()
# ๐Ÿ”„ Auto-Refresh Implementation
def create_autorefresh():
refresh_html = """
<script>
let lastUpdate = Date.now();
let updateInterval = null;
function checkForUpdates() {
const now = Date.now();
if (now - lastUpdate >= 5000) {
lastUpdate = now;
window.parent.document.querySelector('.stApp').classList.add('refreshing');
window.location.reload();
}
}
function startAutoRefresh() {
if (!updateInterval) {
updateInterval = setInterval(checkForUpdates, 1000);
}
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
lastUpdate = Date.now() - 4900;
}
});
}
if (document.readyState === 'complete') {
startAutoRefresh();
} else {
window.addEventListener('load', startAutoRefresh);
}
// Cleanup on unload
window.addEventListener('unload', function() {
if (updateInterval) {
clearInterval(updateInterval);
}
});
</script>
"""
st.components.v1.html(refresh_html, height=0)
# ๐Ÿ’พ State Management
def save_game_state():
if st.session_state.player_name and st.session_state.character_stats:
params = {
'player_name': st.session_state.player_name,
'x': str(st.session_state.position['x']),
'y': str(st.session_state.position['y']),
**{k: str(v) for k, v in st.session_state.character_stats.items()},
'last_sync': str(time.time())
}
st.query_params.from_dict(params)
def load_game_state():
if 'player_name' in st.query_params:
st.session_state.player_name = st.query_params.player_name
st.session_state.position = {
'x': int(st.query_params.get('x', random.randint(0, GRID_WIDTH - 1))),
'y': int(st.query_params.get('y', random.randint(0, GRID_HEIGHT - 1)))
}
st.session_state.character_stats = {
'STR': int(st.query_params.get('STR', 10)),
'DEX': int(st.query_params.get('DEX', 10)),
'CON': int(st.query_params.get('CON', 10)),
'INT': int(st.query_params.get('INT', 10)),
'WIS': int(st.query_params.get('WIS', 10)),
'CHA': int(st.query_params.get('CHA', 10)),
'HP': int(st.query_params.get('HP', 20)),
'MAX_HP': int(st.query_params.get('MAX_HP', 40)),
'score': int(st.query_params.get('score', 0)),
'created_at': float(st.query_params.get('created_at', time.time()))
}
# ๐ŸŽฒ Game State Initialization
if 'initialized' not in st.session_state:
st.session_state.initialized = False
st.session_state.game_state = {
'players': {},
'chat_messages': [],
'last_sync': time.time()
}
st.session_state.player_name = None
st.session_state.character_stats = None
st.session_state.position = {
'x': random.randint(0, GRID_WIDTH - 1),
'y': random.randint(0, GRID_HEIGHT - 1)
}
st.session_state.last_move = time.time()
st.session_state.nearby_players = []
load_game_state()
# ๐ŸŽฎ Player State Management
def save_player_state():
if st.session_state.player_name:
player_data = {
'name': st.session_state.player_name,
'position': st.session_state.position,
'stats': st.session_state.character_stats,
'last_update': time.time()
}
os.makedirs('players', exist_ok=True)
with open(f"players/{st.session_state.player_name}.json", 'w') as f:
json.dump(player_data, f)
save_game_state()
# ๐Ÿ“ Distance Calculation
def calculate_distance(pos1, pos2):
dx = min(abs(pos1['x'] - pos2['x']), GRID_WIDTH - abs(pos1['x'] - pos2['x']))
dy = min(abs(pos1['y'] - pos2['y']), GRID_HEIGHT - abs(pos1['y'] - pos2['y']))
return dx + dy
# ๐Ÿƒโ€โ™‚๏ธ Movement System
def update_position(direction):
if direction == "up":
st.session_state.position['y'] = (st.session_state.position['y'] - 1) % GRID_HEIGHT
elif direction == "down":
st.session_state.position['y'] = (st.session_state.position['y'] + 1) % GRID_HEIGHT
elif direction == "left":
st.session_state.position['x'] = (st.session_state.position['x'] - 1) % GRID_WIDTH
elif direction == "right":
st.session_state.position['x'] = (st.session_state.position['x'] + 1) % GRID_WIDTH
st.session_state.last_move = time.time()
save_player_state()
# ๐Ÿ‘ฅ Nearby Players Update
def update_nearby_players():
current_time = time.time()
all_players = load_all_players(current_time)['players']
nearby = []
score_gain = 0
for player_name, player_data in all_players.items():
if player_name != st.session_state.player_name:
distance = calculate_distance(st.session_state.position, player_data['position'])
if distance <= INTERACTION_RADIUS:
nearby.append({
'name': player_name,
'distance': distance,
'score': player_data['stats']['score'],
'last_seen': current_time - player_data['last_update']
})
score_gain += POINTS_PER_INTERACTION
if score_gain > 0:
st.session_state.character_stats['score'] += score_gain
save_player_state()
st.session_state.nearby_players = nearby
# ๐ŸŽจ Game Board Creation
def create_game_board():
current_time = time.time()
all_players = load_all_players(current_time)['players']
images = get_game_images()
for y in range(GRID_HEIGHT):
cols = st.columns(GRID_WIDTH)
for x in range(GRID_WIDTH):
player_here = None
for player_name, player_data in all_players.items():
if (player_data['position']['x'] == x and
player_data['position']['y'] == y):
player_here = player_name
if x == st.session_state.position['x'] and y == st.session_state.position['y']:
cols[x].image(images['player'], use_column_width=True)
cols[x].markdown(f"**You** ({st.session_state.character_stats['score']} pts)")
elif player_here:
cols[x].image(images['other_player'], use_column_width=True)
cols[x].markdown(f"**{player_here}** ({all_players[player_here]['stats']['score']} pts)")
else:
cols[x].image(images['tile'], use_column_width=True)
# ๐ŸŽฎ Main Game Loop
def main():
create_autorefresh()
# Process any queued updates
try:
while not st.session_state.game_timer.update_queue.empty():
update_type, timestamp = st.session_state.game_timer.update_queue.get_nowait()
if update_type == 'update':
update_nearby_players()
save_player_state()
except queue.Empty:
pass
st.sidebar.title("Player Info")
if st.session_state.player_name is None:
default_name = generate_fantasy_name()
player_name = st.sidebar.text_input("Enter your name or use generated name:", value=default_name)
if st.sidebar.button("Start Playing"):
st.session_state.player_name = player_name
if st.session_state.character_stats is None:
st.session_state.character_stats = {
'STR': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'DEX': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'CON': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'INT': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'WIS': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'CHA': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'HP': random.randint(1, 20) * 2 + random.randint(1, 20),
'MAX_HP': 40,
'score': 0,
'created_at': time.time()
}
save_player_state()
st.rerun()
else:
st.sidebar.markdown("### Nearby Players")
for player in st.session_state.nearby_players:
st.sidebar.markdown(
f"**{player['name']}** - {player['distance']} tiles away - {player['score']} pts\n"
f"Last seen: {player['last_seen']:.1f}s ago"
)
st.sidebar.markdown("### Movement Controls")
move_cols = st.sidebar.columns(3)
if move_cols[1].button("โฌ†๏ธ", key="up"):
update_position("up")
st.rerun()
cols = st.sidebar.columns(3)
if cols[0].button("โฌ…๏ธ", key="left"):
update_position("left")
st.rerun()
if cols[1].button("โฌ‡๏ธ", key="down"):
update_position("down")
st.rerun()
if cols[2].button("โžก๏ธ", key="right"):
update_position("right")
st.rerun()
if st.sidebar.button("Clear Game State"):
st.session_state.game_timer.stop()
st.query_params.clear()
clear_caches()
st.rerun()
st.title("Multiplayer Tile Game")
create_game_board()
# ๐Ÿงน Cleanup on exit
def cleanup():
if 'game_timer' in st.session_state:
st.session_state.game_timer.stop()
# Register cleanup
atexit.register(cleanup)
if __name__ == "__main__":
main()