Spaces:
Running
Running
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() |