import streamlit as st import json import random import time import os from streamlit_javascript import st_javascript from math import cos, sin, pi # Constants GRID_SIZE = 10 MAX_PLAYERS = 10 UPDATE_INTERVAL = 0.05 # Decreased for smoother animation GAME_STATE_FILE = "game_state.json" CELL_SIZE = 50 PLAYER_SIZE = 40 VELOCITY_FACTOR = 0.5 FRICTION = 0.98 BOUNCE_FACTOR = -0.8 # List of common names COMMON_NAMES = ["Emma", "Liam", "Olivia", "Noah", "Ava", "Ethan", "Sophia", "Mason", "Isabella", "William"] # Initialize session state if "player_name" not in st.session_state: st.session_state.player_name = random.choice(COMMON_NAMES) if "selected_player" not in st.session_state: st.session_state.selected_player = None if "player_color" not in st.session_state: st.session_state.player_color = f"#{random.randint(0, 0xFFFFFF):06x}" # Function to load game state def load_game_state(): if os.path.exists(GAME_STATE_FILE): with open(GAME_STATE_FILE, "r") as f: return json.load(f) return {"players": {}, "obstacles": []} # Function to save game state def save_game_state(state): with open(GAME_STATE_FILE, "w") as f: json.dump(state, f) # Function to update player position and velocity def update_player_position(player_name, vx, vy): state = load_game_state() if player_name in state["players"]: player = state["players"][player_name] player["vx"] = player.get("vx", 0) + vx * VELOCITY_FACTOR player["vy"] = player.get("vy", 0) + vy * VELOCITY_FACTOR # Apply friction player["vx"] *= FRICTION player["vy"] *= FRICTION # Update position player["x"] += player["vx"] player["y"] += player["vy"] # Boundary check and bounce if player["x"] < 0: player["x"] = 0 player["vx"] *= BOUNCE_FACTOR elif player["x"] > GRID_SIZE - 1: player["x"] = GRID_SIZE - 1 player["vx"] *= BOUNCE_FACTOR if player["y"] < 0: player["y"] = 0 player["vy"] *= BOUNCE_FACTOR elif player["y"] > GRID_SIZE - 1: player["y"] = GRID_SIZE - 1 player["vy"] *= BOUNCE_FACTOR else: if len(state["players"]) < MAX_PLAYERS: state["players"][player_name] = { "x": GRID_SIZE // 2, "y": GRID_SIZE // 2, "vx": 0, "vy": 0, "color": st.session_state.player_color } save_game_state(state) # Function to generate SVG for the game board def generate_svg(game_state): svg = f'' # Define gradients svg += ''' ''' # Draw grid with gradient svg += f'' for i in range(GRID_SIZE): for j in range(GRID_SIZE): svg += f'' # Draw players with complex shapes and animations for player, data in game_state["players"].items(): x, y = data["x"] * CELL_SIZE + CELL_SIZE // 2, data["y"] * CELL_SIZE + CELL_SIZE // 2 color = data.get("color", "#000000") player_id = f"player-{player}" # Create a group for the player svg += f'' # Player body (hexagon) points = " ".join([f"{x + PLAYER_SIZE // 2 * cos(i * pi / 3)},{y + PLAYER_SIZE // 2 * sin(i * pi / 3)}" for i in range(6)]) svg += f'' svg += f'' svg += '' # Player eye (circle) eye_x, eye_y = x + PLAYER_SIZE // 4, y - PLAYER_SIZE // 4 svg += f'' svg += f'' svg += '' # Player name (full name) svg += f'{player}' svg += '' # Draw obstacles for obstacle in game_state.get("obstacles", []): ox, oy = obstacle["x"] * CELL_SIZE, obstacle["y"] * CELL_SIZE svg += f'' svg += f'' svg += '' svg += '' return svg # Streamlit app st.set_page_config(layout="wide", page_title="Enhanced Multiplayer Grid Game") st.title("Enhanced Multiplayer Grid Game") # Controls section st.markdown("### Game Controls") col1, col2, col3, col4, col5 = st.columns([2, 1, 1, 1, 2]) with col1: player_name = st.text_input("Your name", value=st.session_state.player_name) if player_name != st.session_state.player_name: st.session_state.player_name = player_name update_player_position(player_name, 0, 0) with col2: st.color_picker("Choose color", value=st.session_state.player_color, key="player_color") with col3: if st.button("↑"): update_player_position(st.session_state.player_name, 0, -1) if st.button("↓"): update_player_position(st.session_state.player_name, 0, 1) with col4: col4_1, col4_2, col4_3 = st.columns(3) with col4_1: if st.button("←"): update_player_position(st.session_state.player_name, -1, 0) with col4_3: if st.button("→"): update_player_position(st.session_state.player_name, 1, 0) with col5: st.markdown("Use WASD keys for movement") # Create grid grid = st.empty() # JavaScript for smooth movement and player control js_code = """ let keyState = {}; let lastUpdateTime = Date.now(); document.addEventListener('keydown', (e) => { keyState[e.key.toLowerCase()] = true; }); document.addEventListener('keyup', (e) => { keyState[e.key.toLowerCase()] = false; }); function updatePlayerPosition() { const now = Date.now(); const dt = (now - lastUpdateTime) / 1000; // Time difference in seconds lastUpdateTime = now; let vx = 0, vy = 0; if (keyState['w']) vy -= 1; if (keyState['s']) vy += 1; if (keyState['a']) vx -= 1; if (keyState['d']) vx += 1; if (vx !== 0 || vy !== 0) { const magnitude = Math.sqrt(vx * vx + vy * vy); vx /= magnitude; vy /= magnitude; window.Streamlit.setComponentValue({vx: vx * dt, vy: vy * dt}); } } setInterval(updatePlayerPosition, 50); // Update every 50ms for smooth movement function movePlayer(playerId, x, y) { const player = document.getElementById(playerId); if (player) { player.setAttribute('transform', `translate(${x * 50} ${y * 50})`); } } """ st.components.v1.html(f"", height=0) # Initialize obstacles if not present game_state = load_game_state() if "obstacles" not in game_state: game_state["obstacles"] = [ {"x": random.randint(0, GRID_SIZE-1), "y": random.randint(0, GRID_SIZE-1)} for _ in range(5) ] save_game_state(game_state) # Main game loop def main(): while True: # Load current game state game_state = load_game_state() # Generate and display SVG svg = generate_svg(game_state) grid.markdown(svg, unsafe_allow_html=True) # Handle player movement from JavaScript js_result = st.session_state.get('js_result', None) if js_result: vx, vy = js_result.get('vx', 0), js_result.get('vy', 0) update_player_position(st.session_state.player_name, vx, vy) # Update player positions on the client side for player, data in game_state["players"].items(): st_javascript(f"movePlayer('player-{player}', {data['x']}, {data['y']})") # Wait for update interval time.sleep(UPDATE_INTERVAL) st.experimental_rerun() if __name__ == "__main__": main()