LeafCat79 commited on
Commit
4900774
·
verified ·
1 Parent(s): b93c43a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -13
app.py CHANGED
@@ -1,19 +1,21 @@
1
  """
2
- Text-to-Game Generator - Groq Edition (Truly Free)
3
  - Code : llama-3.1-8b-instant via Groq API (free, no credit card)
4
- - Images: canvas-drawn graphics only (zero API, zero quota, zero cost)
5
 
6
- No GPU needed. No compilation. No HF quota. No credit card.
7
- Setup: add GROQ_API_KEY as a Space secret.
8
  """
9
 
10
  import os
11
  import re
 
12
  import base64
13
  import traceback
14
 
15
  import gradio as gr
16
  from openai import OpenAI
 
 
17
 
18
  # ---------------------------------------------------------------------------
19
  # Google AI Studio client (OpenAI-compatible endpoint)
@@ -43,7 +45,21 @@ def get_client():
43
 
44
 
45
  # ---------------------------------------------------------------------------
46
- # Game type configs canvas-drawn graphics, no image files
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  # ---------------------------------------------------------------------------
48
 
49
  GAME_TYPES = {
@@ -146,6 +162,69 @@ THEME_EXAMPLES = {
146
  "Surprise Me!": [["A sentient library that rearranges itself"], ["Deep sea bioluminescent creatures"], ["Retro space diner on a comet"]],
147
  }
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  # ---------------------------------------------------------------------------
150
  # Code generation
151
  # ---------------------------------------------------------------------------
@@ -156,13 +235,22 @@ def generate_game_code(game_type: str, theme: str, temperature: float, max_new_t
156
 
157
  template = GAME_TYPES[game_type]["prompt_template"]
158
  user_prompt = template.format(theme=theme.strip())
159
- system_msg = (
160
- "You are an expert HTML5 game developer. "
161
- "Write a complete, working, single-file HTML5 game using only canvas 2D drawing. "
162
- "Never use image files or external resources. "
163
- "Draw everything with canvas shapes, colors, and text. "
164
- "Output ONLY the raw HTML - no markdown fences, no explanation."
165
- )
 
 
 
 
 
 
 
 
 
166
 
167
  try:
168
  client = get_client()
@@ -181,7 +269,20 @@ def generate_game_code(game_type: str, theme: str, temperature: float, max_new_t
181
  if "<html" not in code.lower() and "<!doctype" not in code.lower():
182
  code = _wrap_in_html(code, theme)
183
 
184
- status = "Done! Game generated. Click Launch Game to play."
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  return code, status, _build_preview(code)
186
 
187
  except Exception as exc:
 
1
  """
2
+ Text-to-Game Generator - Groq + FLUX Edition
3
  - Code : llama-3.1-8b-instant via Groq API (free, no credit card)
4
+ - Images: black-forest-labs/FLUX.1-schnell via nscale (HF Inference API, free with HF_TOKEN)
5
 
6
+ Setup: add GROQ_API_KEY and HF_TOKEN as Space secrets.
 
7
  """
8
 
9
  import os
10
  import re
11
+ import io
12
  import base64
13
  import traceback
14
 
15
  import gradio as gr
16
  from openai import OpenAI
17
+ from huggingface_hub import InferenceClient
18
+ from PIL import Image
19
 
20
  # ---------------------------------------------------------------------------
21
  # Google AI Studio client (OpenAI-compatible endpoint)
 
45
 
46
 
47
  # ---------------------------------------------------------------------------
48
+ # Image modelFLUX.1-schnell via nscale (free with HF_TOKEN)
49
+ # ---------------------------------------------------------------------------
50
+
51
+ IMAGE_MODEL = "black-forest-labs/FLUX.1-schnell"
52
+ HF_TOKEN = os.environ.get("HF_TOKEN", "")
53
+
54
+
55
+ def get_image_client():
56
+ if not HF_TOKEN:
57
+ return None
58
+ return InferenceClient(provider="nscale", api_key=HF_TOKEN)
59
+
60
+
61
+ # ---------------------------------------------------------------------------
62
+ # Game type configs — uses sprite_NAME.png when HF_TOKEN set, else canvas
63
  # ---------------------------------------------------------------------------
64
 
65
  GAME_TYPES = {
 
162
  "Surprise Me!": [["A sentient library that rearranges itself"], ["Deep sea bioluminescent creatures"], ["Retro space diner on a comet"]],
163
  }
164
 
165
+ # ---------------------------------------------------------------------------
166
+ # Sprite helpers
167
+ # ---------------------------------------------------------------------------
168
+
169
+ def _pil_to_data_uri(pil_image: Image.Image, size: int = 64) -> str:
170
+ img = pil_image.resize((size, size), Image.LANCZOS)
171
+ buf = io.BytesIO()
172
+ img.save(buf, format="PNG")
173
+ return "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
174
+
175
+
176
+ def _colored_placeholder(name: str) -> str:
177
+ colours = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12", "#9b59b6", "#1abc9c"]
178
+ colour = colours[abs(hash(name)) % len(colours)]
179
+ label = name.replace("sprite_", "")[:6]
180
+ svg = (
181
+ '<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64">'
182
+ '<rect width="64" height="64" fill="' + colour + '" rx="8"/>'
183
+ '<text x="32" y="38" font-size="10" text-anchor="middle" fill="white">' + label + '</text>'
184
+ '</svg>'
185
+ )
186
+ return "data:image/svg+xml;base64," + base64.b64encode(svg.encode()).decode()
187
+
188
+
189
+ def _sprite_prompt(sprite_name: str, theme: str) -> str:
190
+ label = sprite_name.replace("sprite_", "").replace("_", " ")
191
+ return (
192
+ "Pixel-art game sprite: " + label + ". Theme: " + theme + ". "
193
+ "Vibrant colours, clear silhouette, plain dark background, 64x64 pixel style."
194
+ )
195
+
196
+
197
+ def _find_sprite_filenames(html_code: str) -> list:
198
+ pattern = r"""(?:src\s*=\s*['"]|\.src\s*=\s*['"])\s*(sprite_[a-zA-Z0-9_]+\.png)\s*['"]"""
199
+ return list(dict.fromkeys(re.findall(pattern, html_code)))[:3]
200
+
201
+
202
+ def _inject_sprites(html_code: str, sprite_map: dict) -> str:
203
+ for fname, data_uri in sprite_map.items():
204
+ html_code = html_code.replace('"' + fname + '"', '"' + data_uri + '"')
205
+ html_code = html_code.replace("'" + fname + "'", "'" + data_uri + "'")
206
+ return html_code
207
+
208
+
209
+ def generate_sprites(sprite_names: list, theme: str) -> dict:
210
+ if not sprite_names:
211
+ return {}
212
+ client = get_image_client()
213
+ if not client:
214
+ return {n: _colored_placeholder(n.replace(".png","")) for n in sprite_names}
215
+ mapping = {}
216
+ for fname in sprite_names:
217
+ name_no_ext = fname.replace(".png", "")
218
+ prompt = _sprite_prompt(name_no_ext, theme)
219
+ try:
220
+ pil_img = client.text_to_image(prompt, model=IMAGE_MODEL)
221
+ mapping[fname] = _pil_to_data_uri(pil_img, size=64)
222
+ except Exception as exc:
223
+ print("[FLUX] Failed '" + fname + "': " + str(exc))
224
+ mapping[fname] = _colored_placeholder(name_no_ext)
225
+ return mapping
226
+
227
+
228
  # ---------------------------------------------------------------------------
229
  # Code generation
230
  # ---------------------------------------------------------------------------
 
235
 
236
  template = GAME_TYPES[game_type]["prompt_template"]
237
  user_prompt = template.format(theme=theme.strip())
238
+ use_sprites = bool(HF_TOKEN)
239
+
240
+ if use_sprites:
241
+ system_msg = (
242
+ "You are an expert HTML5 game developer. "
243
+ "Write a complete, working, single-file HTML5 game using canvas and vanilla JavaScript. "
244
+ "Use new Image() with src='sprite_NAME.png' for every visual asset (max 3 sprites). "
245
+ "Output ONLY the raw HTML - no markdown fences, no explanation."
246
+ )
247
+ else:
248
+ system_msg = (
249
+ "You are an expert HTML5 game developer. "
250
+ "Write a complete, working, single-file HTML5 game using only canvas 2D drawing. "
251
+ "Never use image files. Draw everything with canvas shapes, colors, and text. "
252
+ "Output ONLY the raw HTML - no markdown fences, no explanation."
253
+ )
254
 
255
  try:
256
  client = get_client()
 
269
  if "<html" not in code.lower() and "<!doctype" not in code.lower():
270
  code = _wrap_in_html(code, theme)
271
 
272
+ if use_sprites:
273
+ sprite_names = _find_sprite_filenames(code)
274
+ sprite_map = generate_sprites(sprite_names, theme.strip())
275
+ code = _inject_sprites(code, sprite_map)
276
+ n_real = sum(1 for v in sprite_map.values() if "image/png" in v)
277
+ n_fallback = len(sprite_map) - n_real
278
+ status = (
279
+ "Done! " + str(len(sprite_names)) + " sprite(s) - "
280
+ + str(n_real) + " by FLUX.1-schnell, "
281
+ + str(n_fallback) + " fallback. Click Launch Game to play."
282
+ )
283
+ else:
284
+ status = "Done! Game generated (canvas graphics). Click Launch Game to play."
285
+
286
  return code, status, _build_preview(code)
287
 
288
  except Exception as exc: