Update app.py
Browse files
app.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import streamlit as st
|
2 |
from streamlit.components.v1 import html
|
3 |
|
4 |
-
# Define the p5.js sketch code as a string
|
5 |
p5js_code = """
|
6 |
let gridSize = 40;
|
7 |
let grid = [];
|
@@ -10,6 +10,7 @@ let train = { x: 0, y: 0, dir: 1 };
|
|
10 |
let monsterMode = false;
|
11 |
let currentMonster = 'Godzilla';
|
12 |
let monsterX, monsterY;
|
|
|
13 |
|
14 |
function setup() {
|
15 |
createCanvas(800, 600);
|
@@ -41,15 +42,15 @@ function drawGrid() {
|
|
41 |
for (let j = 0; j < grid[i].length; j++) {
|
42 |
let x = i * gridSize;
|
43 |
let y = j * gridSize;
|
44 |
-
|
45 |
-
|
46 |
-
rect(x, y, gridSize, gridSize);
|
47 |
-
} else {
|
48 |
-
fill(150, 200, 150);
|
49 |
-
rect(x, y, gridSize, gridSize);
|
50 |
-
}
|
51 |
stroke(0);
|
52 |
-
|
|
|
|
|
|
|
|
|
|
|
53 |
}
|
54 |
}
|
55 |
}
|
@@ -58,49 +59,68 @@ function drawBuildings() {
|
|
58 |
buildings.forEach(b => {
|
59 |
let x = b.x * gridSize;
|
60 |
let y = b.y * gridSize;
|
|
|
61 |
fill(b.color);
|
62 |
noStroke();
|
63 |
beginShape();
|
64 |
-
if (b.type === 'Residential') {
|
65 |
-
vertex(x + 10, y + 30); vertex(x + 20, y + 10); vertex(x + 30, y + 30);
|
66 |
-
vertex(x + 25, y + 30); vertex(x + 25, y + 35); vertex(x + 15, y + 35);
|
67 |
-
vertex(x + 15, y + 30);
|
68 |
-
} else if (b.type === 'Commercial') {
|
69 |
-
vertex(x + 10, y + 35); vertex(x + 15, y + 15); vertex(x + 25, y + 15);
|
70 |
-
vertex(x + 30, y + 35);
|
71 |
-
|
72 |
-
|
73 |
-
vertex(x +
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
}
|
75 |
endShape(CLOSE);
|
76 |
});
|
77 |
}
|
78 |
|
79 |
function drawTrain() {
|
|
|
|
|
|
|
80 |
fill(150, 50, 50);
|
81 |
noStroke();
|
82 |
beginShape();
|
83 |
-
|
84 |
-
|
85 |
-
vertex(tx +
|
86 |
-
vertex(tx +
|
87 |
endShape(CLOSE);
|
88 |
train.x += train.dir * 2;
|
89 |
if (train.x > width || train.x < 0) train.dir *= -1;
|
90 |
}
|
91 |
|
92 |
function drawMonster() {
|
93 |
-
|
|
|
94 |
noStroke();
|
95 |
beginShape();
|
96 |
-
if (currentMonster === 'Godzilla') {
|
97 |
-
vertex(monsterX, monsterY + 40); vertex(monsterX +
|
98 |
-
vertex(monsterX +
|
99 |
-
vertex(monsterX +
|
100 |
-
|
101 |
-
vertex(monsterX, monsterY + 40); vertex(monsterX +
|
102 |
-
|
103 |
-
vertex(monsterX +
|
|
|
|
|
|
|
|
|
104 |
}
|
105 |
endShape(CLOSE);
|
106 |
monsterX += random(-5, 5);
|
@@ -113,21 +133,24 @@ function mousePressed() {
|
|
113 |
let i = floor(mouseX / gridSize);
|
114 |
let j = floor(mouseY / gridSize);
|
115 |
if (grid[i][j] === 'empty' && !monsterMode) {
|
116 |
-
let types = ['Residential', 'Commercial', 'Industrial'];
|
117 |
-
let colors = [[0, 200, 0], [0, 0, 200], [200, 200, 0]];
|
118 |
-
let idx = floor(random(
|
119 |
buildings.push({ x: i, y: j, type: types[idx], color: colors[idx] });
|
120 |
grid[i][j] = 'building';
|
121 |
}
|
122 |
}
|
123 |
|
124 |
-
|
125 |
-
monsterMode = !monsterMode;
|
126 |
-
|
|
|
|
|
|
|
127 |
|
128 |
-
window.
|
129 |
-
|
130 |
-
};
|
131 |
"""
|
132 |
|
133 |
# Full HTML content with embedded p5.js
|
@@ -146,30 +169,51 @@ html_content = f"""
|
|
146 |
</head>
|
147 |
<body>
|
148 |
<div id="controls">
|
149 |
-
<button onclick="toggleMonster()">Toggle Monster Mode</button>
|
150 |
<select onchange="setMonster(this.value)">
|
151 |
-
<option value="Godzilla">Godzilla</option>
|
152 |
-
<option value="GiantRobot">Giant Robot</option>
|
153 |
</select>
|
|
|
154 |
</div>
|
155 |
<div id="sketch-holder"></div>
|
156 |
|
157 |
<script>
|
158 |
{p5js_code}
|
159 |
|
160 |
-
function toggleMonster() {{
|
161 |
-
|
162 |
-
}}
|
163 |
-
|
164 |
-
function setMonster(monster) {{
|
165 |
-
window.setMonster(monster);
|
166 |
-
}}
|
167 |
</script>
|
168 |
</body>
|
169 |
</html>
|
170 |
"""
|
171 |
|
172 |
-
# Streamlit app
|
173 |
st.title("SimCity 2000 with Monster Mode")
|
174 |
-
st.write("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
html(html_content, height=700)
|
|
|
1 |
import streamlit as st
|
2 |
from streamlit.components.v1 import html
|
3 |
|
4 |
+
# Define the enhanced p5.js sketch code as a string
|
5 |
p5js_code = """
|
6 |
let gridSize = 40;
|
7 |
let grid = [];
|
|
|
10 |
let monsterMode = false;
|
11 |
let currentMonster = 'Godzilla';
|
12 |
let monsterX, monsterY;
|
13 |
+
let angle = PI / 6; // 3D perspective angle
|
14 |
|
15 |
function setup() {
|
16 |
createCanvas(800, 600);
|
|
|
42 |
for (let j = 0; j < grid[i].length; j++) {
|
43 |
let x = i * gridSize;
|
44 |
let y = j * gridSize;
|
45 |
+
let z = j * gridSize * sin(angle); // 3D depth effect
|
46 |
+
fill(grid[i][j] === 'track' ? 100 : 150, 200, 150);
|
|
|
|
|
|
|
|
|
|
|
47 |
stroke(0);
|
48 |
+
beginShape();
|
49 |
+
vertex(x, y - z);
|
50 |
+
vertex(x + gridSize, y - z);
|
51 |
+
vertex(x + gridSize * cos(angle), y + gridSize * sin(angle) - z);
|
52 |
+
vertex(x + gridSize * (1 - cos(angle)), y + gridSize * sin(angle) - z);
|
53 |
+
endShape(CLOSE);
|
54 |
}
|
55 |
}
|
56 |
}
|
|
|
59 |
buildings.forEach(b => {
|
60 |
let x = b.x * gridSize;
|
61 |
let y = b.y * gridSize;
|
62 |
+
let z = b.y * gridSize * sin(angle);
|
63 |
fill(b.color);
|
64 |
noStroke();
|
65 |
beginShape();
|
66 |
+
if (b.type === 'Residential') { // House with chimney
|
67 |
+
vertex(x + 10, y + 30 - z); vertex(x + 20, y + 10 - z); vertex(x + 30, y + 30 - z);
|
68 |
+
vertex(x + 25, y + 30 - z); vertex(x + 25, y + 35 - z); vertex(x + 15, y + 35 - z);
|
69 |
+
vertex(x + 15, y + 30 - z); vertex(x + 17, y + 25 - z); vertex(x + 23, y + 25 - z);
|
70 |
+
} else if (b.type === 'Commercial') { // Skyscraper with windows
|
71 |
+
vertex(x + 10, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z);
|
72 |
+
vertex(x + 30, y + 35 - z); vertex(x + 27, y + 35 - z); vertex(x + 27, y + 20 - z);
|
73 |
+
vertex(x + 13, y + 20 - z); vertex(x + 13, y + 35 - z);
|
74 |
+
} else if (b.type === 'Industrial') { // Factory with smokestack
|
75 |
+
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 20 - z); vertex(x + 25, y + 20 - z);
|
76 |
+
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 15 - z);
|
77 |
+
vertex(x + 33, y + 15 - z); vertex(x + 33, y + 35 - z);
|
78 |
+
} else if (b.type === 'School') { // School with flagpole
|
79 |
+
vertex(x + 5, y + 35 - z); vertex(x + 10, y + 20 - z); vertex(x + 30, y + 20 - z);
|
80 |
+
vertex(x + 35, y + 35 - z); vertex(x + 25, y + 35 - z); vertex(x + 25, y + 10 - z);
|
81 |
+
vertex(x + 27, y + 10 - z); vertex(x + 27, y + 35 - z);
|
82 |
+
} else if (b.type === 'PowerPlant') { // Power plant with turbines
|
83 |
+
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z);
|
84 |
+
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 25 - z);
|
85 |
+
vertex(x + 20, y + 25 - z); vertex(x + 20, y + 35 - z);
|
86 |
}
|
87 |
endShape(CLOSE);
|
88 |
});
|
89 |
}
|
90 |
|
91 |
function drawTrain() {
|
92 |
+
let tx = train.x;
|
93 |
+
let ty = train.y;
|
94 |
+
let tz = train.y * sin(angle);
|
95 |
fill(150, 50, 50);
|
96 |
noStroke();
|
97 |
beginShape();
|
98 |
+
vertex(tx + 10, ty + 10 - tz); vertex(tx + 30, ty + 10 - tz); vertex(tx + 35, ty + 20 - tz);
|
99 |
+
vertex(tx + 30, ty + 30 - tz); vertex(tx + 10, ty + 30 - tz); vertex(tx + 5, ty + 20 - tz);
|
100 |
+
vertex(tx + 15, ty + 20 - tz); vertex(tx + 15, ty + 15 - tz); vertex(tx + 25, ty + 15 - tz);
|
101 |
+
vertex(tx + 25, ty + 20 - tz);
|
102 |
endShape(CLOSE);
|
103 |
train.x += train.dir * 2;
|
104 |
if (train.x > width || train.x < 0) train.dir *= -1;
|
105 |
}
|
106 |
|
107 |
function drawMonster() {
|
108 |
+
let mz = monsterY * sin(angle);
|
109 |
+
fill(255, 0, 0);
|
110 |
noStroke();
|
111 |
beginShape();
|
112 |
+
if (currentMonster === 'Godzilla') { // Detailed Godzilla with spines
|
113 |
+
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz);
|
114 |
+
vertex(monsterX + 20, monsterY - mz); vertex(monsterX + 30, monsterY + 20 - mz);
|
115 |
+
vertex(monsterX + 40, monsterY + 40 - mz); vertex(monsterX + 35, monsterY + 50 - mz);
|
116 |
+
vertex(monsterX + 25, monsterY + 60 - mz); vertex(monsterX + 15, monsterY + 50 - mz);
|
117 |
+
vertex(monsterX + 20, monsterY + 40 - mz); vertex(monsterX + 25, monsterY + 30 - mz);
|
118 |
+
} else if (currentMonster === 'GiantRobot') { // Detailed Robot with joints
|
119 |
+
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz);
|
120 |
+
vertex(monsterX + 15, monsterY - mz); vertex(monsterX + 25, monsterY - mz);
|
121 |
+
vertex(monsterX + 30, monsterY + 20 - mz); vertex(monsterX + 40, monsterY + 40 - mz);
|
122 |
+
vertex(monsterX + 35, monsterY + 50 - mz); vertex(monsterX + 20, monsterY + 60 - mz);
|
123 |
+
vertex(monsterX + 5, monsterY + 50 - mz); vertex(monsterX + 15, monsterY + 30 - mz);
|
124 |
}
|
125 |
endShape(CLOSE);
|
126 |
monsterX += random(-5, 5);
|
|
|
133 |
let i = floor(mouseX / gridSize);
|
134 |
let j = floor(mouseY / gridSize);
|
135 |
if (grid[i][j] === 'empty' && !monsterMode) {
|
136 |
+
let types = ['Residential', 'Commercial', 'Industrial', 'School', 'PowerPlant'];
|
137 |
+
let colors = [[0, 200, 0], [0, 0, 200], [200, 200, 0], [200, 0, 200], [100, 100, 100]];
|
138 |
+
let idx = floor(random(5));
|
139 |
buildings.push({ x: i, y: j, type: types[idx], color: colors[idx] });
|
140 |
grid[i][j] = 'building';
|
141 |
}
|
142 |
}
|
143 |
|
144 |
+
function keyPressed() {
|
145 |
+
if (key === 'm' || key === 'M') monsterMode = !monsterMode;
|
146 |
+
if (key === 'g' || key === 'G') currentMonster = 'Godzilla';
|
147 |
+
if (key === 'r' || key === 'R') currentMonster = 'GiantRobot';
|
148 |
+
if (key === 't' || key === 'T') train.dir *= -1;
|
149 |
+
}
|
150 |
|
151 |
+
window.toggleMonsterMode = function() { monsterMode = !monsterMode; };
|
152 |
+
window.setMonster = function(monster) { currentMonster = monster; };
|
153 |
+
window.reverseTrain = function() { train.dir *= -1; };
|
154 |
"""
|
155 |
|
156 |
# Full HTML content with embedded p5.js
|
|
|
169 |
</head>
|
170 |
<body>
|
171 |
<div id="controls">
|
172 |
+
<button onclick="toggleMonster()">Toggle Monster Mode (M)</button>
|
173 |
<select onchange="setMonster(this.value)">
|
174 |
+
<option value="Godzilla">Godzilla (G)</option>
|
175 |
+
<option value="GiantRobot">Giant Robot (R)</option>
|
176 |
</select>
|
177 |
+
<button onclick="reverseTrain()">Reverse Train (T)</button>
|
178 |
</div>
|
179 |
<div id="sketch-holder"></div>
|
180 |
|
181 |
<script>
|
182 |
{p5js_code}
|
183 |
|
184 |
+
function toggleMonster() {{ window.toggleMonsterMode(); }}
|
185 |
+
function setMonster(monster) {{ window.setMonster(monster); }}
|
186 |
+
function reverseTrain() {{ window.reverseTrain(); }}
|
|
|
|
|
|
|
|
|
187 |
</script>
|
188 |
</body>
|
189 |
</html>
|
190 |
"""
|
191 |
|
192 |
+
# Streamlit app with sidebar
|
193 |
st.title("SimCity 2000 with Monster Mode")
|
194 |
+
st.write("Click to place buildings. Use keys: M (monster), G (Godzilla), R (Robot), T (train reverse).")
|
195 |
+
|
196 |
+
# Sidebar with emoji-grouped controls
|
197 |
+
with st.sidebar:
|
198 |
+
st.header("Controls")
|
199 |
+
|
200 |
+
# Building Controls
|
201 |
+
st.subheader("🏡 Buildings")
|
202 |
+
st.write("Click on the grid to randomly place buildings (Residential, Commercial, Industrial, School, Power Plant).")
|
203 |
+
|
204 |
+
# Monster Controls
|
205 |
+
st.subheader("👾 Monsters")
|
206 |
+
if st.button("Toggle Monster Mode (M)", key="monster_toggle"):
|
207 |
+
st.markdown('<script>toggleMonster()</script>', unsafe_allow_html=True)
|
208 |
+
monster_choice = st.selectbox("Choose Monster", ["Godzilla (G)", "Giant Robot (R)"], index=0)
|
209 |
+
if monster_choice.startswith("Godzilla"):
|
210 |
+
st.markdown('<script>setMonster("Godzilla")</script>', unsafe_allow_html=True)
|
211 |
+
else:
|
212 |
+
st.markdown('<script>setMonster("GiantRobot")</script>', unsafe_allow_html=True)
|
213 |
+
|
214 |
+
# Train Controls
|
215 |
+
st.subheader("🚂 Train")
|
216 |
+
if st.button("Reverse Train (T)", key="train_reverse"):
|
217 |
+
st.markdown('<script>reverseTrain()</script>', unsafe_allow_html=True)
|
218 |
+
|
219 |
html(html_content, height=700)
|