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 from py_mp import CommandServer, CommandClient, ClientCommand, ServerSideServerCommand from py_mp.commands import NetworkFlag import threading import queue # 🎮 Game Constants GRID_WIDTH = 16 GRID_HEIGHT = 9 REFRESH_RATE = 5 INTERACTION_RADIUS = 2 POINTS_PER_INTERACTION = 1 # 🌐 Network Settings SERVER_HOST = "localhost" SERVER_PORT = 5000 message_queue = queue.Queue() # ⏰ Time Sync def get_server_time(): return time.time() # 🎨 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') } # 📝 Name Generation @st.cache_data 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'] } # 🔄 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} # 🔄 Auto-Refresh Implementation def create_autorefresh(): refresh_html = """ """ 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(get_server_time()) } st.query_params.from_dict(params) # 🎲 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': get_server_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 = get_server_time() st.session_state.nearby_players = [] # 🎮 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': get_server_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 = get_server_time() save_player_state() # 👥 Nearby Players Update def update_nearby_players(): current_time = get_server_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 = get_server_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': get_server_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() st.cache_resource.clear() st.cache_data.clear() st.rerun() st.title("Multiplayer Tile Game") create_game_board() if __name__ == "__main__": main()