Spaces:
Sleeping
Sleeping
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 | |
GRID_WIDTH = 16 | |
GRID_HEIGHT = 9 | |
REFRESH_RATE = 10 | |
INTERACTION_RADIUS = 2 | |
POINTS_PER_INTERACTION = 1 | |
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') | |
} | |
def get_name_components(): | |
return { | |
'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'] | |
} | |
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': time.time()} | |
def create_sync_mechanism(): | |
sync_js = """ | |
<script> | |
function updateGameState() { | |
const timestamp = Date.now(); | |
window.parent.document.querySelector('.stApp').classList.add('refreshing'); | |
setTimeout(() => window.location.reload(), 100); | |
} | |
function displayCharacterSheet(playerData) { | |
const sidebar = window.parent.document.querySelector('[data-testid="stSidebar"]'); | |
if (sidebar && playerData) { | |
const statsDiv = document.createElement('div'); | |
statsDiv.innerHTML = ` | |
<div style="padding: 10px; margin: 5px; border: 1px solid #ccc; border-radius: 5px;"> | |
<h3>${playerData.name}</h3> | |
<p>Score: ${playerData.stats.score}</p> | |
<p>HP: ${playerData.stats.HP}/${playerData.stats.MAX_HP}</p> | |
<p>STR: ${playerData.stats.STR}</p> | |
<p>DEX: ${playerData.stats.DEX}</p> | |
<p>CON: ${playerData.stats.CON}</p> | |
<p>INT: ${playerData.stats.INT}</p> | |
<p>WIS: ${playerData.stats.WIS}</p> | |
<p>CHA: ${playerData.stats.CHA}</p> | |
</div> | |
`; | |
} | |
} | |
function startSync() { | |
// Update game state every 10 seconds | |
setInterval(updateGameState, 10000); | |
// Handle visibility changes | |
document.addEventListener('visibilitychange', function() { | |
if (!document.hidden) { | |
updateGameState(); | |
} | |
}); | |
// Handle focus changes | |
window.addEventListener('focus', updateGameState); | |
} | |
if (document.readyState === 'complete') { | |
startSync(); | |
} else { | |
window.addEventListener('load', startSync); | |
} | |
</script> | |
""" | |
st.components.v1.html(sync_js, height=0) | |
def show_character_sheet(player_data): | |
st.sidebar.markdown("### Character Sheet") | |
st.sidebar.markdown(f""" | |
**Name:** {player_data['name']} | |
**Stats:** | |
- HP: {player_data['stats']['HP']}/{player_data['stats']['MAX_HP']} | |
- STR: {player_data['stats']['STR']} | |
- DEX: {player_data['stats']['DEX']} | |
- CON: {player_data['stats']['CON']} | |
- INT: {player_data['stats']['INT']} | |
- WIS: {player_data['stats']['WIS']} | |
- CHA: {player_data['stats']['CHA']} | |
**Score:** {player_data['stats']['score']} | |
""") | |
[Previous functions remain the same: generate_fantasy_name, load_game_state, save_game_state, calculate_distance, update_position, etc.] | |
def main(): | |
create_sync_mechanism() | |
update_nearby_players() | |
st.sidebar.title("Player Info") | |
current_time = time.time() | |
all_players = load_all_players(current_time)['players'] | |
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: | |
show_character_sheet({'name': st.session_state.player_name, 'stats': st.session_state.character_stats}) | |
st.sidebar.markdown("### Other Players") | |
for player_name, player_data in all_players.items(): | |
if player_name != st.session_state.player_name: | |
show_character_sheet(player_data) | |
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 (time.time() - st.session_state.last_move) > REFRESH_RATE: | |
st.session_state.last_move = time.time() | |
save_player_state() | |
st.rerun() | |
if __name__ == "__main__": | |
main() |