LetsMakeGames2 / app.py
awacke1's picture
Update app.py
c166997 verified
raw
history blame
10.9 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
# ๐ŸŽฎ Game Constants
GRID_WIDTH = 16
GRID_HEIGHT = 9
REFRESH_RATE = 5
INTERACTION_RADIUS = 2
POINTS_PER_INTERACTION = 1
# ๐Ÿ“ Name Generation - Independent function
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 serverTimeOffset = 0;
function checkForUpdates() {
const now = Date.now();
if (now - lastUpdate >= 5000) {
lastUpdate = now;
window.parent.document.querySelector('.stApp').classList.add('refreshing');
setTimeout(() => window.location.reload(), 100);
}
}
function startAutoRefresh() {
setInterval(checkForUpdates, 1000);
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
lastUpdate = Date.now() - 4900;
}
});
}
if (document.readyState === 'complete') {
startAutoRefresh();
} else {
window.addEventListener('load', startAutoRefresh);
}
</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()
update_nearby_players()
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.query_params.clear()
clear_caches()
st.rerun()
st.title("Multiplayer Tile Game")
create_game_board()
if __name__ == "__main__":
main()