awacke1's picture
Update app.py
45f7f6d verified
raw
history blame
8.44 kB
import streamlit as st
from streamlit.components.v1 import html
# Define the enhanced p5.js sketch code as a string
p5js_code = """
let gridSize = 40;
let grid = [];
let buildings = [];
let train = { x: 0, y: 0, dir: 1 };
let monsterMode = false;
let currentMonster = 'Godzilla';
let monsterX, monsterY;
let angle = PI / 6; // 3D perspective angle
function setup() {
createCanvas(800, 600);
for (let i = 0; i < width / gridSize; i++) {
grid[i] = [];
for (let j = 0; j < height / gridSize; j++) {
grid[i][j] = 'empty';
}
}
for (let i = 0; i < width / gridSize; i++) {
grid[i][5] = 'track';
}
train.x = 0;
train.y = 5 * gridSize;
monsterX = width / 2;
monsterY = height / 2;
}
function draw() {
background(220);
drawGrid();
drawBuildings();
drawTrain();
if (monsterMode) drawMonster();
}
function drawGrid() {
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[i].length; j++) {
let x = i * gridSize;
let y = j * gridSize;
let z = j * gridSize * sin(angle); // 3D depth effect
fill(grid[i][j] === 'track' ? 100 : 150, 200, 150);
stroke(0);
beginShape();
vertex(x, y - z);
vertex(x + gridSize, y - z);
vertex(x + gridSize * cos(angle), y + gridSize * sin(angle) - z);
vertex(x + gridSize * (1 - cos(angle)), y + gridSize * sin(angle) - z);
endShape(CLOSE);
}
}
}
function drawBuildings() {
buildings.forEach(b => {
let x = b.x * gridSize;
let y = b.y * gridSize;
let z = b.y * gridSize * sin(angle);
fill(b.color);
noStroke();
beginShape();
if (b.type === 'Residential') { // House with chimney
vertex(x + 10, y + 30 - z); vertex(x + 20, y + 10 - z); vertex(x + 30, y + 30 - z);
vertex(x + 25, y + 30 - z); vertex(x + 25, y + 35 - z); vertex(x + 15, y + 35 - z);
vertex(x + 15, y + 30 - z); vertex(x + 17, y + 25 - z); vertex(x + 23, y + 25 - z);
} else if (b.type === 'Commercial') { // Skyscraper with windows
vertex(x + 10, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z);
vertex(x + 30, y + 35 - z); vertex(x + 27, y + 35 - z); vertex(x + 27, y + 20 - z);
vertex(x + 13, y + 20 - z); vertex(x + 13, y + 35 - z);
} else if (b.type === 'Industrial') { // Factory with smokestack
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 20 - z); vertex(x + 25, y + 20 - z);
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 15 - z);
vertex(x + 33, y + 15 - z); vertex(x + 33, y + 35 - z);
} else if (b.type === 'School') { // School with flagpole
vertex(x + 5, y + 35 - z); vertex(x + 10, y + 20 - z); vertex(x + 30, y + 20 - z);
vertex(x + 35, y + 35 - z); vertex(x + 25, y + 35 - z); vertex(x + 25, y + 10 - z);
vertex(x + 27, y + 10 - z); vertex(x + 27, y + 35 - z);
} else if (b.type === 'PowerPlant') { // Power plant with turbines
vertex(x + 5, y + 35 - z); vertex(x + 15, y + 15 - z); vertex(x + 25, y + 15 - z);
vertex(x + 35, y + 35 - z); vertex(x + 30, y + 35 - z); vertex(x + 30, y + 25 - z);
vertex(x + 20, y + 25 - z); vertex(x + 20, y + 35 - z);
}
endShape(CLOSE);
});
}
function drawTrain() {
let tx = train.x;
let ty = train.y;
let tz = train.y * sin(angle);
fill(150, 50, 50);
noStroke();
beginShape();
vertex(tx + 10, ty + 10 - tz); vertex(tx + 30, ty + 10 - tz); vertex(tx + 35, ty + 20 - tz);
vertex(tx + 30, ty + 30 - tz); vertex(tx + 10, ty + 30 - tz); vertex(tx + 5, ty + 20 - tz);
vertex(tx + 15, ty + 20 - tz); vertex(tx + 15, ty + 15 - tz); vertex(tx + 25, ty + 15 - tz);
vertex(tx + 25, ty + 20 - tz);
endShape(CLOSE);
train.x += train.dir * 2;
if (train.x > width || train.x < 0) train.dir *= -1;
}
function drawMonster() {
let mz = monsterY * sin(angle);
fill(255, 0, 0);
noStroke();
beginShape();
if (currentMonster === 'Godzilla') { // Detailed Godzilla with spines
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz);
vertex(monsterX + 20, monsterY - mz); vertex(monsterX + 30, monsterY + 20 - mz);
vertex(monsterX + 40, monsterY + 40 - mz); vertex(monsterX + 35, monsterY + 50 - mz);
vertex(monsterX + 25, monsterY + 60 - mz); vertex(monsterX + 15, monsterY + 50 - mz);
vertex(monsterX + 20, monsterY + 40 - mz); vertex(monsterX + 25, monsterY + 30 - mz);
} else if (currentMonster === 'GiantRobot') { // Detailed Robot with joints
vertex(monsterX, monsterY + 40 - mz); vertex(monsterX + 10, monsterY + 20 - mz);
vertex(monsterX + 15, monsterY - mz); vertex(monsterX + 25, monsterY - mz);
vertex(monsterX + 30, monsterY + 20 - mz); vertex(monsterX + 40, monsterY + 40 - mz);
vertex(monsterX + 35, monsterY + 50 - mz); vertex(monsterX + 20, monsterY + 60 - mz);
vertex(monsterX + 5, monsterY + 50 - mz); vertex(monsterX + 15, monsterY + 30 - mz);
}
endShape(CLOSE);
monsterX += random(-5, 5);
monsterY += random(-5, 5);
monsterX = constrain(monsterX, 0, width);
monsterY = constrain(monsterY, 0, height);
}
function mousePressed() {
let i = floor(mouseX / gridSize);
let j = floor(mouseY / gridSize);
if (grid[i][j] === 'empty' && !monsterMode) {
let types = ['Residential', 'Commercial', 'Industrial', 'School', 'PowerPlant'];
let colors = [[0, 200, 0], [0, 0, 200], [200, 200, 0], [200, 0, 200], [100, 100, 100]];
let idx = floor(random(5));
buildings.push({ x: i, y: j, type: types[idx], color: colors[idx] });
grid[i][j] = 'building';
}
}
function keyPressed() {
if (key === 'm' || key === 'M') monsterMode = !monsterMode;
if (key === 'g' || key === 'G') currentMonster = 'Godzilla';
if (key === 'r' || key === 'R') currentMonster = 'GiantRobot';
if (key === 't' || key === 'T') train.dir *= -1;
}
window.toggleMonsterMode = function() { monsterMode = !monsterMode; };
window.setMonster = function(monster) { currentMonster = monster; };
window.reverseTrain = function() { train.dir *= -1; };
"""
# Full HTML content with embedded p5.js
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
<style>
body {{ font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f0f0f0; }}
#controls {{ margin-bottom: 20px; }}
button {{ padding: 10px 20px; margin: 5px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; }}
button:hover {{ background: #45a049; }}
select {{ padding: 10px; margin: 5px; border-radius: 5px; }}
</style>
</head>
<body>
<div id="controls">
<button onclick="toggleMonster()">Toggle Monster Mode (M)</button>
<select onchange="setMonster(this.value)">
<option value="Godzilla">Godzilla (G)</option>
<option value="GiantRobot">Giant Robot (R)</option>
</select>
<button onclick="reverseTrain()">Reverse Train (T)</button>
</div>
<div id="sketch-holder"></div>
<script>
{p5js_code}
function toggleMonster() {{ window.toggleMonsterMode(); }}
function setMonster(monster) {{ window.setMonster(monster); }}
function reverseTrain() {{ window.reverseTrain(); }}
</script>
</body>
</html>
"""
# Streamlit app with sidebar
st.title("SimCity 2000 with Monster Mode")
st.write("Click to place buildings. Use keys: M (monster), G (Godzilla), R (Robot), T (train reverse).")
# Sidebar with emoji-grouped controls
with st.sidebar:
st.header("Controls")
# Building Controls
st.subheader("🏑 Buildings")
st.write("Click on the grid to randomly place buildings (Residential, Commercial, Industrial, School, Power Plant).")
# Monster Controls
st.subheader("πŸ‘Ύ Monsters")
if st.button("Toggle Monster Mode (M)", key="monster_toggle"):
st.markdown('<script>toggleMonster()</script>', unsafe_allow_html=True)
monster_choice = st.selectbox("Choose Monster", ["Godzilla (G)", "Giant Robot (R)"], index=0)
if monster_choice.startswith("Godzilla"):
st.markdown('<script>setMonster("Godzilla")</script>', unsafe_allow_html=True)
else:
st.markdown('<script>setMonster("GiantRobot")</script>', unsafe_allow_html=True)
# Train Controls
st.subheader("πŸš‚ Train")
if st.button("Reverse Train (T)", key="train_reverse"):
st.markdown('<script>reverseTrain()</script>', unsafe_allow_html=True)
html(html_content, height=700)