File size: 9,214 Bytes
2fb15db
 
 
 
 
b0a6ae6
6e7673f
2fb15db
 
 
 
b07c26b
2fb15db
b0a6ae6
 
b07c26b
 
 
2fb15db
0635fb7
 
 
2fb15db
 
0635fb7
2fb15db
 
b0a6ae6
 
2fb15db
 
 
 
 
 
6e7673f
2fb15db
 
 
 
 
 
b07c26b
 
2fb15db
 
b07c26b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2fb15db
 
b07c26b
 
 
 
 
 
 
2fb15db
 
b0a6ae6
 
 
 
6e7673f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0a6ae6
 
6e7673f
b0a6ae6
6e7673f
b0a6ae6
 
 
6e7673f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0a6ae6
6e7673f
0635fb7
 
6e7673f
 
 
 
 
 
 
 
 
b0a6ae6
 
 
 
2fb15db
b07c26b
b0a6ae6
2fb15db
0635fb7
 
 
 
b0a6ae6
 
 
 
b07c26b
b0a6ae6
 
0635fb7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2fb15db
 
 
 
b07c26b
b0a6ae6
b07c26b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6e7673f
 
b07c26b
 
6e7673f
 
b07c26b
b0a6ae6
 
 
 
 
 
6e7673f
 
 
 
 
 
 
 
 
2fb15db
b07c26b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0ff9616
b07c26b
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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'<svg width="{GRID_SIZE * CELL_SIZE}" height="{GRID_SIZE * CELL_SIZE}" xmlns="http://www.w3.org/2000/svg">'
    
    # Define gradients
    svg += '''
    <defs>
        <linearGradient id="grid-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" style="stop-color:#f0f0f0;stop-opacity:1" />
            <stop offset="100%" style="stop-color:#d0d0d0;stop-opacity:1" />
        </linearGradient>
        <radialGradient id="player-gradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
            <stop offset="0%" style="stop-color:#ffffff;stop-opacity:0.8" />
            <stop offset="100%" style="stop-color:#000000;stop-opacity:0.1" />
        </radialGradient>
    </defs>
    '''
    
    # Draw grid with gradient
    svg += f'<rect width="{GRID_SIZE * CELL_SIZE}" height="{GRID_SIZE * CELL_SIZE}" fill="url(#grid-gradient)" />'
    for i in range(GRID_SIZE):
        for j in range(GRID_SIZE):
            svg += f'<rect x="{i * CELL_SIZE}" y="{j * CELL_SIZE}" width="{CELL_SIZE}" height="{CELL_SIZE}" fill="none" stroke="#a0a0a0" stroke-width="1" />'
    
    # 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'<g id="{player_id}">'
        
        # 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'<polygon points="{points}" fill="{color}" stroke="black" stroke-width="2">'
        svg += f'<animate attributeName="transform" attributeType="XML" type="rotate" from="0 {x} {y}" to="360 {x} {y}" dur="10s" repeatCount="indefinite" />'
        svg += '</polygon>'
        
        # Player eye (circle)
        eye_x, eye_y = x + PLAYER_SIZE // 4, y - PLAYER_SIZE // 4
        svg += f'<circle cx="{eye_x}" cy="{eye_y}" r="{PLAYER_SIZE // 8}" fill="white" stroke="black" stroke-width="1">'
        svg += f'<animate attributeName="r" values="{PLAYER_SIZE // 8};{PLAYER_SIZE // 10};{PLAYER_SIZE // 8}" dur="3s" repeatCount="indefinite" />'
        svg += '</circle>'
        
        # Player name (full name)
        svg += f'<text x="{x}" y="{y + PLAYER_SIZE // 2 + 15}" text-anchor="middle" fill="black" font-size="10" font-weight="bold">{player}</text>'
        
        svg += '</g>'
    
    # Draw obstacles
    for obstacle in game_state.get("obstacles", []):
        ox, oy = obstacle["x"] * CELL_SIZE, obstacle["y"] * CELL_SIZE
        svg += f'<rect x="{ox}" y="{oy}" width="{CELL_SIZE}" height="{CELL_SIZE}" fill="url(#player-gradient)" stroke="black" stroke-width="2">'
        svg += f'<animate attributeName="opacity" values="0.7;0.9;0.7" dur="{random.uniform(2,5)}s" repeatCount="indefinite" />'
        svg += '</rect>'
    
    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"<script>{js_code}</script>", 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.rerun()

if __name__ == "__main__":
    main()