Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,7 +1,62 @@
|
|
1 |
import streamlit as st
|
2 |
from streamlit.components.v1 import html
|
|
|
|
|
3 |
|
4 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
p5js_code = """
|
6 |
let gridSize = 40;
|
7 |
let grid = [];
|
@@ -10,25 +65,21 @@ let train = { x: 0, y: 0, dir: 1 };
|
|
10 |
let monsterMode = false;
|
11 |
let currentMonster = 'Godzilla';
|
12 |
let monsterX, monsterY;
|
13 |
-
let angle = PI / 6;
|
14 |
let debugText = "Loading...";
|
|
|
15 |
|
16 |
function setup() {
|
17 |
createCanvas(800, 600);
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
}
|
27 |
-
train.x = 0;
|
28 |
-
train.y = 5 * gridSize;
|
29 |
-
monsterX = width / 2;
|
30 |
-
monsterY = height / 2;
|
31 |
-
debugText = "Setup complete. Click to build, use M/G/R/T keys.";
|
32 |
}
|
33 |
|
34 |
function draw() {
|
@@ -38,7 +89,8 @@ function draw() {
|
|
38 |
drawTrain();
|
39 |
if (monsterMode) drawMonster();
|
40 |
fill(0);
|
41 |
-
|
|
|
42 |
}
|
43 |
|
44 |
function drawGrid() {
|
@@ -55,6 +107,10 @@ function drawGrid() {
|
|
55 |
vertex(x + gridSize * cos(angle), y + gridSize * sin(angle) - z);
|
56 |
vertex(x + gridSize * (1 - cos(angle)), y + gridSize * sin(angle) - z);
|
57 |
endShape(CLOSE);
|
|
|
|
|
|
|
|
|
58 |
}
|
59 |
}
|
60 |
}
|
@@ -138,12 +194,21 @@ function mousePressed() {
|
|
138 |
let j = floor(mouseY / gridSize);
|
139 |
if (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length) {
|
140 |
if (grid[i][j] === 'empty' && !monsterMode) {
|
141 |
-
let
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
}
|
148 |
}
|
149 |
}
|
@@ -152,36 +217,81 @@ function keyPressed() {
|
|
152 |
if (key === 'm' || key === 'M') {
|
153 |
monsterMode = !monsterMode;
|
154 |
debugText = `Monster Mode: ${monsterMode}`;
|
|
|
155 |
}
|
156 |
if (key === 'g' || key === 'G') {
|
157 |
currentMonster = 'Godzilla';
|
158 |
debugText = "Monster set to Godzilla";
|
|
|
159 |
}
|
160 |
if (key === 'r' || key === 'R') {
|
161 |
currentMonster = 'GiantRobot';
|
162 |
debugText = "Monster set to Giant Robot";
|
|
|
163 |
}
|
164 |
if (key === 't' || key === 'T') {
|
165 |
train.dir *= -1;
|
166 |
debugText = "Train direction reversed";
|
|
|
167 |
}
|
168 |
}
|
169 |
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
}
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
|
|
|
|
|
|
|
|
182 |
"""
|
183 |
|
184 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
html_content = f"""
|
186 |
<!DOCTYPE html>
|
187 |
<html>
|
@@ -205,6 +315,15 @@ html_content = f"""
|
|
205 |
<button onclick="reverseTrain()">Reverse Train (T)</button>
|
206 |
</div>
|
207 |
<div id="sketch-holder"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
|
209 |
<script>
|
210 |
{p5js_code}
|
@@ -217,18 +336,23 @@ html_content = f"""
|
|
217 |
</html>
|
218 |
"""
|
219 |
|
220 |
-
# Streamlit app
|
221 |
-
st.title("SimCity 2000
|
222 |
-
st.write("Click to place
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
st.
|
230 |
-
st.
|
231 |
-
st.
|
232 |
-
st.
|
233 |
-
|
234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
from streamlit.components.v1 import html
|
3 |
+
import random
|
4 |
+
import json
|
5 |
|
6 |
+
# Shared state for multiplayer
|
7 |
+
if 'game_state' not in st.session_state:
|
8 |
+
st.session_state.game_state = {
|
9 |
+
'grid': [['empty' for _ in range(15)] for _ in range(20)],
|
10 |
+
'buildings': [],
|
11 |
+
'players': {},
|
12 |
+
'train': {'x': 0, 'y': 5 * 40, 'dir': 1},
|
13 |
+
'monster_mode': False,
|
14 |
+
'current_monster': 'Godzilla',
|
15 |
+
'monster_x': 400,
|
16 |
+
'monster_y': 300
|
17 |
+
}
|
18 |
+
|
19 |
+
# Random player names
|
20 |
+
random_names = [
|
21 |
+
"SkyWalker", "ForestRanger", "CityBuilder", "MonsterTamer", "RailMaster",
|
22 |
+
"PlantWhisperer", "UrbanWizard", "NatureSage", "GridLord", "EcoWarrior"
|
23 |
+
]
|
24 |
+
|
25 |
+
# Story plot generator beginnings
|
26 |
+
story_beginnings = [
|
27 |
+
("🏠", "In a bustling city nestled between towering mountains,"),
|
28 |
+
("🏃", "A lone figure darted through the streets as the sun began to set,"),
|
29 |
+
("🧠", "Thoughts raced through their mind, planning the next move,"),
|
30 |
+
("😲", "Shock rippled through the crowd as the ground trembled,"),
|
31 |
+
("💬", "'We need to act now!' someone shouted over the chaos,"),
|
32 |
+
("🔍", "A closer look revealed hidden secrets in the city’s core,"),
|
33 |
+
("🎬", "A train screeched into the station, unloading its mysteries,"),
|
34 |
+
("🏞️", "Lush greenery framed the skyline, a stark contrast to the urban sprawl,"),
|
35 |
+
("⏩", "Events unfolded rapidly, leaving no time to breathe,"),
|
36 |
+
("👁️", "The horizon glowed with an eerie, captivating light,"),
|
37 |
+
("👂", "Whispers of wind carried tales of forgotten lands,"),
|
38 |
+
("🗣️", "A juicy rumor spread like wildfire among the citizens,"),
|
39 |
+
("🤸", "With a simple leap, the adventure began anew,"),
|
40 |
+
("🧩", "The essentials of survival lay scattered across the grid,"),
|
41 |
+
("🚶", "A slow walk revealed the city’s heartbeat,"),
|
42 |
+
("🌊", "A journey unfolded, waves of change crashing in,"),
|
43 |
+
("🎯", "Focus sharpened as the goal came into view,"),
|
44 |
+
("🤔", "Neurotic thoughts plagued the planner’s restless mind,")
|
45 |
+
]
|
46 |
+
|
47 |
+
# Witty nature labels
|
48 |
+
nature_labels = [
|
49 |
+
"Whispering Winds", "Sassy Sunlight", "Grumpy Gravel", "Chatty Clouds",
|
50 |
+
"Pensive Pines", "Witty Wetlands", "Sneaky Streams", "Bouncy Breezes",
|
51 |
+
"Daring Dewdrops", "Cheeky Crags"
|
52 |
+
]
|
53 |
+
|
54 |
+
# Emoji palette
|
55 |
+
plants = ["🌱", "🌲", "🌳", "🌴", "🌵", "🌾", "🌿", "🍀", "🍁", "🍂"]
|
56 |
+
buildings = ["🏠", "🏡", "🏢", "🏣", "🏤", "🏥", "🏦", "🏨", "🏩", "🏪"]
|
57 |
+
creatures = ["🐾", "🐱", "🐶", "🐭", "🐰", "🦊", "🐻", "🐷", "🐮", "🐸"]
|
58 |
+
|
59 |
+
# p5.js code
|
60 |
p5js_code = """
|
61 |
let gridSize = 40;
|
62 |
let grid = [];
|
|
|
65 |
let monsterMode = false;
|
66 |
let currentMonster = 'Godzilla';
|
67 |
let monsterX, monsterY;
|
68 |
+
let angle = PI / 6;
|
69 |
let debugText = "Loading...";
|
70 |
+
let playerId = null;
|
71 |
|
72 |
function setup() {
|
73 |
createCanvas(800, 600);
|
74 |
+
grid = JSON.parse(document.getElementById('game_state').innerHTML);
|
75 |
+
buildings = JSON.parse(document.getElementById('buildings').innerHTML);
|
76 |
+
train = JSON.parse(document.getElementById('train').innerHTML);
|
77 |
+
monsterMode = JSON.parse(document.getElementById('monster_mode').innerHTML);
|
78 |
+
currentMonster = document.getElementById('current_monster').innerHTML;
|
79 |
+
monsterX = parseInt(document.getElementById('monster_x').innerHTML);
|
80 |
+
monsterY = parseInt(document.getElementById('monster_y').innerHTML);
|
81 |
+
playerId = document.getElementById('player_id').innerHTML;
|
82 |
+
debugText = `Player ${playerId} joined. Click to build!`;
|
|
|
|
|
|
|
|
|
|
|
83 |
}
|
84 |
|
85 |
function draw() {
|
|
|
89 |
drawTrain();
|
90 |
if (monsterMode) drawMonster();
|
91 |
fill(0);
|
92 |
+
textSize(12);
|
93 |
+
text(debugText, 10, 20);
|
94 |
}
|
95 |
|
96 |
function drawGrid() {
|
|
|
107 |
vertex(x + gridSize * cos(angle), y + gridSize * sin(angle) - z);
|
108 |
vertex(x + gridSize * (1 - cos(angle)), y + gridSize * sin(angle) - z);
|
109 |
endShape(CLOSE);
|
110 |
+
if (grid[i][j] !== 'empty' && grid[i][j] !== 'track') {
|
111 |
+
textSize(20);
|
112 |
+
text(grid[i][j], x + 10, y + 20 - z);
|
113 |
+
}
|
114 |
}
|
115 |
}
|
116 |
}
|
|
|
194 |
let j = floor(mouseY / gridSize);
|
195 |
if (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length) {
|
196 |
if (grid[i][j] === 'empty' && !monsterMode) {
|
197 |
+
let selectedEmoji = document.getElementById('selected_emoji').innerHTML;
|
198 |
+
if (selectedEmoji.startsWith('🏠') || selectedEmoji.startsWith('🏡') || selectedEmoji.startsWith('🏢') ||
|
199 |
+
selectedEmoji.startsWith('🏣') || selectedEmoji.startsWith('🏤') || selectedEmoji.startsWith('🏥') ||
|
200 |
+
selectedEmoji.startsWith('🏦') || selectedEmoji.startsWith('🏨') || selectedEmoji.startsWith('🏩') ||
|
201 |
+
selectedEmoji.startsWith('🏪')) {
|
202 |
+
let types = ['Residential', 'Commercial', 'Industrial', 'School', 'PowerPlant'];
|
203 |
+
let colors = [[0, 200, 0], [0, 0, 200], [200, 200, 0], [200, 0, 200], [100, 100, 100]];
|
204 |
+
let idx = floor(random(5));
|
205 |
+
buildings.push({ x: i, y: j, type: types[idx], color: colors[idx] });
|
206 |
+
grid[i][j] = 'building';
|
207 |
+
} else {
|
208 |
+
grid[i][j] = selectedEmoji;
|
209 |
+
}
|
210 |
+
debugText = `Player ${playerId} placed ${selectedEmoji} at (${i}, ${j})`;
|
211 |
+
updateState();
|
212 |
}
|
213 |
}
|
214 |
}
|
|
|
217 |
if (key === 'm' || key === 'M') {
|
218 |
monsterMode = !monsterMode;
|
219 |
debugText = `Monster Mode: ${monsterMode}`;
|
220 |
+
updateState();
|
221 |
}
|
222 |
if (key === 'g' || key === 'G') {
|
223 |
currentMonster = 'Godzilla';
|
224 |
debugText = "Monster set to Godzilla";
|
225 |
+
updateState();
|
226 |
}
|
227 |
if (key === 'r' || key === 'R') {
|
228 |
currentMonster = 'GiantRobot';
|
229 |
debugText = "Monster set to Giant Robot";
|
230 |
+
updateState();
|
231 |
}
|
232 |
if (key === 't' || key === 'T') {
|
233 |
train.dir *= -1;
|
234 |
debugText = "Train direction reversed";
|
235 |
+
updateState();
|
236 |
}
|
237 |
}
|
238 |
|
239 |
+
function updateState() {
|
240 |
+
fetch('/update_state', {
|
241 |
+
method: 'POST',
|
242 |
+
headers: { 'Content-Type': 'application/json' },
|
243 |
+
body: JSON.stringify({
|
244 |
+
grid: grid,
|
245 |
+
buildings: buildings,
|
246 |
+
train: train,
|
247 |
+
monster_mode: monsterMode,
|
248 |
+
current_monster: currentMonster,
|
249 |
+
monster_x: monsterX,
|
250 |
+
monster_y: monsterY,
|
251 |
+
player_id: playerId
|
252 |
+
})
|
253 |
+
});
|
254 |
+
}
|
255 |
"""
|
256 |
|
257 |
+
# Player selection
|
258 |
+
if 'player_id' not in st.session_state:
|
259 |
+
st.session_state.player_id = random.choice(random_names)
|
260 |
+
player_id = st.sidebar.selectbox("Choose Player", random_names, index=random_names.index(st.session_state.player_id))
|
261 |
+
st.session_state.player_id = player_id
|
262 |
+
|
263 |
+
# Story plot generator
|
264 |
+
st.sidebar.subheader("📜 Story Plot")
|
265 |
+
story_emoji, story_text = random.choice(story_beginnings)
|
266 |
+
st.sidebar.write(f"{story_emoji} {story_text} {random.choice(nature_labels)} stirred the scene.")
|
267 |
+
|
268 |
+
# Palette grid
|
269 |
+
st.sidebar.subheader("🎨 Palette")
|
270 |
+
cols = st.sidebar.columns(5)
|
271 |
+
selected_emoji = st.session_state.get('selected_emoji', plants[0])
|
272 |
+
|
273 |
+
for i, emoji in enumerate(plants + buildings + creatures):
|
274 |
+
col = cols[i % 5]
|
275 |
+
if col.button(emoji, key=f"emoji_{i}"):
|
276 |
+
st.session_state.selected_emoji = emoji
|
277 |
+
selected_emoji = emoji
|
278 |
+
|
279 |
+
# Scoreboard
|
280 |
+
if player_id not in st.session_state.game_state['players']:
|
281 |
+
st.session_state.game_state['players'][player_id] = 0
|
282 |
+
for b in st.session_state.game_state['buildings']:
|
283 |
+
if b.get('player') == player_id:
|
284 |
+
st.session_state.game_state['players'][player_id] += 1
|
285 |
+
for i in range(len(st.session_state.game_state['grid'])):
|
286 |
+
for j in range(len(st.session_state.game_state['grid'][i])):
|
287 |
+
if st.session_state.game_state['grid'][i][j] not in ['empty', 'track', 'building']:
|
288 |
+
if st.session_state.game_state['grid'][i][j] in plants + creatures:
|
289 |
+
st.session_state.game_state['players'][player_id] += 1
|
290 |
+
st.sidebar.subheader("🏆 Scores")
|
291 |
+
for p, s in st.session_state.game_state['players'].items():
|
292 |
+
st.sidebar.write(f"{p}: {s}")
|
293 |
+
|
294 |
+
# HTML content
|
295 |
html_content = f"""
|
296 |
<!DOCTYPE html>
|
297 |
<html>
|
|
|
315 |
<button onclick="reverseTrain()">Reverse Train (T)</button>
|
316 |
</div>
|
317 |
<div id="sketch-holder"></div>
|
318 |
+
<div id="game_state" style="display:none">{json.dumps(st.session_state.game_state['grid'])}</div>
|
319 |
+
<div id="buildings" style="display:none">{json.dumps(st.session_state.game_state['buildings'])}</div>
|
320 |
+
<div id="train" style="display:none">{json.dumps(st.session_state.game_state['train'])}</div>
|
321 |
+
<div id="monster_mode" style="display:none">{json.dumps(st.session_state.game_state['monster_mode'])}</div>
|
322 |
+
<div id="current_monster" style="display:none">{st.session_state.game_state['current_monster']}</div>
|
323 |
+
<div id="monster_x" style="display:none">{st.session_state.game_state['monster_x']}</div>
|
324 |
+
<div id="monster_y" style="display:none">{st.session_state.game_state['monster_y']}</div>
|
325 |
+
<div id="player_id" style="display:none">{player_id}</div>
|
326 |
+
<div id="selected_emoji" style="display:none">{selected_emoji}</div>
|
327 |
|
328 |
<script>
|
329 |
{p5js_code}
|
|
|
336 |
</html>
|
337 |
"""
|
338 |
|
339 |
+
# Streamlit app
|
340 |
+
st.title("SimCity 2000 Multiplayer Adventure")
|
341 |
+
st.write(f"Player: {player_id}. Click to place {selected_emoji}. Use M/G/R/T keys.")
|
342 |
+
|
343 |
+
html(html_content, height=700)
|
344 |
+
|
345 |
+
# Note: Streamlit doesn't natively support POST endpoints, so this is a placeholder for state update logic
|
346 |
+
# In a real app, you'd need a backend (e.g., Flask) to handle POST requests for true multiplayer sync
|
347 |
+
def update_state(data):
|
348 |
+
st.session_state.game_state['grid'] = data['grid']
|
349 |
+
st.session_state.game_state['buildings'] = data['buildings']
|
350 |
+
st.session_state.game_state['train'] = data['train']
|
351 |
+
st.session_state.game_state['monster_mode'] = data['monster_mode']
|
352 |
+
st.session_state.game_state['current_monster'] = data['current_monster']
|
353 |
+
st.session_state.game_state['monster_x'] = data['monster_x']
|
354 |
+
st.session_state.game_state['monster_y'] = data['monster_y']
|
355 |
+
if data['player_id'] not in st.session_state.game_state['players']:
|
356 |
+
st.session_state.game_state['players'][data['player_id']] = 0
|
357 |
+
st.session_state.game_state['players'][data['player_id']] += 1
|
358 |
+
st.rerun()
|