Spaces:
Running
Running
Update index.html
Browse files- index.html +138 -121
index.html
CHANGED
@@ -19,8 +19,8 @@
|
|
19 |
<gradio-file name="app.py" entrypoint>
|
20 |
import gradio as gr
|
21 |
import json
|
22 |
-
|
23 |
-
|
24 |
|
25 |
class GameState:
|
26 |
def __init__(self):
|
@@ -29,160 +29,177 @@ class GameState:
|
|
29 |
"minerals": 50,
|
30 |
"circuits": 0
|
31 |
}
|
32 |
-
self.grid = [[None for _ in range(
|
33 |
-
self.
|
34 |
-
"solarPanel": {
|
35 |
-
"cost": {"minerals": 10},
|
36 |
-
"produces": "energy",
|
37 |
-
"color": "#FEF08A"
|
38 |
-
},
|
39 |
-
"mineralExtractor": {
|
40 |
-
"cost": {"energy": 10},
|
41 |
-
"produces": "minerals",
|
42 |
-
"color": "#D1D5DB"
|
43 |
-
},
|
44 |
-
"circuitFactory": {
|
45 |
-
"cost": {"energy": 15, "minerals": 15},
|
46 |
-
"produces": "circuits",
|
47 |
-
"color": "#BBF7D0"
|
48 |
-
}
|
49 |
-
}
|
50 |
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
-
def
|
54 |
-
if
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
game_state.resources[produces] += 1
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
77 |
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
80 |
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
def create_ui():
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
<div style="max-width: 800px; margin: 0 auto; padding: 1rem;">
|
90 |
-
<div id="resources" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1rem;"
|
91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
<div id="message" style="text-align: center; color: #2563EB; font-weight: 500; margin: 0.5rem 0;"></div>
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
</div>
|
95 |
|
96 |
<script>
|
97 |
-
|
|
|
98 |
|
99 |
-
function updateUI(
|
100 |
-
|
101 |
-
document.getElementById('resources').innerHTML = Object.entries(gameState.resources)
|
102 |
-
.map(([resource, amount]) => `
|
103 |
-
<div style="padding: 0.75rem; border-radius: 0.5rem; background: ${getResourceColor(resource)}">
|
104 |
-
${resource}: ${amount}
|
105 |
-
</div>
|
106 |
-
`).join('');
|
107 |
|
108 |
-
// Update
|
109 |
-
document.getElementById('
|
110 |
-
|
111 |
-
|
112 |
-
style="padding: 1rem; border-radius: 0.5rem; cursor: pointer; background: ${data.color};
|
113 |
-
${type === selectedBuilding ? 'outline: 2px solid #3B82F6;' : ''}">
|
114 |
-
${type}<br>
|
115 |
-
Cost: ${Object.entries(data.cost).map(([r, c]) => `${c} ${r}`).join(', ')}
|
116 |
-
</button>
|
117 |
-
`).join('');
|
118 |
|
119 |
// Update grid
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
switch(resource) {
|
132 |
-
case 'energy': return '#FEF9C3';
|
133 |
-
case 'minerals': return '#F3F4F6';
|
134 |
-
case 'circuits': return '#DCFCE7';
|
135 |
-
default: return 'white';
|
136 |
-
}
|
137 |
-
}
|
138 |
|
139 |
-
|
140 |
-
selectedBuilding = type;
|
141 |
-
syncGameState("tick", null);
|
142 |
-
}
|
143 |
|
144 |
-
function placeBuilding(row, col) {
|
145 |
-
if (!selectedBuilding) {
|
146 |
document.getElementById('message').textContent = 'Select a building first!';
|
147 |
return;
|
148 |
-
}
|
149 |
-
|
150 |
-
}
|
151 |
-
|
152 |
-
// Resource production tick
|
153 |
-
setInterval(() => syncGameState("tick", null), 1000);
|
154 |
|
155 |
-
//
|
156 |
-
|
|
|
|
|
|
|
|
|
157 |
</script>
|
158 |
"""
|
159 |
return gr.HTML(html)
|
160 |
|
161 |
-
def handle_action(action_type, data):
|
162 |
-
return json.dumps(update_game(action_type, data))
|
163 |
-
|
164 |
with gr.Blocks() as demo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
ui = create_ui()
|
166 |
-
action = gr.State("tick")
|
167 |
-
data = gr.State(None)
|
168 |
|
169 |
-
|
|
|
170 |
|
171 |
-
|
172 |
-
|
173 |
-
window.syncGameState = function(actionType, actionData) {
|
174 |
-
gradioApp().querySelector("gradio-app").props.action_type = actionType;
|
175 |
-
gradioApp().querySelector("gradio-app").props.action_data = actionData;
|
176 |
-
updateUI(JSON.parse(handle_action(actionType, actionData)));
|
177 |
-
}
|
178 |
-
}
|
179 |
-
""")
|
180 |
|
181 |
demo.launch()
|
182 |
</gradio-file>
|
183 |
|
184 |
<gradio-requirements>
|
185 |
-
# No additional requirements needed
|
186 |
</gradio-requirements>
|
187 |
</gradio-lite>
|
188 |
</body>
|
|
|
19 |
<gradio-file name="app.py" entrypoint>
|
20 |
import gradio as gr
|
21 |
import json
|
22 |
+
|
23 |
+
GRID_SIZE = 8
|
24 |
|
25 |
class GameState:
|
26 |
def __init__(self):
|
|
|
29 |
"minerals": 50,
|
30 |
"circuits": 0
|
31 |
}
|
32 |
+
self.grid = [[None for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
|
33 |
+
self.message = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
+
def to_json(self):
|
36 |
+
return json.dumps({
|
37 |
+
"resources": self.resources,
|
38 |
+
"grid": self.grid,
|
39 |
+
"message": self.message
|
40 |
+
})
|
41 |
|
42 |
+
def process_click(row, col, building_type, state_json):
|
43 |
+
if not state_json:
|
44 |
+
return GameState().to_json()
|
45 |
+
|
46 |
+
state = json.loads(state_json)
|
47 |
+
game = GameState()
|
48 |
+
game.resources = state["resources"]
|
49 |
+
game.grid = state["grid"]
|
|
|
50 |
|
51 |
+
# Building costs
|
52 |
+
costs = {
|
53 |
+
"solarPanel": {"minerals": 10},
|
54 |
+
"mineralExtractor": {"energy": 10},
|
55 |
+
"circuitFactory": {"energy": 15, "minerals": 15}
|
56 |
+
}
|
57 |
+
|
58 |
+
# Check if we can afford the building
|
59 |
+
if building_type in costs:
|
60 |
+
can_afford = True
|
61 |
+
for resource, cost in costs[building_type].items():
|
62 |
+
if game.resources[resource] < cost:
|
63 |
+
can_afford = False
|
64 |
+
game.message = f"Not enough {resource}!"
|
65 |
+
break
|
66 |
|
67 |
+
if can_afford:
|
68 |
+
# Deduct resources
|
69 |
+
for resource, cost in costs[building_type].items():
|
70 |
+
game.resources[resource] -= cost
|
71 |
|
72 |
+
# Place building
|
73 |
+
if game.grid[row][col] is None:
|
74 |
+
game.grid[row][col] = building_type
|
75 |
+
game.message = f"Placed {building_type}"
|
76 |
+
else:
|
77 |
+
game.message = "Space already occupied!"
|
78 |
|
79 |
+
# Produce resources from buildings
|
80 |
+
for r in range(GRID_SIZE):
|
81 |
+
for c in range(GRID_SIZE):
|
82 |
+
building = game.grid[r][c]
|
83 |
+
if building == "solarPanel":
|
84 |
+
game.resources["energy"] += 1
|
85 |
+
elif building == "mineralExtractor":
|
86 |
+
game.resources["minerals"] += 1
|
87 |
+
elif building == "circuitFactory":
|
88 |
+
game.resources["circuits"] += 1
|
89 |
+
|
90 |
+
return game.to_json()
|
91 |
|
92 |
def create_ui():
|
93 |
+
building_colors = {
|
94 |
+
"solarPanel": "#FEF08A",
|
95 |
+
"mineralExtractor": "#D1D5DB",
|
96 |
+
"circuitFactory": "#BBF7D0"
|
97 |
+
}
|
98 |
+
|
99 |
+
html = f"""
|
100 |
<div style="max-width: 800px; margin: 0 auto; padding: 1rem;">
|
101 |
+
<div id="resources" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1rem;">
|
102 |
+
<div style="padding: 0.75rem; border-radius: 0.5rem; background: #FEF9C3;">Energy: <span id="energy">50</span></div>
|
103 |
+
<div style="padding: 0.75rem; border-radius: 0.5rem; background: #F3F4F6;">Minerals: <span id="minerals">50</span></div>
|
104 |
+
<div style="padding: 0.75rem; border-radius: 0.5rem; background: #DCFCE7;">Circuits: <span id="circuits">0</span></div>
|
105 |
+
</div>
|
106 |
+
|
107 |
+
<div style="display: flex; gap: 1rem; margin-bottom: 1rem;">
|
108 |
+
<button onclick="window.selectedBuilding='solarPanel'" style="padding: 1rem; border-radius: 0.5rem; background: #FEF08A;">
|
109 |
+
Solar Panel<br>Cost: 10 minerals
|
110 |
+
</button>
|
111 |
+
<button onclick="window.selectedBuilding='mineralExtractor'" style="padding: 1rem; border-radius: 0.5rem; background: #D1D5DB;">
|
112 |
+
Mineral Extractor<br>Cost: 10 energy
|
113 |
+
</button>
|
114 |
+
<button onclick="window.selectedBuilding='circuitFactory'" style="padding: 1rem; border-radius: 0.5rem; background: #BBF7D0;">
|
115 |
+
Circuit Factory<br>Cost: 15 energy, 15 minerals
|
116 |
+
</button>
|
117 |
+
</div>
|
118 |
+
|
119 |
<div id="message" style="text-align: center; color: #2563EB; font-weight: 500; margin: 0.5rem 0;"></div>
|
120 |
+
|
121 |
+
<div id="grid" style="display: grid; grid-template-columns: repeat({GRID_SIZE}, 1fr); gap: 0.25rem; background: #F3F4F6; padding: 0.5rem; border-radius: 0.5rem;">
|
122 |
+
{''''.join(f'''
|
123 |
+
<button
|
124 |
+
onclick="placeBuilding({r}, {c})"
|
125 |
+
id="cell-{r}-{c}"
|
126 |
+
style="aspect-ratio: 1; border-radius: 0.5rem; border: none; cursor: pointer; background: white;">
|
127 |
+
</button>
|
128 |
+
''' for r in range(GRID_SIZE) for c in range(GRID_SIZE))}
|
129 |
+
</div>
|
130 |
</div>
|
131 |
|
132 |
<script>
|
133 |
+
window.selectedBuilding = null;
|
134 |
+
window.gameState = null;
|
135 |
|
136 |
+
function updateUI(state) {
|
137 |
+
window.gameState = state;
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
|
139 |
+
// Update resources
|
140 |
+
document.getElementById('energy').textContent = state.resources.energy;
|
141 |
+
document.getElementById('minerals').textContent = state.resources.minerals;
|
142 |
+
document.getElementById('circuits').textContent = state.resources.circuits;
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
// Update grid
|
145 |
+
for (let r = 0; r < {GRID_SIZE}; r++) {{
|
146 |
+
for (let c = 0; c < {GRID_SIZE}; c++) {{
|
147 |
+
const cell = document.getElementById(`cell-${{r}}-${{c}}`);
|
148 |
+
const building = state.grid[r][c];
|
149 |
+
cell.style.background = building ? buildingColors[building] : 'white';
|
150 |
+
}}
|
151 |
+
}}
|
152 |
+
|
153 |
+
// Update message
|
154 |
+
document.getElementById('message').textContent = state.message;
|
155 |
+
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
|
157 |
+
const buildingColors = {json.dumps(building_colors)};
|
|
|
|
|
|
|
158 |
|
159 |
+
function placeBuilding(row, col) {{
|
160 |
+
if (!window.selectedBuilding) {{
|
161 |
document.getElementById('message').textContent = 'Select a building first!';
|
162 |
return;
|
163 |
+
}}
|
164 |
+
send_click(row, col, window.selectedBuilding, JSON.stringify(window.gameState));
|
165 |
+
}}
|
|
|
|
|
|
|
166 |
|
167 |
+
// Production tick
|
168 |
+
setInterval(() => {{
|
169 |
+
if (window.gameState) {{
|
170 |
+
send_click(-1, -1, null, JSON.stringify(window.gameState));
|
171 |
+
}}
|
172 |
+
}}, 1000);
|
173 |
</script>
|
174 |
"""
|
175 |
return gr.HTML(html)
|
176 |
|
|
|
|
|
|
|
177 |
with gr.Blocks() as demo:
|
178 |
+
state = gr.State()
|
179 |
+
click_handler = gr.Interface(
|
180 |
+
fn=process_click,
|
181 |
+
inputs=[
|
182 |
+
gr.Number(label="Row"),
|
183 |
+
gr.Number(label="Col"),
|
184 |
+
gr.Text(label="Building"),
|
185 |
+
gr.Text(label="State")
|
186 |
+
],
|
187 |
+
outputs=gr.Text(label="New State"),
|
188 |
+
live=True
|
189 |
+
)
|
190 |
+
|
191 |
ui = create_ui()
|
|
|
|
|
192 |
|
193 |
+
def update_ui(value):
|
194 |
+
return value
|
195 |
|
196 |
+
click_handler.load(lambda: GameState().to_json())
|
197 |
+
demo.load(lambda: None, None, _js="() => { window.send_click = (row, col, building, state) => { "+click_handler.jsfn+"(row, col, building, state).then((v) => updateUI(JSON.parse(v))) } }")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
|
199 |
demo.launch()
|
200 |
</gradio-file>
|
201 |
|
202 |
<gradio-requirements>
|
|
|
203 |
</gradio-requirements>
|
204 |
</gradio-lite>
|
205 |
</body>
|