Download scripts/extract_characters.py from BladeSzaSza/Immersive-Vibe-Development-Studio: direct link, hf CLI and curl.
- Browser
- Download file 5.02 kB
-
https://huggingface.co/spaces/BladeSzaSza/Immersive-Vibe-Development-Studio/resolve/main/scripts/extract_characters.py
- Command line
-
hf download hf://spaces/BladeSzaSza/Immersive-Vibe-Development-Studio/scripts/extract_characters.py
-
curl -L -o extract_characters.py https://huggingface.co/spaces/BladeSzaSza/Immersive-Vibe-Development-Studio/resolve/main/scripts/extract_characters.py
5.02 kB
| #!/usr/bin/env python3 | |
| """ | |
| Dev-time script. Extracts buildXxx() functions from playground sandbox JS files. | |
| Writes sandbox_cache/characters_registry.json and sandbox_cache/characters.js. | |
| Usage: | |
| python3 scripts/extract_characters.py | |
| Source: /Users/bolyos/Desktop/playground/<game>/sandbox/ | |
| """ | |
| import json | |
| import re | |
| from pathlib import Path | |
| PLAYGROUND_BASE = Path("/Users/bolyos/Desktop/playground") | |
| OUT_DIR = Path(__file__).parent.parent / "sandbox_cache" | |
| OUT_DIR.mkdir(exist_ok=True) | |
| # Pure Three.js geometry primitives โ not characters, exclude them | |
| GEOMETRY_PRIMITIVES = { | |
| "buildBox", "buildCylinder", "buildSphere", "buildCone", | |
| "buildIcosahedron", "buildOctahedron", "buildDodecahedron", | |
| "buildTetrahedron", "buildLathe", | |
| } | |
| # Scan patterns in priority order | |
| SCAN_PATTERNS = [ | |
| "*/sandbox/src/characters.js", # jungle modular structure โ try first | |
| "*/sandbox/main.js", | |
| "*/sandbox/bundle.js", | |
| ] | |
| def extract_functions(source: str) -> dict[str, str]: | |
| """Extract top-level function buildXxx() {...} blocks from JS source.""" | |
| registry: dict[str, str] = {} | |
| pattern = re.compile(r'^(function (build\w+)\([^)]*\)\s*\{)', re.MULTILINE) | |
| for m in pattern.finditer(source): | |
| fn_name = m.group(2) | |
| if fn_name in GEOMETRY_PRIMITIVES: | |
| continue | |
| start = m.start() | |
| depth = 0 | |
| for j in range(m.start(1), len(source)): | |
| if source[j] == "{": | |
| depth += 1 | |
| elif source[j] == "}": | |
| depth -= 1 | |
| if depth == 0: | |
| registry[fn_name] = source[start : j + 1] | |
| break | |
| return registry | |
| def game_slug(js_path: Path) -> str: | |
| """Derive a clean slug from the game directory name.""" | |
| # parent chain: .../playground/<game>/sandbox/[src/]file.js | |
| parts = js_path.parts | |
| try: | |
| pg_idx = next(i for i, p in enumerate(parts) if p == "playground") | |
| game_name = parts[pg_idx + 1] | |
| except (StopIteration, IndexError): | |
| game_name = js_path.parent.parent.name | |
| return re.sub(r"[^a-zA-Z0-9]+", "_", game_name).strip("_").lower() | |
| # โโ Collect all JS files to scan โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| js_files: list[Path] = [] | |
| for pattern in SCAN_PATTERNS: | |
| for f in sorted(PLAYGROUND_BASE.glob(pattern)): | |
| if f not in js_files: | |
| js_files.append(f) | |
| print(f"Files to scan: {len(js_files)}") | |
| for f in js_files: | |
| print(f" {f}") | |
| # โโ Extract, resolving buildHero collisions by namespacing โโโโโโโโโโโโโโโโโโโโ | |
| registry: dict[str, str] = {} | |
| hero_sources: dict[str, str] = {} # fn_name โ game_slug for collision tracking | |
| for js_file in js_files: | |
| slug = game_slug(js_file) | |
| try: | |
| text = js_file.read_text(errors="ignore") | |
| except OSError as e: | |
| print(f" WARNING: could not read {js_file}: {e}") | |
| continue | |
| found = extract_functions(text) | |
| print(f" {js_file.name} ({slug}): {len(found)} functions found") | |
| for fn_name, fn_body in found.items(): | |
| if fn_name == "buildHero": | |
| namespaced = f"buildHero_{slug}" | |
| # Rename the function declaration to match the namespaced key | |
| fn_body_renamed = fn_body.replace( | |
| f"function buildHero(", f"function {namespaced}(", 1 | |
| ) | |
| registry[namespaced] = fn_body_renamed | |
| print(f" โณ buildHero โ {namespaced}") | |
| else: | |
| if fn_name in registry: | |
| print(f" โณ {fn_name} already seen, skipping duplicate") | |
| else: | |
| registry[fn_name] = fn_body | |
| print(f"\nTotal unique build functions: {len(registry)}") | |
| # โโ Character functions (names containing 'Character', 'Songoku', 'Hero', or Pokemon names) โโ | |
| character_fns = sorted([ | |
| k for k in registry | |
| if any(tag in k for tag in ["Character", "Hero", "Songoku", | |
| "Raticate", "Persian", "Tauros", "Snorlax", | |
| "Graveler", "Onix", "Zubat", "Golbat", | |
| "Pidgey", "Fearow", "Beedrill", "Butterfree", | |
| "Voltorb", "Electrode", "Jigglypuff", "Abra", | |
| "Alakazam", "Gengar", "Doduo", "Rapidash"]) | |
| ]) | |
| print(f"\nCharacter functions ({len(character_fns)}): {character_fns}") | |
| # โโ Write outputs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| json_path = OUT_DIR / "characters_registry.json" | |
| json_path.write_text(json.dumps(registry, indent=2)) | |
| print(f"\nWritten: {json_path} ({json_path.stat().st_size} bytes)") | |
| js_path = OUT_DIR / "characters.js" | |
| js_path.write_text("\n\n".join(registry.values()) + "\n") | |
| print(f"Written: {js_path} ({js_path.stat().st_size} bytes)") | |