SimFactory2 / index.html
awacke1's picture
Update index.html
aef21d2 verified
raw
history blame
7.86 kB
import gradio as gr
import json
from dataclasses import dataclass, asdict
import numpy as np
@dataclass
class Building:
type: str
color: str
cost: dict
produces: str
class FactoryGame:
def __init__(self):
self.grid_size = 8
self.grid = [[None for _ in range(self.grid_size)] for _ in range(self.grid_size)]
self.resources = {"energy": 50, "minerals": 50, "circuits": 0}
self.buildings = {
"solarPanel": Building(
type="solarPanel",
color="#FEF08A", # yellow-200
cost={"minerals": 10},
produces="energy"
),
"mineralExtractor": Building(
type="mineralExtractor",
color="#D1D5DB", # gray-300
cost={"energy": 10},
produces="minerals"
),
"circuitFactory": Building(
type="circuitFactory",
color="#BBF7D0", # green-200
cost={"energy": 15, "minerals": 15},
produces="circuits"
)
}
def to_json(self):
return json.dumps({
"grid": self.grid,
"resources": self.resources,
"buildings": {k: asdict(v) for k, v in self.buildings.items()}
})
def can_afford(self, building_type):
building = self.buildings[building_type]
return all(self.resources.get(resource, 0) >= cost
for resource, cost in building.cost.items())
def place_building(self, row, col, building_type):
if not self.can_afford(building_type):
return False, "Not enough resources!"
if self.grid[row][col] is not None:
return False, "Cell already occupied!"
# Deduct costs
building = self.buildings[building_type]
for resource, cost in building.cost.items():
self.resources[resource] -= cost
# Place building
self.grid[row][col] = building_type
return True, "Building placed successfully!"
def update_resources(self):
for row in range(self.grid_size):
for col in range(self.grid_size):
building_type = self.grid[row][col]
if building_type:
building = self.buildings[building_type]
self.resources[building.produces] = self.resources.get(building.produces, 0) + 1
def create_game_html():
return """
<html>
<head>
<style>
.game-container { max-width: 800px; margin: 0 auto; padding: 1rem; }
.resources { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1rem; }
.resource { padding: 0.75rem; border-radius: 0.5rem; }
.buildings { display: flex; gap: 1rem; margin-bottom: 1rem; }
.building { padding: 1rem; border-radius: 0.5rem; cursor: pointer; text-align: center; }
.building.selected { outline: 2px solid #3B82F6; }
.grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 0.25rem; background: #F3F4F6; padding: 0.5rem; border-radius: 0.5rem; }
.cell { aspect-ratio: 1; border-radius: 0.5rem; border: none; cursor: pointer; }
.message { text-align: center; color: #2563EB; font-weight: 500; margin: 0.5rem 0; }
</style>
</head>
<body>
<div class="game-container">
<div class="resources" id="resources"></div>
<div class="buildings" id="buildings"></div>
<div class="message" id="message"></div>
<div class="grid" id="grid"></div>
</div>
<script>
let game = null;
let selectedBuilding = null;
function updateUI() {
if (!game) return;
// Update resources
const resourcesDiv = document.getElementById('resources');
resourcesDiv.innerHTML = Object.entries(game.resources)
.map(([resource, amount]) => `
<div class="resource" style="background: ${getResourceColor(resource)}">
${resource}: ${amount}
</div>
`).join('');
// Update buildings selection
const buildingsDiv = document.getElementById('buildings');
buildingsDiv.innerHTML = Object.entries(game.buildings)
.map(([type, data]) => `
<button class="building ${type === selectedBuilding ? 'selected' : ''}"
style="background: ${data.color}"
onclick="selectBuilding('${type}')">
${type}<br>
Cost: ${Object.entries(data.cost).map(([r, c]) => `${c} ${r}`).join(', ')}
</button>
`).join('');
// Update grid
const gridDiv = document.getElementById('grid');
gridDiv.innerHTML = game.grid.map((row, i) =>
row.map((cell, j) => `
<button class="cell"
style="background: ${cell ? game.buildings[cell].color : 'white'}"
onclick="placeBuilding(${i}, ${j})">
</button>
`).join('')
).join('');
}
function getResourceColor(resource) {
switch(resource) {
case 'energy': return '#FEF9C3';
case 'minerals': return '#F3F4F6';
case 'circuits': return '#DCFCE7';
default: return 'white';
}
}
function selectBuilding(type) {
selectedBuilding = type;
updateUI();
}
function placeBuilding(row, col) {
if (!selectedBuilding) {
document.getElementById('message').textContent = 'Select a building first!';
return;
}
gradioCallback([row, col, selectedBuilding]).then(response => {
game = JSON.parse(response);
updateUI();
});
}
function initialize(gameState) {
game = JSON.parse(gameState);
updateUI();
}
// Resource production tick
setInterval(() => {
if (game) {
gradioCallback(['tick']).then(response => {
game = JSON.parse(response);
updateUI();
});
}
}, 1000);
</script>
</body>
</html>
"""
def create_interface():
game = FactoryGame()
def handle_action(action):
if isinstance(action, list) and len(action) == 3:
row, col, building_type = action
game.place_building(row, col, building_type)
elif action == 'tick':
game.update_resources()
return game.to_json()
with gr.Blocks() as demo:
gr.HTML(create_game_html())
game_state = gr.State(game.to_json())
demo.load(lambda: game.to_json(), None, game_state)
demo.load(None, js="initialize(arguments[0])", inputs=[game_state])
callback = demo.load(handle_action, inputs=gr.State([]), outputs=game_state)
demo.load(None, js=f"window.gradioCallback = {callback}")
return demo
if __name__ == "__main__":
interface = create_interface()
interface.launch()