import streamlit as st from streamlit.components.v1 import html # Define the 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 let debugText = "Loading..."; 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; debugText = "Setup complete. Click to build, use M/G/R/T keys."; } function draw() { background(220); drawGrid(); drawBuildings(); drawTrain(); if (monsterMode) drawMonster(); fill(0); text(debugText, 10, 20); // Debug feedback } 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); 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') { 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') { 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') { 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') { 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') { 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') { 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') { 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 (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length) { 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'; debugText = `Placed ${types[idx]} at (${i}, ${j})`; } } } function keyPressed() { if (key === 'm' || key === 'M') { monsterMode = !monsterMode; debugText = `Monster Mode: ${monsterMode}`; } if (key === 'g' || key === 'G') { currentMonster = 'Godzilla'; debugText = "Monster set to Godzilla"; } if (key === 'r' || key === 'R') { currentMonster = 'GiantRobot'; debugText = "Monster set to Giant Robot"; } if (key === 't' || key === 'T') { train.dir *= -1; debugText = "Train direction reversed"; } } window.toggleMonsterMode = function() { monsterMode = !monsterMode; debugText = `Monster Mode: ${monsterMode}`; }; window.setMonster = function(monster) { currentMonster = monster; debugText = `Monster set to ${monster}`; }; window.reverseTrain = function() { train.dir *= -1; debugText = "Train direction reversed"; }; """ # Full HTML content with embedded p5.js html_content = f"""
""" # 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 info (no JS calls) with st.sidebar: st.header("Controls") st.subheader("🏡 Buildings") st.write("Click to randomly place: Residential, Commercial, Industrial, School, Power Plant") st.subheader("👾 Monsters") st.write("M: Toggle Monster Mode\nG: Godzilla\nR: Giant Robot") st.subheader("🚂 Train") st.write("T: Reverse Train Direction") html(html_content, height=700)