Spaces:
Running
Running
Update backend/agents/puzzle_agent.py
Browse files
backend/agents/puzzle_agent.py
CHANGED
|
@@ -304,7 +304,7 @@ STRICT RULES:
|
|
| 304 |
2. Reference the exact mission steps for that level in its goal_description.
|
| 305 |
3. CRITICAL: Provide a UNIQUE programming/logic problem in the `story` field for EACH level.
|
| 306 |
4. The `title` of each level should match its assigned Theme (e.g., "Bank Heist", "Space Station").
|
| 307 |
-
5. Generate a
|
| 308 |
|
| 309 |
OUTPUT only valid JSON (no markdown):
|
| 310 |
{{
|
|
@@ -319,6 +319,9 @@ OUTPUT only valid JSON (no markdown):
|
|
| 319 |
"story": "Present the unique logical problem or scenario for this level here...",
|
| 320 |
"goal_description": "Step 1: [action] β Step 2: [action] β Reach Exit",
|
| 321 |
"hint": "Specific, highly useful hint for this specific puzzle logic",
|
|
|
|
|
|
|
|
|
|
| 322 |
"allowed_blocks": ["move_forward", "turn_left", "turn_right"]
|
| 323 |
}}
|
| 324 |
]
|
|
@@ -356,16 +359,21 @@ OUTPUT only valid JSON (no markdown):
|
|
| 356 |
}
|
| 357 |
diff_key = _diff_norm.get(difficulty.lower(), "easy")
|
| 358 |
|
| 359 |
-
# Step 1: Pick 5
|
| 360 |
filtered = [s for s in self.scenarios if s.get("difficulty", "").lower() == diff_key]
|
| 361 |
-
if
|
|
|
|
| 362 |
filtered = self.scenarios
|
| 363 |
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
arc_scenarios.append(random.choice(filtered) if filtered else {})
|
| 368 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 |
# Step 2: Pick DAA concept β harder = more complex maze type + larger dim
|
| 370 |
concept_map = {
|
| 371 |
"easy": [DAA_CONCEPTS[0], DAA_CONCEPTS[3]], # collect+reach, sequence
|
|
@@ -400,7 +408,7 @@ OUTPUT only valid JSON (no markdown):
|
|
| 400 |
print(f"[PuzzleAgent] AI enrichment failed: {e}. Using fallback.")
|
| 401 |
arc = self._build_fallback_arc(arc_scenarios, concept, compiled_grids)
|
| 402 |
|
| 403 |
-
# Step 5: Inject compiled grids into each level
|
| 404 |
levels = arc.get("levels", [])
|
| 405 |
for i, level in enumerate(levels):
|
| 406 |
cg = compiled_grids[i] if i < len(compiled_grids) else compiled_grids[-1]
|
|
@@ -408,13 +416,23 @@ OUTPUT only valid JSON (no markdown):
|
|
| 408 |
level["maze_rows"] = cg["rows"]
|
| 409 |
level["maze_cols"] = cg["cols"]
|
| 410 |
level["ordered_steps"] = cg["ordered_steps"]
|
| 411 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
level["daa_concept"] = concept["title"]
|
| 413 |
level["daa_description"]= concept["description"]
|
| 414 |
level["type"] = "maze"
|
| 415 |
|
| 416 |
arc["levels"] = levels
|
| 417 |
-
|
| 418 |
return arc
|
| 419 |
|
| 420 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -438,6 +456,9 @@ OUTPUT only valid JSON (no markdown):
|
|
| 438 |
),
|
| 439 |
"goal_description": step_str + " β Exit",
|
| 440 |
"hint": "Try planning your path by starting from the end and tracing backwards.",
|
|
|
|
|
|
|
|
|
|
| 441 |
"daa_concept": concept["title"],
|
| 442 |
"daa_description": concept["description"],
|
| 443 |
"allowed_blocks": ["move_forward", "turn_left", "turn_right"],
|
|
|
|
| 304 |
2. Reference the exact mission steps for that level in its goal_description.
|
| 305 |
3. CRITICAL: Provide a UNIQUE programming/logic problem in the `story` field for EACH level.
|
| 306 |
4. The `title` of each level should match its assigned Theme (e.g., "Bank Heist", "Space Station").
|
| 307 |
+
5. CRITICAL: Generate a COMPLETELY UNIQUE `hint` for EVERY level. The hint MUST be different for Level 1, Level 2, etc., and must directly reference the specific theme/logic of that exact level! Do NOT repeat hints!
|
| 308 |
|
| 309 |
OUTPUT only valid JSON (no markdown):
|
| 310 |
{{
|
|
|
|
| 319 |
"story": "Present the unique logical problem or scenario for this level here...",
|
| 320 |
"goal_description": "Step 1: [action] β Step 2: [action] β Reach Exit",
|
| 321 |
"hint": "Specific, highly useful hint for this specific puzzle logic",
|
| 322 |
+
"npc_emoji": "π¦",
|
| 323 |
+
"item_emoji": "π΅",
|
| 324 |
+
"goal_emoji": "π",
|
| 325 |
"allowed_blocks": ["move_forward", "turn_left", "turn_right"]
|
| 326 |
}}
|
| 327 |
]
|
|
|
|
| 359 |
}
|
| 360 |
diff_key = _diff_norm.get(difficulty.lower(), "easy")
|
| 361 |
|
| 362 |
+
# Step 1: Pick 5 completely DISTINCT scenarios matching normalised difficulty
|
| 363 |
filtered = [s for s in self.scenarios if s.get("difficulty", "").lower() == diff_key]
|
| 364 |
+
if len(filtered) < 5:
|
| 365 |
+
# If not enough filtered, use the entire dataset to guarantee 5 unique scenarios
|
| 366 |
filtered = self.scenarios
|
| 367 |
|
| 368 |
+
arc_scenarios = random.sample(filtered, min(5, len(filtered)))
|
| 369 |
+
# Fallback if dataset has less than 5 elements
|
| 370 |
+
while len(arc_scenarios) < 5:
|
| 371 |
arc_scenarios.append(random.choice(filtered) if filtered else {})
|
| 372 |
|
| 373 |
+
# Ensure each level gets a distinct visual theme color palette
|
| 374 |
+
palette_keys = list(THEME_PALETTES.keys())
|
| 375 |
+
random.shuffle(palette_keys)
|
| 376 |
+
|
| 377 |
# Step 2: Pick DAA concept β harder = more complex maze type + larger dim
|
| 378 |
concept_map = {
|
| 379 |
"easy": [DAA_CONCEPTS[0], DAA_CONCEPTS[3]], # collect+reach, sequence
|
|
|
|
| 408 |
print(f"[PuzzleAgent] AI enrichment failed: {e}. Using fallback.")
|
| 409 |
arc = self._build_fallback_arc(arc_scenarios, concept, compiled_grids)
|
| 410 |
|
| 411 |
+
# Step 5: Inject compiled grids and dynamic palettes into each level
|
| 412 |
levels = arc.get("levels", [])
|
| 413 |
for i, level in enumerate(levels):
|
| 414 |
cg = compiled_grids[i] if i < len(compiled_grids) else compiled_grids[-1]
|
|
|
|
| 416 |
level["maze_rows"] = cg["rows"]
|
| 417 |
level["maze_cols"] = cg["cols"]
|
| 418 |
level["ordered_steps"] = cg["ordered_steps"]
|
| 419 |
+
|
| 420 |
+
# Assign a distinct color palette
|
| 421 |
+
palette_key = palette_keys[i % len(palette_keys)]
|
| 422 |
+
base_palette = THEME_PALETTES.get(palette_key, THEME_PALETTES["default"]).copy()
|
| 423 |
+
|
| 424 |
+
# Inject dynamic AI emojis into the palette so the Flutter UI can read them
|
| 425 |
+
base_palette["npc_emoji"] = level.get("npc_emoji", "π€΅")
|
| 426 |
+
base_palette["item_emoji"] = level.get("item_emoji", "π")
|
| 427 |
+
base_palette["goal_emoji"] = level.get("goal_emoji", "π")
|
| 428 |
+
|
| 429 |
+
level["theme_palette"] = base_palette
|
| 430 |
level["daa_concept"] = concept["title"]
|
| 431 |
level["daa_description"]= concept["description"]
|
| 432 |
level["type"] = "maze"
|
| 433 |
|
| 434 |
arc["levels"] = levels
|
| 435 |
+
# Remove top-level theme_palette so we don't override the distinct ones later
|
| 436 |
return arc
|
| 437 |
|
| 438 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 456 |
),
|
| 457 |
"goal_description": step_str + " β Exit",
|
| 458 |
"hint": "Try planning your path by starting from the end and tracing backwards.",
|
| 459 |
+
"npc_emoji": "π€΅",
|
| 460 |
+
"item_emoji": "π",
|
| 461 |
+
"goal_emoji": "π",
|
| 462 |
"daa_concept": concept["title"],
|
| 463 |
"daa_description": concept["description"],
|
| 464 |
"allowed_blocks": ["move_forward", "turn_left", "turn_right"],
|