Text Generation
Transformers
Safetensors
qwen2
godot
gdscript
qlora
game-development
conversational
text-generation-inference
Instructions to use khursheed/VibeGame-8B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use khursheed/VibeGame-8B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="khursheed/VibeGame-8B") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("khursheed/VibeGame-8B") model = AutoModelForCausalLM.from_pretrained("khursheed/VibeGame-8B", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use khursheed/VibeGame-8B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "khursheed/VibeGame-8B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khursheed/VibeGame-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/khursheed/VibeGame-8B
- SGLang
How to use khursheed/VibeGame-8B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "khursheed/VibeGame-8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khursheed/VibeGame-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "khursheed/VibeGame-8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khursheed/VibeGame-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use khursheed/VibeGame-8B with Docker Model Runner:
docker model run hf.co/khursheed/VibeGame-8B
Download code/scripts/create_teacher_batch_a.py from khursheed/VibeGame-8B: direct link, hf CLI and curl.
- Browser
- Download file 82.3 kB
-
https://huggingface.co/khursheed/VibeGame-8B/resolve/main/code/scripts/create_teacher_batch_a.py
- Command line
-
hf download hf://khursheed/VibeGame-8B/code/scripts/create_teacher_batch_a.py
-
curl -L -o create_teacher_batch_a.py https://huggingface.co/khursheed/VibeGame-8B/resolve/main/code/scripts/create_teacher_batch_a.py
82.3 kB
| #!/usr/bin/env python3 | |
| """Original Codex targets for train-only prompts 009–020 and 045–064; Linux admission required.""" | |
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| import sys | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / 'src')) | |
| from vibegame.project import parse_project | |
| from vibegame.seeds import PROJECT, SCENE | |
| COMMON = ''' | |
| func _ready() -> void: | |
| \trestart() | |
| func _unhandled_input(event: InputEvent) -> void: | |
| \tif event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_R: | |
| \t\trestart() | |
| \t\tqueue_redraw() | |
| \t\treturn | |
| \tgame_input(event) | |
| func label(s: String, p: Vector2, size: int = 22, color: Color = Color.WHITE) -> void: | |
| \tdraw_string(ThemeDB.fallback_font, p, s, HORIZONTAL_ALIGNMENT_LEFT, -1, size, color) | |
| func header(title: String, detail: String) -> void: | |
| \tdraw_rect(Rect2(0, 0, 960, 92), Color("14213a")) | |
| \tlabel(title, Vector2(26, 38), 28, Color("63f5d2")) | |
| \tlabel(detail, Vector2(26, 72), 18, Color("c9d5e9")) | |
| ''' | |
| GAMES = { | |
| 9: ('Ice Circuit', '''extends Node2D | |
| var tile = Vector2i(1, 1) | |
| var visual = Vector2.ZERO | |
| var marks: Array[Vector2i] = [] | |
| var steps = 0 | |
| var won = false | |
| func center(c: Vector2i) -> Vector2: | |
| \treturn Vector2(235, 120) + (Vector2(c) + Vector2(0.5, 0.5)) * 70 | |
| func blocked(c: Vector2i) -> bool: | |
| \treturn c.x <= 0 or c.y <= 0 or c.x >= 6 or c.y >= 5 or c == Vector2i(3, 2) or c == Vector2i(3, 3) | |
| func restart() -> void: | |
| \ttile = Vector2i(1, 1) | |
| \tvisual = center(tile) | |
| \tmarks.assign([Vector2i(3, 1), Vector2i(5, 1), Vector2i(5, 2), Vector2i(5, 4), Vector2i(3, 4), Vector2i(1, 4), Vector2i(1, 3)]) | |
| \tsteps = 0 | |
| \twon = false | |
| func game_input(event: InputEvent) -> void: | |
| \tif won or visual.distance_to(center(tile)) > 1 or not (event is InputEventKey): | |
| \t\treturn | |
| \tif not event.pressed or event.echo: | |
| \t\treturn | |
| \tvar direction = Vector2i.ZERO | |
| \tif event.keycode == KEY_LEFT: direction = Vector2i.LEFT | |
| \tif event.keycode == KEY_RIGHT: direction = Vector2i.RIGHT | |
| \tif event.keycode == KEY_UP: direction = Vector2i.UP | |
| \tif event.keycode == KEY_DOWN: direction = Vector2i.DOWN | |
| \tif direction == Vector2i.ZERO or blocked(tile + direction): | |
| \t\treturn | |
| \tsteps += 1 | |
| \twhile not blocked(tile + direction): | |
| \t\ttile += direction | |
| \t\tmarks.erase(tile) | |
| \t\tif tile == Vector2i(1, 1) and marks.is_empty(): | |
| \t\t\twon = true | |
| func _process(dt: float) -> void: | |
| \tvisual = visual.move_toward(center(tile), dt * 680) | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("ICE CIRCUIT", "Arrow: slide to a wall / Visit all gold tiles, then return to EXIT / R: restart") | |
| \tfor y in range(6): | |
| \t\tfor x in range(7): | |
| \t\t\tvar c = Vector2i(x, y) | |
| \t\t\tdraw_rect(Rect2(center(c) - Vector2(33, 33), Vector2(66, 66)), Color("3b5368") if blocked(c) else Color("244366")) | |
| \tdraw_rect(Rect2(center(Vector2i(1, 1)) - Vector2(25, 25), Vector2(50, 50)), Color("4a866e")) | |
| \tfor c in marks: | |
| \t\tdraw_circle(center(c), 12, Color("ffdc72")) | |
| \tdraw_circle(visual, 20, Color("68edff")) | |
| \tlabel("EXIT", center(Vector2i(1, 1)) + Vector2(-24, 31), 15) | |
| \tlabel("Marked tiles: %d / 7 Slides: %d" % [7 - marks.size(), steps], Vector2(260, 588)) | |
| \tif won: label("Circuit complete! R to play again.", Vector2(265, 620), 25) | |
| '''), | |
| 10: ('Magnet Cups', '''extends Node2D | |
| var balls: Array[Vector2] = [] | |
| var speeds: Array[Vector2] = [] | |
| var home_cup: Array[int] = [] | |
| var filled = [false, false, false] | |
| var cups = [Vector2(220, 535), Vector2(480, 535), Vector2(740, 535)] | |
| var attracting = true | |
| var captured = 0 | |
| func restart() -> void: | |
| \tballs.assign([Vector2(240, 180), Vector2(480, 215), Vector2(720, 180)]) | |
| \tspeeds.assign([Vector2.ZERO, Vector2.ZERO, Vector2.ZERO]) | |
| \thome_cup.assign([-1, -1, -1]) | |
| \tfilled = [false, false, false] | |
| \tattracting = true | |
| \tcaptured = 0 | |
| func game_input(event: InputEvent) -> void: | |
| \tif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\tattracting = not attracting | |
| \tif event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_SPACE: | |
| \t\tattracting = not attracting | |
| func _process(dt: float) -> void: | |
| \tvar magnet = get_global_mouse_position().clamp(Vector2(25, 110), Vector2(935, 610)) | |
| \tfor i in range(3): | |
| \t\tif home_cup[i] >= 0: continue | |
| \t\tvar offset = magnet - balls[i] | |
| \t\tvar strength = maxf(0, 1 - offset.length() / 360.0) * 490.0 | |
| \t\tspeeds[i] += offset.normalized() * strength * dt * (1 if attracting else -1) | |
| \t\tspeeds[i] *= maxf(0, 1 - dt * 2.1) | |
| \t\tballs[i] += speeds[i] * dt | |
| \t\tif balls[i].x < 20 or balls[i].x > 940: speeds[i].x *= -0.5 | |
| \t\tif balls[i].y < 112 or balls[i].y > 610: speeds[i].y *= -0.5 | |
| \t\tballs[i] = balls[i].clamp(Vector2(20, 112), Vector2(940, 610)) | |
| \t\tfor j in range(3): | |
| \t\t\tif not filled[j] and balls[i].distance_to(cups[j]) < 30: | |
| \t\t\t\tfilled[j] = true | |
| \t\t\t\thome_cup[i] = j | |
| \t\t\t\tballs[i] = cups[j] | |
| \t\t\t\tcaptured += 1 | |
| \t\t\t\tbreak | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("MAGNET CUPS", "Move mouse: magnet / Click or Space: attract-repel / Fill all three cups / R: restart") | |
| \tfor j in range(3): | |
| \t\tdraw_rect(Rect2(cups[j] - Vector2(39, 10), Vector2(78, 44)), Color("386552") if filled[j] else Color("516379")) | |
| \t\tdraw_arc(cups[j], 36, 0, PI, 18, Color("d5e8f5"), 5) | |
| \tfor b in balls: draw_circle(b, 13, Color("d8e4ed")) | |
| \tvar magnet = get_global_mouse_position().clamp(Vector2(25, 110), Vector2(935, 610)) | |
| \tdraw_circle(magnet, 20, Color("ef6078") if attracting else Color("68b8ff")) | |
| \tlabel("+" if attracting else "-", magnet + Vector2(-7, 8), 24) | |
| \tlabel("%s Cups %d / 3" % ["ATTRACT" if attracting else "REPEL", captured], Vector2(28, 620)) | |
| \tif captured == 3: label("All metal collected! R for another round.", Vector2(230, 330), 27) | |
| '''), | |
| 11: ('Rescue Harbor', '''extends Node2D | |
| var boat = Vector2(110, 535) | |
| var people: Array[Vector2] = [] | |
| var rocks = [Vector2(320, 320), Vector2(555, 250), Vector2(670, 475), Vector2(245, 150)] | |
| var dock = Rect2(35, 455, 165, 145) | |
| var cargo = 0 | |
| var rescued = 0 | |
| var left = 95.0 | |
| var done = false | |
| func restart() -> void: | |
| \tboat = Vector2(110, 535) | |
| \tpeople.assign([Vector2(790, 135), Vector2(840, 280), Vector2(825, 470), Vector2(460, 145), Vector2(470, 535), Vector2(145, 300)]) | |
| \tcargo = 0 | |
| \trescued = 0 | |
| \tleft = 95 | |
| \tdone = false | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif not done: | |
| \t\tleft -= dt | |
| \t\tvar old = boat | |
| \t\tboat += Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 255 * dt | |
| \t\tboat = boat.clamp(Vector2(20, 112), Vector2(940, 608)) | |
| \t\tfor rock in rocks: | |
| \t\t\tif boat.distance_to(rock) < 58: boat = old | |
| \t\tfor i in range(people.size() - 1, -1, -1): | |
| \t\t\tif cargo < 3 and boat.distance_to(people[i]) < 30: | |
| \t\t\t\tcargo += 1 | |
| \t\t\t\tpeople.remove_at(i) | |
| \t\tif dock.has_point(boat): | |
| \t\t\trescued += cargo | |
| \t\t\tcargo = 0 | |
| \t\tdone = rescued == 6 or left <= 0 | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("RESCUE HARBOR", "Arrows: steer / Touch survivors, then return to dock / Capacity: three passengers / R: restart") | |
| \tdraw_rect(Rect2(0, 95, 960, 545), Color("12334d")) | |
| \tdraw_rect(dock, Color("627154")) | |
| \tlabel("DOCK", dock.position + Vector2(40, 80), 23) | |
| \tfor rock in rocks: | |
| \t\tdraw_circle(rock, 42, Color("6d7484")) | |
| \t\tdraw_circle(rock + Vector2(-8, -9), 23, Color("8b94a1")) | |
| \tfor person in people: | |
| \t\tdraw_circle(person, 15, Color("ffdd73")) | |
| \t\tdraw_arc(person, 21, 0, TAU, 20, Color("fff0be"), 2) | |
| \tdraw_rect(Rect2(boat - Vector2(20, 13), Vector2(40, 26)), Color("5ff0cf")) | |
| \tfor i in range(cargo): draw_circle(boat + Vector2(i * 12 - 12, 0), 4, Color("ffdc73")) | |
| \tlabel("Rescued %d / 6 Aboard %d / 3 Time %.0f" % [rescued, cargo, maxf(left, 0)], Vector2(260, 620)) | |
| \tif done: label("Everyone is safe! R to restart" if rescued == 6 else "Time ran out. R to restart", Vector2(285, 335), 26) | |
| '''), | |
| 12: ('Three Ball Juggle', '''extends Node2D | |
| var balls: Array[Vector2] = [] | |
| var speeds: Array[Vector2] = [] | |
| var score = 0 | |
| var misses = 0 | |
| var done = false | |
| func restart() -> void: | |
| \tballs.assign([Vector2(180, 330), Vector2(650, 170), Vector2(790, 290)]) | |
| \tspeeds.assign([Vector2(65, -70), Vector2(-80, 0), Vector2(55, -110)]) | |
| \tscore = 0 | |
| \tmisses = 0 | |
| \tdone = false | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif not done: | |
| \t\tfor i in range(3): | |
| \t\t\tvar before = balls[i] | |
| \t\t\tspeeds[i].y += 440 * dt | |
| \t\t\tballs[i] += speeds[i] * dt | |
| \t\t\tif balls[i].x < 18 or balls[i].x > 942: | |
| \t\t\t\tspeeds[i].x = -speeds[i].x | |
| \t\t\t\tballs[i].x = clampf(balls[i].x, 18, 942) | |
| \t\t\tif before.y >= 220 and balls[i].y < 220 and speeds[i].y < 0: score += 1 | |
| \t\t\tfor side in range(2): | |
| \t\t\t\tvar held = Input.is_action_pressed("ui_left" if side == 0 else "ui_right") | |
| \t\t\t\tvar py = 530.0 if held else 580.0 | |
| \t\t\t\tvar px = 30 + side * 465 | |
| \t\t\t\tif balls[i].x >= px and balls[i].x <= px + 430 and before.y <= py and balls[i].y >= py - 10 and speeds[i].y > 0: | |
| \t\t\t\t\tballs[i].y = py - 11 | |
| \t\t\t\t\tspeeds[i].y = -565 if held else -295 | |
| \t\t\tif balls[i].y > 670: | |
| \t\t\t\tmisses += 1 | |
| \t\t\t\tballs[i] = Vector2(180 + i * 290, 180) | |
| \t\t\t\tspeeds[i].y = 0 | |
| \t\tdone = misses >= 3 or score >= 30 | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("THREE BALL JUGGLE", "Hold Left / Right: lift the matching paddle / Reach the gold height 30 times / R: restart") | |
| \tdraw_line(Vector2(20, 220), Vector2(940, 220), Color("c5a858"), 2) | |
| \tlabel("SCORE HEIGHT", Vector2(395, 205), 18, Color("ffe18b")) | |
| \tfor side in range(2): | |
| \t\tvar held = Input.is_action_pressed("ui_left" if side == 0 else "ui_right") | |
| \t\tdraw_rect(Rect2(30 + side * 465, 530 if held else 580, 430, 14), Color("66eccd") if held else Color("557786")) | |
| \tfor i in range(3): draw_circle(balls[i], 11, Color.from_hsv(float(i) / 3, 0.6, 1)) | |
| \tlabel("Height hits %d / 30 Misses %d / 3" % [score, misses], Vector2(290, 625)) | |
| \tif done: label("Juggle complete! R to restart" if score >= 30 else "Three balls dropped. R to restart", Vector2(260, 365), 26) | |
| '''), | |
| 13: ('Dispatch Four', '''extends Node2D | |
| var crates: Array[Vector2] = [] | |
| var departures: Array[Vector2] = [] | |
| var cooldown = [0.0, 0.0, 0.0, 0.0] | |
| var clock_time = 0.0 | |
| var spawn_time = 0.0 | |
| var next_lane = 0 | |
| var delivered = 0 | |
| var strikes = 0 | |
| var notice = "Wait for a crate and a green exit." | |
| func restart() -> void: | |
| \tcrates.clear() | |
| \tdepartures.clear() | |
| \tcooldown = [0.0, 0.0, 0.0, 0.0] | |
| \tclock_time = 0 | |
| \tspawn_time = 0 | |
| \tnext_lane = 0 | |
| \tdelivered = 0 | |
| \tstrikes = 0 | |
| \tnotice = "Wait for a crate and a green exit." | |
| func clear_lane(lane: int) -> bool: | |
| \treturn cooldown[lane] <= 0 and fmod(clock_time + lane * 1.4, 6.3) >= 2.1 | |
| func game_input(event: InputEvent) -> void: | |
| \tif delivered >= 16 or strikes >= 3 or not (event is InputEventKey): return | |
| \tif not event.pressed or event.echo: return | |
| \tvar lane = -1 | |
| \tif event.keycode == KEY_1: lane = 0 | |
| \tif event.keycode == KEY_2: lane = 1 | |
| \tif event.keycode == KEY_3: lane = 2 | |
| \tif event.keycode == KEY_4: lane = 3 | |
| \tif lane < 0: return | |
| \tfor i in range(crates.size()): | |
| \t\tif int(crates[i].y) == lane and crates[i].x >= 424: | |
| \t\t\tif clear_lane(lane): | |
| \t\t\t\tdepartures.append(Vector2(470, lane)) | |
| \t\t\t\tcrates.remove_at(i) | |
| \t\t\t\tcooldown[lane] = 1.4 | |
| \t\t\t\tdelivered += 1 | |
| \t\t\t\tnotice = "Dispatched safely." | |
| \t\t\telse: | |
| \t\t\t\tstrikes += 1 | |
| \t\t\t\tnotice = "Exit blocked! Wait for green." | |
| \t\t\treturn | |
| func _process(dt: float) -> void: | |
| \tif delivered < 16 and strikes < 3: | |
| \t\tclock_time += dt | |
| \t\tspawn_time -= dt | |
| \t\tfor lane in range(4): cooldown[lane] = maxf(0, cooldown[lane] - dt) | |
| \t\tif spawn_time <= 0: | |
| \t\t\tcrates.append(Vector2(50, next_lane)) | |
| \t\t\tnext_lane = (next_lane + 1) % 4 | |
| \t\t\tspawn_time = 1.6 | |
| \t\tfor i in range(crates.size()): | |
| \t\t\tvar limit_x = 435.0 | |
| \t\t\tfor j in range(i): | |
| \t\t\t\tif crates[j].y == crates[i].y: limit_x = minf(limit_x, crates[j].x - 56) | |
| \t\t\tcrates[i].x = minf(crates[i].x + dt * 100, limit_x) | |
| \t\t\tif crates[i].x < -10: | |
| \t\t\t\tstrikes = 3 | |
| \t\t\t\tnotice = "Warehouse overflow." | |
| \t\tfor i in range(departures.size() - 1, -1, -1): | |
| \t\t\tdepartures[i].x += 350 * dt | |
| \t\t\tif departures[i].x > 980: departures.remove_at(i) | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("DISPATCH FOUR", "Keys 1 / 2 / 3 / 4: release waiting crate only on GREEN / Send sixteen / R: restart") | |
| \tfor lane in range(4): | |
| \t\tvar y = 150 + lane * 108 | |
| \t\tdraw_rect(Rect2(30, y, 900, 68), Color("25364c")) | |
| \t\tdraw_rect(Rect2(487, y, 12, 68), Color("63e5b3") if clear_lane(lane) else Color("ef687a")) | |
| \t\tlabel(str(lane + 1), Vector2(8, y + 43), 22) | |
| \tfor c in crates: draw_rect(Rect2(c.x - 22, 164 + int(c.y) * 108, 44, 40), Color("f8cb72")) | |
| \tfor c in departures: draw_rect(Rect2(c.x - 22, 164 + int(c.y) * 108, 44, 40), Color("71e7ce")) | |
| \tlabel("Sent %d / 16 Mistakes %d / 3" % [delivered, strikes], Vector2(30, 608)) | |
| \tlabel("Shift complete! R to restart" if delivered >= 16 else ("Shift failed! R to restart" if strikes >= 3 else notice), Vector2(405, 608), 20) | |
| '''), | |
| 14: ('Bridge Switchyard', '''extends Node2D | |
| var route = [Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2), Vector2i(2, 1), Vector2i(2, 0), Vector2i(3, 0), Vector2i(4, 0), Vector2i(5, 0), Vector2i(5, 1), Vector2i(5, 2), Vector2i(5, 3)] | |
| var directions = [Vector2i.UP, Vector2i.RIGHT, Vector2i.DOWN, Vector2i.LEFT] | |
| var bridges = {} | |
| var ball = Vector2i.ZERO | |
| var entry = 8 | |
| var ticking = 0.0 | |
| var rolling = false | |
| var outcome = "" | |
| func middle(c: Vector2i) -> Vector2: | |
| \treturn Vector2(220, 145) + (Vector2(c) + Vector2(0.5, 0.5)) * 85 | |
| func bit_for(d: Vector2i) -> int: | |
| \tfor i in range(4): | |
| \t\tif directions[i] == d: return 1 << i | |
| \treturn 0 | |
| func restart() -> void: | |
| \tbridges.clear() | |
| \tfor i in range(route.size()): | |
| \t\tvar incoming = Vector2i.LEFT if i == 0 else route[i - 1] - route[i] | |
| \t\tvar outgoing = Vector2i.DOWN if i == route.size() - 1 else route[i + 1] - route[i] | |
| \t\tvar mask = bit_for(incoming) | bit_for(outgoing) | |
| \t\tfor turn in range((i * 3 + 1) % 4): mask = ((mask << 1) & 15) | (mask >> 3) | |
| \t\tbridges[route[i]] = mask | |
| \tball = route[0] | |
| \tentry = 8 | |
| \trolling = false | |
| \toutcome = "" | |
| \tticking = 0 | |
| func game_input(event: InputEvent) -> void: | |
| \tif rolling or outcome != "": return | |
| \tif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\tvar mouse = get_global_mouse_position() | |
| \t\tvar cell = Vector2i(floor((mouse.x - 220) / 85), floor((mouse.y - 145) / 85)) | |
| \t\tif bridges.has(cell): | |
| \t\t\tvar mask = int(bridges[cell]) | |
| \t\t\tbridges[cell] = ((mask << 1) & 15) | (mask >> 3) | |
| \tif event is InputEventKey and event.pressed and event.keycode == KEY_SPACE: | |
| \t\trolling = true | |
| \t\tticking = 0.35 | |
| func _process(dt: float) -> void: | |
| \tif rolling: | |
| \t\tticking -= dt | |
| \t\tif ticking <= 0: | |
| \t\t\tticking = 0.35 | |
| \t\t\tvar mask = int(bridges.get(ball, 0)) | |
| \t\t\tif (mask & entry) == 0: | |
| \t\t\t\toutcome = "Gap! R to reset the puzzle." | |
| \t\t\t\trolling = false | |
| \t\t\telif ball == route[-1]: | |
| \t\t\t\toutcome = "Goal reached! R to play again." | |
| \t\t\t\trolling = false | |
| \t\t\telse: | |
| \t\t\t\tfor i in range(4): | |
| \t\t\t\t\tif (mask & (1 << i)) != 0 and (1 << i) != entry: | |
| \t\t\t\t\t\tball += directions[i] | |
| \t\t\t\t\t\tentry = 1 << ((i + 2) % 4) | |
| \t\t\t\t\t\tbreak | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("BRIDGE SWITCHYARD", "Click a bridge to rotate / Connect START to GOAL / Space: roll ball / R: reset") | |
| \tfor y in range(4): | |
| \t\tfor x in range(6): draw_rect(Rect2(middle(Vector2i(x, y)) - Vector2(40, 40), Vector2(80, 80)), Color("263347")) | |
| \tfor c in bridges: | |
| \t\tfor i in range(4): | |
| \t\t\tif (int(bridges[c]) & (1 << i)) != 0: draw_line(middle(c), middle(c) + Vector2(directions[i]) * 42, Color("98b8c9"), 22) | |
| \tdraw_circle(middle(route[-1]), 15, Color("69ecc3")) | |
| \tdraw_circle(middle(ball), 13, Color("ffe187")) | |
| \tlabel("START", middle(route[0]) + Vector2(-35, 34), 16) | |
| \tlabel("GOAL", middle(route[-1]) + Vector2(-28, 35), 16) | |
| \tlabel(outcome if outcome != "" else "Rotate the bridges before you launch.", Vector2(260, 585), 24) | |
| '''), | |
| 15: ('Key After Dark', '''extends Node2D | |
| var player = Vector2(80, 545) | |
| var key_pos = Vector2(855, 525) | |
| var exit_box = Rect2(820, 110, 100, 70) | |
| var walls = [Rect2(380, 195, 80, 260), Rect2(155, 340, 92, 86), Rect2(705, 195, 82, 72)] | |
| var guards = [Vector2(330, 275), Vector2(635, 365)] | |
| var angles = [0.0, 0.0] | |
| var elapsed = 0.0 | |
| var has_key = false | |
| var outcome = "" | |
| func restart() -> void: | |
| \tplayer = Vector2(80, 545) | |
| \telapsed = 0 | |
| \thas_key = false | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func exposed(g: Vector2, angle: float) -> bool: | |
| \tvar offset = player - g | |
| \tif offset.length() > 225 or absf(wrapf(offset.angle() - angle, -PI, PI)) > 0.42: return false | |
| \tfor i in range(1, 21): | |
| \t\tvar point = g.lerp(player, float(i) / 20) | |
| \t\tfor wall in walls: | |
| \t\t\tif wall.has_point(point): return false | |
| \treturn true | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\telapsed += dt | |
| \t\tvar old = player | |
| \t\tplayer += Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 220 * dt | |
| \t\tplayer = player.clamp(Vector2(20, 112), Vector2(940, 609)) | |
| \t\tfor wall in walls: | |
| \t\t\tif wall.grow(11).has_point(player): player = old | |
| \t\tangles[0] = elapsed * 0.58 | |
| \t\tangles[1] = -elapsed * 0.47 + PI | |
| \t\tfor i in range(2): | |
| \t\t\tif exposed(guards[i], angles[i]): outcome = "Spotted! R to try again." | |
| \t\tif player.distance_to(key_pos) < 25: has_key = true | |
| \t\tif has_key and exit_box.has_point(player): outcome = "Escaped with the key! R to replay." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("KEY AFTER DARK", "Arrows: sneak / Gold key unlocks green exit / Walls block guard sight / R: restart") | |
| \tdraw_rect(exit_box, Color("47785e") if has_key else Color("424b60")) | |
| \tlabel("EXIT", exit_box.position + Vector2(21, 43), 23) | |
| \tfor i in range(2): | |
| \t\tvar g = guards[i] | |
| \t\tvar a = angles[i] | |
| \t\tdraw_colored_polygon(PackedVector2Array([g, g + Vector2.from_angle(a - 0.42) * 225, g + Vector2.from_angle(a + 0.42) * 225]), Color(1, 0.4, 0.35, 0.26)) | |
| \t\tdraw_circle(g, 13, Color("f77d87")) | |
| \tfor wall in walls: draw_rect(wall, Color("536477")) | |
| \tif not has_key: | |
| \t\tdraw_circle(key_pos, 11, Color("ffdb72")) | |
| \t\tdraw_line(key_pos, key_pos + Vector2(22, 0), Color("ffdb72"), 6) | |
| \tdraw_circle(player, 11, Color("6af3d2")) | |
| \tlabel("Key: %s" % ("COLLECTED" if has_key else "missing"), Vector2(30, 620)) | |
| \tif outcome != "": label(outcome, Vector2(275, 620), 25) | |
| '''), | |
| 16: ('Parking Escape', '''extends Node2D | |
| var cars = [] | |
| var selected = 0 | |
| var moves = 0 | |
| var won = false | |
| func restart() -> void: | |
| \tcars = [{"p":Vector2i(0, 2), "n":2, "h":true}, {"p":Vector2i(2, 1), "n":2, "h":false}, {"p":Vector2i(4, 0), "n":3, "h":false}, {"p":Vector2i(3, 4), "n":2, "h":true}, {"p":Vector2i(0, 0), "n":2, "h":true}, {"p":Vector2i(5, 3), "n":2, "h":false}] | |
| \tselected = 0 | |
| \tmoves = 0 | |
| \twon = false | |
| func extent(car: Dictionary) -> Vector2i: | |
| \treturn Vector2i(car.n, 1) if car.h else Vector2i(1, car.n) | |
| func picture(car: Dictionary) -> Rect2: | |
| \treturn Rect2(Vector2(240, 145) + Vector2(car.p) * 70 + Vector2(4, 4), Vector2(extent(car)) * 70 - Vector2(8, 8)) | |
| func try_move(direction: Vector2i) -> void: | |
| \tvar car = cars[selected] | |
| \tif (car.h and direction.y != 0) or (not car.h and direction.x != 0): return | |
| \tvar target = car.p + direction | |
| \tvar size_cells = extent(car) | |
| \tif target.x < 0 or target.y < 0 or target.y + size_cells.y > 6: return | |
| \tif selected != 0 and target.x + size_cells.x > 6: return | |
| \tif selected == 0 and target.x > 6: return | |
| \tvar region = Rect2i(target, size_cells) | |
| \tfor i in range(cars.size()): | |
| \t\tif i != selected and region.intersects(Rect2i(cars[i].p, extent(cars[i]))): return | |
| \tcars[selected].p = target | |
| \tmoves += 1 | |
| \twon = cars[0].p.x >= 6 | |
| func game_input(event: InputEvent) -> void: | |
| \tif won: return | |
| \tif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\tfor i in range(cars.size()): | |
| \t\t\tif picture(cars[i]).has_point(get_global_mouse_position()): selected = i | |
| \tif event is InputEventKey and event.pressed and not event.echo: | |
| \t\tif event.keycode == KEY_LEFT: try_move(Vector2i.LEFT) | |
| \t\tif event.keycode == KEY_RIGHT: try_move(Vector2i.RIGHT) | |
| \t\tif event.keycode == KEY_UP: try_move(Vector2i.UP) | |
| \t\tif event.keycode == KEY_DOWN: try_move(Vector2i.DOWN) | |
| func _process(_dt: float) -> void: | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("PARKING ESCAPE", "Click a car / Arrows slide along its length / Free the RED car through right gap / R: restart") | |
| \tfor y in range(6): | |
| \t\tfor x in range(6): draw_rect(Rect2(240 + x * 70, 145 + y * 70, 68, 68), Color("29384e")) | |
| \tdraw_rect(Rect2(660, 285, 170, 70), Color("366c5c")) | |
| \tlabel("EXIT", Vector2(709, 328), 24) | |
| \tfor i in range(cars.size()): | |
| \t\tvar box = picture(cars[i]) | |
| \t\tdraw_rect(box, Color("ff6c7f") if i == 0 else Color.from_hsv(float(i) / 8, 0.45, 0.85)) | |
| \t\tif i == selected: draw_rect(box.grow(2), Color.WHITE, false, 3) | |
| \t\tlabel(str(i + 1), box.position + Vector2(14, 31), 23) | |
| \tlabel("Moves: %d" % moves, Vector2(250, 610)) | |
| \tif won: label("Red car escaped! R to reset.", Vector2(445, 610), 25) | |
| '''), | |
| 17: ('Two Sensor Laser', '''extends Node2D | |
| var mirrors = [Vector2i(3, 2), Vector2i(3, 0), Vector2i(5, 0), Vector2i(5, 3), Vector2i(7, 3)] | |
| var slants = [1, 0, 0, 1, 1] | |
| var sensors = [Vector2i(2, 2), Vector2i(5, 1)] | |
| var lit = [false, false] | |
| var beam: Array[Vector2] = [] | |
| var won = false | |
| func middle(c: Vector2i) -> Vector2: | |
| \treturn Vector2(180, 170) + (Vector2(c) + Vector2(0.5, 0.5)) * 70 | |
| func restart() -> void: | |
| \tslants = [1, 0, 0, 1, 1] | |
| \ttrace_beam() | |
| func trace_beam() -> void: | |
| \tbeam.clear() | |
| \tlit = [false, false] | |
| \twon = false | |
| \tvar cell = Vector2i(0, 2) | |
| \tvar direction = Vector2i.RIGHT | |
| \tbeam.append(middle(cell)) | |
| \tfor step in range(64): | |
| \t\tcell += direction | |
| \t\tbeam.append(middle(cell)) | |
| \t\tif cell.x < 0 or cell.x >= 8 or cell.y < 0 or cell.y >= 5: break | |
| \t\tfor j in range(2): | |
| \t\t\tif cell == sensors[j]: lit[j] = true | |
| \t\tif cell == Vector2i(7, 0): | |
| \t\t\twon = lit[0] and lit[1] | |
| \t\t\tbreak | |
| \t\tvar mirror_index = mirrors.find(cell) | |
| \t\tif mirror_index >= 0: | |
| \t\t\tdirection = Vector2i(-direction.y, -direction.x) if slants[mirror_index] == 0 else Vector2i(direction.y, direction.x) | |
| func game_input(event: InputEvent) -> void: | |
| \tif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT and not won: | |
| \t\tvar mouse = get_global_mouse_position() | |
| \t\tfor i in range(mirrors.size()): | |
| \t\t\tif middle(mirrors[i]).distance_to(mouse) < 32: | |
| \t\t\t\tslants[i] = 1 - slants[i] | |
| \t\t\t\ttrace_beam() | |
| func _process(_dt: float) -> void: | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("TWO SENSOR LASER", "Click mirrors: flip diagonal / Light BOTH sensors before reaching receiver / R: restart") | |
| \tfor y in range(5): | |
| \t\tfor x in range(8): draw_rect(Rect2(middle(Vector2i(x, y)) - Vector2(33, 33), Vector2(66, 66)), Color("26364d")) | |
| \tif beam.size() > 1: draw_polyline(PackedVector2Array(beam), Color("ff7792"), 3) | |
| \tfor i in range(mirrors.size()): | |
| \t\tvar c = middle(mirrors[i]) | |
| \t\tvar offset = Vector2(23, -23) if slants[i] == 0 else Vector2(23, 23) | |
| \t\tdraw_line(c - offset, c + offset, Color("c0e6f5"), 7) | |
| \tfor i in range(2): | |
| \t\tdraw_circle(middle(sensors[i]), 17, Color("67ecc1") if lit[i] else Color("94784c")) | |
| \t\tlabel(str(i + 1), middle(sensors[i]) + Vector2(-6, 7), 18) | |
| \tdraw_circle(middle(Vector2i(0, 2)), 19, Color("ef5f87")) | |
| \tdraw_rect(Rect2(middle(Vector2i(7, 0)) - Vector2(24, 24), Vector2(48, 48)), Color("60dfb4") if won else Color("6981a0")) | |
| \tlabel("Receiver", middle(Vector2i(7, 0)) + Vector2(-36, -34), 16) | |
| \tlabel("Both sensors online! R to replay." if won else "Route the beam through sensors 1 and 2.", Vector2(275, 600), 25) | |
| '''), | |
| 18: ('Firefly Lantern', '''extends Node2D | |
| var player = Vector2(95, 550) | |
| var flies: Array[Vector2] = [] | |
| var hazards: Array[Vector2] = [] | |
| var bases = [Vector2(460, 190), Vector2(760, 420), Vector2(310, 350)] | |
| var elapsed = 0.0 | |
| var immunity = 0.0 | |
| var lives = 3 | |
| var light_on = false | |
| var outcome = "" | |
| func restart() -> void: | |
| \tplayer = Vector2(95, 550) | |
| \tflies.clear() | |
| \tfor i in range(10): flies.append(Vector2(140 + (i * 173) % 700, 150 + (i * 97) % 370)) | |
| \thazards.assign(bases) | |
| \telapsed = 0 | |
| \tlives = 3 | |
| \timmunity = 0 | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tlight_on = Input.is_action_pressed("ui_accept") and outcome == "" | |
| \tif outcome == "": | |
| \t\telapsed += dt | |
| \t\timmunity = maxf(0, immunity - dt) | |
| \t\tplayer += Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * (135 if light_on else 265) * dt | |
| \t\tplayer = player.clamp(Vector2(20, 115), Vector2(940, 610)) | |
| \t\tfor i in range(flies.size() - 1, -1, -1): | |
| \t\t\tif light_on and flies[i].distance_to(player) < 260: | |
| \t\t\t\tflies[i] = flies[i].move_toward(player, 105 * dt) | |
| \t\t\telse: | |
| \t\t\t\tflies[i] += Vector2(sin(elapsed + i), cos(elapsed * 0.6 + i)) * 24 * dt | |
| \t\t\tflies[i] = flies[i].clamp(Vector2(25, 125), Vector2(930, 590)) | |
| \t\t\tif light_on and flies[i].distance_to(player) < 22: flies.remove_at(i) | |
| \t\tfor i in range(3): | |
| \t\t\tif light_on and hazards[i].distance_to(player) < 450: | |
| \t\t\t\thazards[i] = hazards[i].move_toward(player, 145 * dt) | |
| \t\t\telse: | |
| \t\t\t\thazards[i] = hazards[i].move_toward(bases[i], 50 * dt) | |
| \t\t\tif hazards[i].distance_to(player) < 33 and immunity <= 0: | |
| \t\t\t\tlives -= 1 | |
| \t\t\t\timmunity = 1.5 | |
| \t\tif flies.is_empty(): outcome = "All ten fireflies collected! R to replay." | |
| \t\telif lives <= 0 or elapsed >= 75: outcome = "Lantern lost. R to try again." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("FIREFLY LANTERN", "Arrows: move / Hold Space: attract fireflies AND hazards / Release to escape faster / R: restart") | |
| \tif light_on: draw_circle(player, 260, Color(1, 0.9, 0.3, 0.1)) | |
| \tfor f in flies: | |
| \t\tdraw_circle(f, 13, Color(1, 0.85, 0.3, 0.17)) | |
| \t\tdraw_circle(f, 5, Color("ffe076")) | |
| \tfor h in hazards: draw_circle(h, 22, Color("fa6e91")) | |
| \tif immunity <= 0 or int(immunity * 10) % 2 == 0: draw_circle(player, 12, Color("72f3d6")) | |
| \tlabel("Fireflies %d / 10 Lives %d Time %.0f" % [10 - flies.size(), maxi(lives, 0), maxf(75 - elapsed, 0)], Vector2(30, 622)) | |
| \tif outcome != "": label(outcome, Vector2(245, 335), 25) | |
| '''), | |
| 19: ('Circle Push', '''extends Node2D | |
| var hub = Vector2(480, 345) | |
| var player = Vector2(480, 345) | |
| var blocks: Array[Vector2] = [] | |
| var touched: Array[bool] = [] | |
| var radius = 260.0 | |
| var elapsed = 0.0 | |
| var pushed = 0 | |
| var outcome = "" | |
| func restart() -> void: | |
| \tplayer = hub | |
| \tblocks.clear() | |
| \ttouched.clear() | |
| \tfor i in range(10): | |
| \t\tblocks.append(hub + Vector2.from_angle(float(i) * TAU / 10) * 188) | |
| \t\ttouched.append(false) | |
| \tradius = 260 | |
| \telapsed = 0 | |
| \tpushed = 0 | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\telapsed += dt | |
| \t\tradius = maxf(120, 260 - elapsed * 3.5) | |
| \t\tvar motion = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 245 * dt | |
| \t\tplayer += motion | |
| \t\tfor i in range(blocks.size() - 1, -1, -1): | |
| \t\t\tvar offset = blocks[i] - player | |
| \t\t\tif offset.length() < 32 and motion.length() > 0: | |
| \t\t\t\tblocks[i] = player + offset.normalized() * 32 + motion * 0.25 | |
| \t\t\t\ttouched[i] = true | |
| \t\t\tif blocks[i].distance_to(hub) > radius + 8: | |
| \t\t\t\tif touched[i]: pushed += 1 | |
| \t\t\t\tblocks.remove_at(i) | |
| \t\t\t\ttouched.remove_at(i) | |
| \t\tif player.distance_to(hub) + 11 > radius: outcome = "Outside the safe circle! R to retry." | |
| \t\telif elapsed >= 40: outcome = "Survived! R to play again." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("CIRCLE PUSH", "Arrows: move and push / Stay inside for 40 seconds / Push blocks out for bonus points / R: restart") | |
| \tdraw_circle(hub, radius, Color("244d4b")) | |
| \tdraw_arc(hub, radius, 0, TAU, 80, Color("6decc6"), 4) | |
| \tfor i in range(blocks.size()): | |
| \t\tdraw_rect(Rect2(blocks[i] - Vector2(15, 15), Vector2(30, 30)), Color("ffd475") if touched[i] else Color("b49c71")) | |
| \tdraw_circle(player, 11, Color("74dcff")) | |
| \tlabel("Survival %.1f / 40s Blocks pushed out: %d" % [minf(elapsed, 40), pushed], Vector2(225, 624)) | |
| \tif outcome != "": label(outcome, Vector2(260, 335), 25) | |
| '''), | |
| 20: ('Spark Route', '''extends Node2D | |
| var path: Array[Vector2i] = [] | |
| var targets = [Vector2i(3, 1), Vector2i(6, 3), Vector2i(9, 1)] | |
| var fired = [false, false, false] | |
| var barriers = [Vector2i(4, 2), Vector2i(4, 3), Vector2i(5, 1), Vector2i(7, 2), Vector2i(8, 2)] | |
| var progress = 0 | |
| var timer = 0.0 | |
| var running = false | |
| var outcome = "" | |
| func middle(c: Vector2i) -> Vector2: | |
| \treturn Vector2(160, 145) + (Vector2(c) + Vector2(0.5, 0.5)) * 64 | |
| func restart() -> void: | |
| \tpath.assign([Vector2i(0, 2), Vector2i(1, 2), Vector2i(2, 2)]) | |
| \tfired = [false, false, false] | |
| \tprogress = 0 | |
| \ttimer = 0.7 | |
| \trunning = false | |
| \toutcome = "" | |
| func game_input(event: InputEvent) -> void: | |
| \tif outcome != "": return | |
| \tif event is InputEventKey and event.pressed and event.keycode == KEY_SPACE: running = true | |
| \tif event is InputEventMouseButton and event.pressed: | |
| \t\tif event.button_index == MOUSE_BUTTON_RIGHT and path.size() > maxi(1, progress + 1): | |
| \t\t\tpath.pop_back() | |
| \t\tif event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\t\tvar mouse = get_global_mouse_position() | |
| \t\t\tvar cell = Vector2i(floor((mouse.x - 160) / 64), floor((mouse.y - 145) / 64)) | |
| \t\t\tvar offset = cell - path[-1] | |
| \t\t\tif cell.x >= 0 and cell.x < 10 and cell.y >= 0 and cell.y < 5 and abs(offset.x) + abs(offset.y) == 1 and not path.has(cell) and not barriers.has(cell): | |
| \t\t\t\tpath.append(cell) | |
| func _process(dt: float) -> void: | |
| \tif running and outcome == "": | |
| \t\ttimer -= dt | |
| \t\tif timer <= 0: | |
| \t\t\ttimer = 0.7 | |
| \t\t\tif progress + 1 >= path.size(): | |
| \t\t\t\toutcome = "Spark reached unfinished fuse. R to retry." | |
| \t\t\telse: | |
| \t\t\t\tprogress += 1 | |
| \t\t\t\tfor i in range(3): | |
| \t\t\t\t\tif path[progress] == targets[i]: fired[i] = true | |
| \t\t\t\tif fired[0] and fired[1] and fired[2]: outcome = "All fireworks launched! R to replay." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("SPARK ROUTE", "Click next adjacent cell: extend fuse / Right-click: undo unburned end / Space: launch / R: restart") | |
| \tfor y in range(5): | |
| \t\tfor x in range(10): | |
| \t\t\tvar c = Vector2i(x, y) | |
| \t\t\tdraw_rect(Rect2(middle(c) - Vector2(30, 30), Vector2(60, 60)), Color("526174") if barriers.has(c) else Color("25374c")) | |
| \tfor i in range(1, path.size()): draw_line(middle(path[i - 1]), middle(path[i]), Color("66523d") if i <= progress else Color("d4b67d"), 7) | |
| \tfor i in range(3): | |
| \t\tvar c = middle(targets[i]) | |
| \t\tdraw_circle(c, 17, Color("70efc9") if fired[i] else Color("e98bab")) | |
| \t\tlabel(str(i + 1), c + Vector2(-6, 7), 17) | |
| \t\tif fired[i]: | |
| \t\t\tfor ray in range(8): draw_line(c + Vector2.from_angle(ray * TAU / 8) * 22, c + Vector2.from_angle(ray * TAU / 8) * 33, Color("ffe28d"), 2) | |
| \tdraw_circle(middle(path[progress]), 10, Color("fff0a0")) | |
| \tvar count = int(fired[0]) + int(fired[1]) + int(fired[2]) | |
| \tlabel("Fireworks %d / 3 Fuse cells ahead: %d" % [count, path.size() - progress - 1], Vector2(260, 550)) | |
| \tlabel(outcome if outcome != "" else "Lay a route through all three targets; keep extending while the spark moves.", Vector2(80, 601), 21) | |
| '''), | |
| } | |
| GAMES.update({ | |
| 45: ('Keep Your Eye', '''extends Node2D | |
| var cup_x = [240.0, 480.0, 720.0] | |
| var cup_y = [350.0, 350.0, 350.0] | |
| var phase = "reveal" | |
| var timer = 1.5 | |
| var swaps = 0 | |
| var first = 0 | |
| var second = 1 | |
| var from_a = 0.0 | |
| var from_b = 0.0 | |
| var choice = -1 | |
| func restart() -> void: | |
| \tcup_x = [240.0, 480.0, 720.0] | |
| \tcup_y = [350.0, 350.0, 350.0] | |
| \tphase = "reveal" | |
| \ttimer = 1.5 | |
| \tswaps = 0 | |
| \tchoice = -1 | |
| func begin_swap() -> void: | |
| \tfirst = swaps % 3 | |
| \tsecond = (swaps + 1) % 3 | |
| \tfrom_a = cup_x[first] | |
| \tfrom_b = cup_x[second] | |
| \ttimer = 0 | |
| \tphase = "shuffle" | |
| func game_input(event: InputEvent) -> void: | |
| \tif phase != "guess": return | |
| \tif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\tfor i in range(3): | |
| \t\t\tif Rect2(cup_x[i] - 65, cup_y[i] - 85, 130, 115).has_point(get_global_mouse_position()): | |
| \t\t\t\tchoice = i | |
| \t\t\t\tphase = "result" | |
| func _process(dt: float) -> void: | |
| \tif phase == "reveal": | |
| \t\ttimer -= dt | |
| \t\tif timer <= 0: begin_swap() | |
| \telif phase == "shuffle": | |
| \t\ttimer += dt | |
| \t\tvar t = minf(timer / 0.72, 1) | |
| \t\tcup_x[first] = lerpf(from_a, from_b, t) | |
| \t\tcup_x[second] = lerpf(from_b, from_a, t) | |
| \t\tcup_y[first] = 350 - sin(t * PI) * 60 | |
| \t\tcup_y[second] = 350 + sin(t * PI) * 60 | |
| \t\tif t >= 1: | |
| \t\t\tcup_y[first] = 350 | |
| \t\t\tcup_y[second] = 350 | |
| \t\t\tswaps += 1 | |
| \t\t\tif swaps >= 8: phase = "guess" | |
| \t\t\telse: begin_swap() | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("KEEP YOUR EYE", "Remember the gold token / Watch eight swaps / Click its final cup / R: restart") | |
| \tdraw_rect(Rect2(100, 405, 760, 18), Color("6d5866")) | |
| \tfor i in range(3): | |
| \t\tvar c = Vector2(cup_x[i], cup_y[i]) | |
| \t\tdraw_colored_polygon(PackedVector2Array([c + Vector2(-45, -85), c + Vector2(45, -85), c + Vector2(65, 25), c + Vector2(-65, 25)]), Color("73bdcf")) | |
| \t\tdraw_line(c + Vector2(-65, 25), c + Vector2(65, 25), Color("c5f0f6"), 5) | |
| \t\tif (phase == "reveal" or phase == "result") and i == 1: draw_circle(c + Vector2(0, 48), 16, Color("ffdc70")) | |
| \tvar text = "Remember this cup!" if phase == "reveal" else ("Follow the moving cups..." if phase == "shuffle" else "Choose the cup hiding the token.") | |
| \tif phase == "result": text = "Correct! R for another round." if choice == 1 else "Wrong cup. R to try again." | |
| \tlabel(text, Vector2(295, 540), 26) | |
| \tlabel("Swaps %d / 8" % swaps, Vector2(395, 590), 21) | |
| '''), | |
| 46: ('Echo Bloom', '''extends Node2D | |
| var dots: Array[Vector2] = [] | |
| var speeds: Array[Vector2] = [] | |
| var touched: Array[bool] = [] | |
| var blooms: Array[Vector3] = [] | |
| var used = false | |
| var hits = 0 | |
| var outcome = "" | |
| func restart() -> void: | |
| \tdots.clear() | |
| \tspeeds.clear() | |
| \ttouched.clear() | |
| \tblooms.clear() | |
| \tfor i in range(36): | |
| \t\tdots.append(Vector2(195 + (i % 6) * 110, 170 + int(i / 6) * 67)) | |
| \t\tspeeds.append(Vector2.from_angle(i * 2.39) * 67) | |
| \t\ttouched.append(false) | |
| \tused = false | |
| \thits = 0 | |
| \toutcome = "" | |
| func bloom_radius(age: float) -> float: | |
| \treturn maxf(0, 1 - absf(age - 0.8) / 0.8) * 80 | |
| func game_input(event: InputEvent) -> void: | |
| \tif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT and not used: | |
| \t\tvar mouse = get_global_mouse_position() | |
| \t\tif mouse.y > 110 and mouse.y < 570: | |
| \t\t\tblooms.append(Vector3(mouse.x, mouse.y, 0)) | |
| \t\t\tused = true | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\tfor i in range(blooms.size() - 1, -1, -1): | |
| \t\t\tblooms[i].z += dt | |
| \t\t\tif blooms[i].z >= 1.6: blooms.remove_at(i) | |
| \t\tfor i in range(dots.size()): | |
| \t\t\tif touched[i]: continue | |
| \t\t\tdots[i] += speeds[i] * dt | |
| \t\t\tif dots[i].x < 80 or dots[i].x > 880: speeds[i].x = -speeds[i].x | |
| \t\t\tif dots[i].y < 135 or dots[i].y > 545: speeds[i].y = -speeds[i].y | |
| \t\t\tdots[i] = dots[i].clamp(Vector2(80, 135), Vector2(880, 545)) | |
| \t\t\tvar hit = false | |
| \t\t\tfor bloom in blooms: | |
| \t\t\t\tif dots[i].distance_to(Vector2(bloom.x, bloom.y)) < bloom_radius(bloom.z) + 5: hit = true | |
| \t\t\tif hit: | |
| \t\t\t\ttouched[i] = true | |
| \t\t\t\thits += 1 | |
| \t\t\t\tblooms.append(Vector3(dots[i].x, dots[i].y, 0)) | |
| \t\tif used and blooms.is_empty(): outcome = "Chain complete! R to replay." if hits >= 12 else "Chain too short. R to try again." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("ECHO BLOOM", "Click once to seed an explosion / Every touched dot becomes a new bloom / Chain twelve / R: restart") | |
| \tfor i in range(dots.size()): | |
| \t\tif not touched[i]: draw_circle(dots[i], 6, Color.from_hsv(float(i) / 36, 0.45, 1)) | |
| \tfor bloom in blooms: | |
| \t\tdraw_circle(Vector2(bloom.x, bloom.y), bloom_radius(bloom.z), Color(0.45, 0.9, 0.8, 0.35)) | |
| \t\tdraw_arc(Vector2(bloom.x, bloom.y), bloom_radius(bloom.z), 0, TAU, 32, Color("7df1cb"), 2) | |
| \tlabel("Chain %d / 12 Clicks remaining: %d" % [hits, 0 if used else 1], Vector2(280, 600)) | |
| \tif outcome != "": label(outcome, Vector2(290, 340), 26) | |
| '''), | |
| 47: ('Spring Landing', '''extends Node2D | |
| var puck = Vector2(120, 508) | |
| var speed = Vector2.ZERO | |
| var charge = 0.0 | |
| var held_before = false | |
| var flying = false | |
| var targets = [380.0, 560.0, 715.0, 465.0, 655.0] | |
| var successes = 0 | |
| var misses = 0 | |
| var outcome = "" | |
| func restart() -> void: | |
| \tpuck = Vector2(120, 508) | |
| \tspeed = Vector2.ZERO | |
| \tcharge = 0 | |
| \theld_before = false | |
| \tflying = false | |
| \tsuccesses = 0 | |
| \tmisses = 0 | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tvar held = Input.is_action_pressed("ui_accept") | |
| \tif outcome == "": | |
| \t\tif not flying: | |
| \t\t\tif held: charge = minf(1, charge + dt * 0.65) | |
| \t\t\tif held_before and not held: | |
| \t\t\t\tspeed = Vector2(160 + charge * 700, -300 - charge * 150) | |
| \t\t\t\tflying = true | |
| \t\t\t\tcharge = 0 | |
| \t\telse: | |
| \t\t\tspeed.y += 700 * dt | |
| \t\t\tpuck += speed * dt | |
| \t\t\tif puck.y >= 508 and speed.y > 0: | |
| \t\t\t\tif absf(puck.x - targets[successes]) <= 55: successes += 1 | |
| \t\t\t\telse: misses += 1 | |
| \t\t\t\tflying = false | |
| \t\t\t\tpuck = Vector2(120, 508) | |
| \t\t\t\tif successes >= 5: outcome = "Five perfect landings! R to replay." | |
| \t\t\t\telif misses >= 3: outcome = "Three misses. R to try again." | |
| \theld_before = held | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("SPRING LANDING", "Hold Space to compress / Release to launch / Land in five changing target bands / R: restart") | |
| \tdraw_rect(Rect2(20, 522, 920, 15), Color("596777")) | |
| \tvar target = targets[mini(successes, 4)] | |
| \tdraw_rect(Rect2(target - 55, 517, 110, 25), Color("5fce9f")) | |
| \tlabel("LAND HERE", Vector2(target - 52, 567), 18) | |
| \tvar length = 85 - charge * 55 | |
| \tvar previous = Vector2(30, 510) | |
| \tfor i in range(1, 9): | |
| \t\tvar next = Vector2(30 + length * i / 8, 510 + (10 if i % 2 == 0 else -10)) | |
| \t\tdraw_line(previous, next, Color("b4c9dd"), 3) | |
| \t\tprevious = next | |
| \tdraw_circle(puck, 12, Color("ffdc7d")) | |
| \tdraw_rect(Rect2(35, 575, 200, 18), Color("354156")) | |
| \tdraw_rect(Rect2(35, 575, 200 * charge, 18), Color("f5be69")) | |
| \tlabel("Charge", Vector2(35, 620), 20) | |
| \tlabel("Landings %d / 5 Misses %d / 3" % [successes, misses], Vector2(300, 620)) | |
| \tif outcome != "": label(outcome, Vector2(270, 310), 27) | |
| '''), | |
| 48: ('Color Reservoirs', '''extends Node2D | |
| var drops: Array[Vector3] = [] | |
| var filled = [0, 0] | |
| var paint = 0 | |
| var timer = 0.8 | |
| var spawned = 0 | |
| var mistakes = 0 | |
| var outcome = "" | |
| func restart() -> void: | |
| \tdrops.clear() | |
| \tfilled = [0, 0] | |
| \tpaint = 0 | |
| \ttimer = 0.8 | |
| \tspawned = 0 | |
| \tmistakes = 0 | |
| \toutcome = "" | |
| func ink(index: int) -> Color: | |
| \treturn Color("ef7688") if index == 0 else Color("67b8fa") | |
| func game_input(event: InputEvent) -> void: | |
| \tif event is InputEventKey and event.pressed and not event.echo: | |
| \t\tif event.keycode == KEY_1: paint = 0 | |
| \t\tif event.keycode == KEY_2: paint = 1 | |
| \t\tif event.keycode == KEY_SPACE: paint = 1 - paint | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\ttimer -= dt | |
| \t\tif timer <= 0: | |
| \t\t\tdrops.append(Vector3(spawned % 2, 120, -1)) | |
| \t\t\tspawned += 1 | |
| \t\t\ttimer = 1.05 | |
| \t\tfor i in range(drops.size() - 1, -1, -1): | |
| \t\t\tdrops[i].y += 150 * dt | |
| \t\t\tif drops[i].y >= 340 and drops[i].z < 0: drops[i].z = paint | |
| \t\t\tif drops[i].y >= 545: | |
| \t\t\t\tvar lane = int(drops[i].x) | |
| \t\t\t\tif int(drops[i].z) == lane: filled[lane] = mini(10, filled[lane] + 1) | |
| \t\t\t\telse: mistakes += 1 | |
| \t\t\t\tdrops.remove_at(i) | |
| \t\tif filled[0] >= 10 and filled[1] >= 10: outcome = "Both reservoirs full! R to replay." | |
| \t\telif mistakes >= 5: outcome = "Too much contamination! R to retry." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("COLOR RESERVOIRS", "1: red nozzle / 2: blue nozzle / Space: toggle / Droplets take color at the horizontal nozzle / R") | |
| \tfor lane in range(2): | |
| \t\tvar x = 325 + lane * 310 | |
| \t\tdraw_rect(Rect2(x - 55, 110, 110, 445), Color("253b51")) | |
| \t\tdraw_rect(Rect2(x - 85, 545, 170, 60), Color("465467")) | |
| \t\tdraw_rect(Rect2(x - 80, 600 - filled[lane] * 5, 160, filled[lane] * 5), ink(lane)) | |
| \t\tlabel("%s %d / 10" % ["RED" if lane == 0 else "BLUE", filled[lane]], Vector2(x - 65, 634), 20) | |
| \tdraw_line(Vector2(235, 340), Vector2(725, 340), ink(paint), 10) | |
| \tfor d in drops: draw_circle(Vector2(325 + d.x * 310, d.y), 14, Color("d2d6dc") if d.z < 0 else ink(int(d.z))) | |
| \tlabel("Mistakes %d / 5" % mistakes, Vector2(25, 570), 20) | |
| \tif outcome != "": label(outcome, Vector2(265, 295), 25) | |
| '''), | |
| 49: ('Quarter Turn Room', '''extends Node2D | |
| var hub = Vector2(480, 350) | |
| var ball = Vector2(290, 170) | |
| var speed = Vector2.ZERO | |
| var quarter = 0 | |
| var has_key = false | |
| var outcome = "" | |
| var ledges = [Rect2(-125, -35, 250, 16), Rect2(-30, 105, 155, 16)] | |
| func turn_point(v: Vector2) -> Vector2: | |
| \tvar result = v | |
| \tfor i in range(quarter): result = Vector2(-result.y, result.x) | |
| \treturn hub + result | |
| func transformed(rect: Rect2) -> Rect2: | |
| \tvar a = turn_point(rect.position) | |
| \tvar b = turn_point(rect.end) | |
| \treturn Rect2(Vector2(minf(a.x, b.x), minf(a.y, b.y)), Vector2(absf(b.x - a.x), absf(b.y - a.y))) | |
| func restart() -> void: | |
| \tball = Vector2(290, 170) | |
| \tspeed = Vector2.ZERO | |
| \tquarter = 0 | |
| \thas_key = false | |
| \toutcome = "" | |
| func game_input(event: InputEvent) -> void: | |
| \tif outcome != "" or not (event is InputEventKey): return | |
| \tif not event.pressed or event.echo: return | |
| \tvar direction = 0 | |
| \tif event.keycode == KEY_LEFT: direction = -1 | |
| \tif event.keycode == KEY_RIGHT: direction = 1 | |
| \tif direction != 0: | |
| \t\tquarter = (quarter + direction + 4) % 4 | |
| \t\tvar offset = ball - hub | |
| \t\tball = hub + Vector2(-offset.y, offset.x) * direction | |
| \t\tspeed = Vector2(-speed.y, speed.x) * direction | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\tvar old = ball | |
| \t\tspeed.y += 430 * dt | |
| \t\tspeed.x *= maxf(0, 1 - dt * 2) | |
| \t\tball += speed * dt | |
| \t\tfor ledge in ledges: | |
| \t\t\tvar box = transformed(ledge) | |
| \t\t\tif ball.x > box.position.x - 11 and ball.x < box.end.x + 11 and old.y + 11 <= box.position.y + 2 and ball.y + 11 >= box.position.y and speed.y > 0: | |
| \t\t\t\tball.y = box.position.y - 11 | |
| \t\t\t\tspeed.y = 0 | |
| \t\tif ball.y > 569: ball.y = 569; speed.y = 0 | |
| \t\tif ball.y < 131: ball.y = 131; speed.y = 0 | |
| \t\tif ball.x < 261 or ball.x > 699: speed.x = 0 | |
| \t\tball.x = clampf(ball.x, 261, 699) | |
| \t\tif ball.distance_to(turn_point(Vector2(-200, 195))) < 30: has_key = true | |
| \t\tif has_key and ball.distance_to(turn_point(Vector2(195, -200))) < 31: outcome = "Exit unlocked! R to play again." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("QUARTER TURN ROOM", "Left / Right: turn room 90 degrees / Gravity always points DOWN / Collect key then exit / R") | |
| \tdraw_rect(Rect2(250, 120, 460, 460), Color("27394f")) | |
| \tdraw_rect(Rect2(250, 120, 460, 460), Color("8bacca"), false, 7) | |
| \tfor ledge in ledges: draw_rect(transformed(ledge), Color("8399b1")) | |
| \tvar exit_pos = turn_point(Vector2(195, -200)) | |
| \tdraw_rect(Rect2(exit_pos - Vector2(21, 21), Vector2(42, 42)), Color("67dcb5") if has_key else Color("506150")) | |
| \tif not has_key: draw_circle(turn_point(Vector2(-200, 195)), 12, Color("ffda77")) | |
| \tdraw_circle(ball, 11, Color("78ddfa")) | |
| \tlabel("Key: %s Quarter turns: %d" % ["yes" if has_key else "no", quarter], Vector2(300, 615)) | |
| \tif outcome != "": label(outcome, Vector2(280, 340), 24) | |
| '''), | |
| 50: ('Junction Keeper', '''extends Node2D | |
| var trains = [] | |
| var route = 0 | |
| var timer = 1.0 | |
| var sequence = 0 | |
| var delivered = 0 | |
| var crashes = 0 | |
| var outcome = "" | |
| var junction = Vector2(480, 350) | |
| func restart() -> void: | |
| \ttrains.clear() | |
| \troute = 0 | |
| \ttimer = 1 | |
| \tsequence = 0 | |
| \tdelivered = 0 | |
| \tcrashes = 0 | |
| \toutcome = "" | |
| func game_input(event: InputEvent) -> void: | |
| \tif event is InputEventKey and event.pressed: | |
| \t\tif event.keycode == KEY_1: route = 0 | |
| \t\tif event.keycode == KEY_2: route = 1 | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\ttimer -= dt | |
| \t\tif timer <= 0: | |
| \t\t\ttrains.append({"p":Vector2(100, 350) if sequence % 2 == 0 else Vector2(480, 115), "kind":int(sequence / 2) % 2, "phase":0, "exit":0}) | |
| \t\t\tsequence += 1 | |
| \t\t\ttimer = 2.3 | |
| \t\tfor i in range(trains.size() - 1, -1, -1): | |
| \t\t\tvar t = trains[i] | |
| \t\t\tif t.phase == 0: | |
| \t\t\t\tif not Input.is_action_pressed("ui_accept"): t.p = t.p.move_toward(junction, 95 * dt) | |
| \t\t\t\tif t.p.distance_to(junction) < 3: | |
| \t\t\t\t\tvar occupied = false | |
| \t\t\t\t\tfor other in trains: | |
| \t\t\t\t\t\tif other.phase == 1 and other.p.distance_to(junction) < 65: occupied = true | |
| \t\t\t\t\tif occupied or route != t.kind: | |
| \t\t\t\t\t\tcrashes += 1 | |
| \t\t\t\t\t\ttrains.remove_at(i) | |
| \t\t\t\t\t\tcontinue | |
| \t\t\t\t\tt.phase = 1 | |
| \t\t\t\t\tt.exit = route | |
| \t\t\telse: | |
| \t\t\t\tt.p += (Vector2.RIGHT if t.exit == 0 else Vector2.DOWN) * 145 * dt | |
| \t\t\t\tif t.p.x > 840 or t.p.y > 565: | |
| \t\t\t\t\tdelivered += 1 | |
| \t\t\t\t\ttrains.remove_at(i) | |
| \t\tif delivered >= 12: outcome = "Twelve safe arrivals! R to replay." | |
| \t\telif crashes >= 3: outcome = "Junction closed after three errors. R to retry." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("JUNCTION KEEPER", "1: red station / 2: blue station / Hold Space to STOP incoming trains / R: restart") | |
| \tdraw_line(Vector2(75, 350), Vector2(865, 350), Color("56718a"), 14) | |
| \tdraw_line(Vector2(480, 110), Vector2(480, 585), Color("56718a"), 14) | |
| \tdraw_rect(Rect2(840, 320, 70, 60), Color("dc7486")) | |
| \tdraw_rect(Rect2(445, 555, 70, 50), Color("629ee4")) | |
| \tdraw_line(junction, junction + (Vector2(75, 0) if route == 0 else Vector2(0, 75)), Color("74efc5"), 7) | |
| \tfor t in trains: | |
| \t\tdraw_rect(Rect2(t.p - Vector2(17, 14), Vector2(34, 28)), Color("f08091") if t.kind == 0 else Color("6db8ff")) | |
| \t\tlabel("R" if t.kind == 0 else "B", t.p + Vector2(-7, 7), 18) | |
| \tlabel("Arrivals %d / 12 Errors %d / 3" % [delivered, crashes], Vector2(30, 620)) | |
| \tif outcome != "": label(outcome, Vector2(220, 250), 25) | |
| '''), | |
| 51: ('Three Robot Programs', '''extends Node2D | |
| var starts = [Vector2i(0, 0), Vector2i(0, 2), Vector2i(0, 4)] | |
| var goals = [Vector2i(4, 1), Vector2i(3, 3), Vector2i(5, 4)] | |
| var walls = [Vector2i(2, 3), Vector2i(4, 3), Vector2i(6, 1), Vector2i(6, 2)] | |
| var robots = [] | |
| var programs = [[], [], []] | |
| var selected = 0 | |
| var step = 0 | |
| var timer = 0.0 | |
| var running = false | |
| var outcome = "" | |
| func middle(c: Vector2i) -> Vector2: | |
| \treturn Vector2(200, 135) + (Vector2(c) + Vector2(0.5, 0.5)) * 68 | |
| func restart() -> void: | |
| \trobots = starts.duplicate() | |
| \tprograms = [[], [], []] | |
| \tselected = 0 | |
| \tstep = 0 | |
| \ttimer = 0 | |
| \trunning = false | |
| \toutcome = "" | |
| func game_input(event: InputEvent) -> void: | |
| \tif running or outcome != "" or not (event is InputEventKey): return | |
| \tif not event.pressed or event.echo: return | |
| \tif event.keycode == KEY_1: selected = 0 | |
| \tif event.keycode == KEY_2: selected = 1 | |
| \tif event.keycode == KEY_3: selected = 2 | |
| \tif event.keycode == KEY_BACKSPACE and not programs[selected].is_empty(): programs[selected].pop_back() | |
| \tif event.keycode == KEY_SPACE: running = true; timer = 0.5 | |
| \tvar direction = Vector2i.ZERO | |
| \tif event.keycode == KEY_LEFT: direction = Vector2i.LEFT | |
| \tif event.keycode == KEY_RIGHT: direction = Vector2i.RIGHT | |
| \tif event.keycode == KEY_UP: direction = Vector2i.UP | |
| \tif event.keycode == KEY_DOWN: direction = Vector2i.DOWN | |
| \tif direction != Vector2i.ZERO and programs[selected].size() < 6: programs[selected].append(direction) | |
| func _process(dt: float) -> void: | |
| \tif running and outcome == "": | |
| \t\ttimer -= dt | |
| \t\tif timer <= 0: | |
| \t\t\ttimer = 0.5 | |
| \t\t\tvar proposed = robots.duplicate() | |
| \t\t\tvar any_command = false | |
| \t\t\tfor i in range(3): | |
| \t\t\t\tif step < programs[i].size(): proposed[i] += programs[i][step]; any_command = true | |
| \t\t\tif not any_command: | |
| \t\t\t\toutcome = "All targets reached! R to replay." if robots == goals else "Program ended off target. R to replan." | |
| \t\t\telse: | |
| \t\t\t\tfor i in range(3): | |
| \t\t\t\t\tvar c = proposed[i] | |
| \t\t\t\t\tif c.x < 0 or c.x >= 8 or c.y < 0 or c.y >= 5 or walls.has(c): outcome = "A robot hit a wall. R to replan." | |
| \t\t\t\t\tfor j in range(i): | |
| \t\t\t\t\t\tif proposed[j] == c or (c == robots[j] and proposed[j] == robots[i]): outcome = "Robots collided. R to replan." | |
| \t\t\t\tif outcome == "": robots = proposed; step += 1 | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("THREE ROBOT PROGRAMS", "1 / 2 / 3: select robot / Arrows: record up to six commands / Backspace: undo / Space: run / R") | |
| \tfor y in range(5): | |
| \t\tfor x in range(8): | |
| \t\t\tvar c = Vector2i(x, y) | |
| \t\t\tdraw_rect(Rect2(middle(c) - Vector2(32, 32), Vector2(64, 64)), Color("64748a") if walls.has(c) else Color("25374d")) | |
| \tfor i in range(3): | |
| \t\tvar color = Color.from_hsv(float(i) / 3, 0.5, 1) | |
| \t\tdraw_circle(middle(goals[i]), 24, color, false, 3) | |
| \t\tdraw_rect(Rect2(middle(robots[i]) - Vector2(14, 14), Vector2(28, 28)), color) | |
| \t\tvar text = "%d: " % (i + 1) | |
| \t\tfor direction in programs[i]: text += "L " if direction == Vector2i.LEFT else ("R " if direction == Vector2i.RIGHT else ("U " if direction == Vector2i.UP else "D ")) | |
| \t\tlabel(text + ("< selected" if selected == i else ""), Vector2(220, 510 + i * 29), 21, color) | |
| \tif outcome != "": label(outcome, Vector2(230, 617), 24) | |
| '''), | |
| 52: ('Moving Fuel Link', '''extends Node2D | |
| var rover = 480.0 | |
| var tanker = 480.0 | |
| var elapsed = 0.0 | |
| var supply = 100.0 | |
| var filled = 0.0 | |
| var waste = 0.0 | |
| var pouring = false | |
| var aligned = false | |
| var outcome = "" | |
| func restart() -> void: | |
| \trover = 480 | |
| \ttanker = 480 | |
| \telapsed = 0 | |
| \tsupply = 100 | |
| \tfilled = 0 | |
| \twaste = 0 | |
| \toutcome = "" | |
| \tpouring = false | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\telapsed += dt | |
| \t\ttanker = 480 + sin(elapsed * 0.65) * 315 | |
| \t\trover = clampf(rover + Input.get_axis("ui_left", "ui_right") * 345 * dt, 85, 875) | |
| \t\taligned = absf(tanker - rover) <= 23 | |
| \t\tpouring = Input.is_action_pressed("ui_accept") and supply > 0 | |
| \t\tif pouring: | |
| \t\t\tvar amount = minf(supply, 16 * dt) | |
| \t\t\tsupply -= amount | |
| \t\t\tif aligned: filled += amount | |
| \t\t\telse: waste += amount | |
| \t\tif filled >= 70: outcome = "Rover fueled! R for another transfer." | |
| \t\telif waste >= 20 or supply <= 0: outcome = "Too much fuel wasted. R to retry." | |
| \telse: pouring = false | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("MOVING FUEL LINK", "Left / Right: rover / Hold Space only while openings align / Fill seventy, waste under twenty / R") | |
| \tdraw_line(Vector2(50, 175), Vector2(910, 175), Color("7b8b9c"), 7) | |
| \tdraw_rect(Rect2(tanker - 65, 132, 130, 42), Color("7aa7be")) | |
| \tdraw_rect(Rect2(tanker - 8, 174, 16, 28), Color("d7e7eb")) | |
| \tdraw_rect(Rect2(rover - 65, 505, 130, 50), Color("68c3ae")) | |
| \tdraw_circle(Vector2(rover - 42, 563), 15, Color("33435b")) | |
| \tdraw_circle(Vector2(rover + 42, 563), 15, Color("33435b")) | |
| \tdraw_rect(Rect2(rover - 26, 491, 52, 14), Color("a3f4d1") if aligned else Color("b4becd")) | |
| \tif pouring: draw_line(Vector2(tanker, 200), Vector2(tanker, 490 if aligned else 590), Color("f5d76d"), 7) | |
| \tdraw_rect(Rect2(245, 595, 470, 15), Color("344258")) | |
| \tdraw_rect(Rect2(245, 595, 470 * minf(filled / 70, 1), 15), Color("6ce0b9")) | |
| \tlabel("Fuel %.0f / 70 Wasted %.0f / 20 Tank %.0f" % [filled, waste, supply], Vector2(245, 637), 20) | |
| \tif outcome != "": label(outcome, Vector2(255, 340), 26) | |
| '''), | |
| 53: ('Seven Links', '''extends Node2D | |
| var nodes: Array[Vector2] = [] | |
| var chain: Array[int] = [] | |
| var outcome = "" | |
| var won = false | |
| func restart() -> void: | |
| \tnodes.clear() | |
| \tfor y in range(3): | |
| \t\tfor x in range(4): nodes.append(Vector2(245 + x * 155, 190 + y * 135)) | |
| \tchain.clear() | |
| \toutcome = "" | |
| \twon = false | |
| func crosses(a: Vector2, b: Vector2, c: Vector2, d: Vector2) -> bool: | |
| \treturn (b - a).cross(c - a) * (b - a).cross(d - a) < 0 and (d - c).cross(a - c) * (d - c).cross(b - c) < 0 | |
| func game_input(event: InputEvent) -> void: | |
| \tif won: return | |
| \tif event is InputEventMouseButton and event.pressed: | |
| \t\tif event.button_index == MOUSE_BUTTON_RIGHT and not chain.is_empty(): chain.pop_back(); outcome = "" | |
| \t\tif event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\t\tfor i in range(nodes.size()): | |
| \t\t\t\tif nodes[i].distance_to(get_global_mouse_position()) > 24 or chain.has(i): continue | |
| \t\t\t\tif chain.is_empty(): chain.append(i); continue | |
| \t\t\t\tvar a = nodes[chain[-1]] | |
| \t\t\t\tif a.distance_to(nodes[i]) > 215: outcome = "That node is too far away."; continue | |
| \t\t\t\tvar blocked = false | |
| \t\t\t\tfor j in range(maxi(0, chain.size() - 2)): | |
| \t\t\t\t\tif crosses(a, nodes[i], nodes[chain[j]], nodes[chain[j + 1]]): blocked = true | |
| \t\t\t\tif blocked: outcome = "That link would cross an existing segment." | |
| \t\t\t\telse: chain.append(i); outcome = "" | |
| \tif event is InputEventKey and event.pressed and event.keycode == KEY_SPACE: | |
| \t\twon = chain.size() == 8 | |
| \t\toutcome = "Exactly seven links! R to replay." if won else "Need exactly seven links. Keep editing." | |
| func _process(_dt: float) -> void: | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("SEVEN LINKS", "Click nearby nodes / No crossings or revisits / Right-click: undo / Space: confirm exactly seven / R") | |
| \tfor i in range(1, chain.size()): draw_line(nodes[chain[i - 1]], nodes[chain[i]], Color("6ee9c5"), 5) | |
| \tfor i in range(nodes.size()): | |
| \t\tdraw_circle(nodes[i], 21, Color("7de7bf") if chain.has(i) else Color("637e9b")) | |
| \t\tlabel(str(i + 1), nodes[i] + Vector2(-10, 7), 18, Color("12233c")) | |
| \tlabel("Links %d / 7" % maxi(0, chain.size() - 1), Vector2(380, 550), 26) | |
| \tlabel(outcome, Vector2(230, 602), 23) | |
| '''), | |
| 54: ('Crumble Climber', '''extends Node2D | |
| var player = Vector2(480, 560) | |
| var vertical_speed = -520.0 | |
| var camera_y = 0.0 | |
| var platforms = [] | |
| var outcome = "" | |
| func restart() -> void: | |
| \tplayer = Vector2(480, 560) | |
| \tvertical_speed = -520 | |
| \tcamera_y = 0 | |
| \tplatforms.clear() | |
| \tplatforms.append({"x":410.0, "y":590.0, "w":140.0, "fragile":false, "active":true}) | |
| \tfor i in range(1, 13): platforms.append({"x":280.0 + (i * 97) % 320, "y":590.0 - i * 115, "w":120.0, "fragile":i % 3 == 0, "active":true}) | |
| \tplatforms.append({"x":255.0, "y":-905.0, "w":450.0, "fragile":false, "active":true}) | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\tvar before = player.y | |
| \t\tplayer.x = clampf(player.x + Input.get_axis("ui_left", "ui_right") * 330 * dt, 18, 942) | |
| \t\tvertical_speed += 780 * dt | |
| \t\tplayer.y += vertical_speed * dt | |
| \t\tif vertical_speed > 0: | |
| \t\t\tfor i in range(platforms.size()): | |
| \t\t\t\tvar p = platforms[i] | |
| \t\t\t\tif p.active and player.x >= p.x - 10 and player.x <= p.x + p.w + 10 and before + 12 <= p.y and player.y + 12 >= p.y: | |
| \t\t\t\t\tplayer.y = p.y - 12 | |
| \t\t\t\t\tvertical_speed = -520 | |
| \t\t\t\t\tif p.fragile: p.active = false | |
| \t\t\t\t\tif i == platforms.size() - 1: outcome = "Roof reached! R to climb again." | |
| \t\tcamera_y = minf(camera_y, player.y - 270) | |
| \t\tif player.y > camera_y + 680: outcome = "You fell! R to retry." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \tfor i in range(platforms.size()): | |
| \t\tvar p = platforms[i] | |
| \t\tif p.active: | |
| \t\t\tdraw_rect(Rect2(p.x, p.y - camera_y, p.w, 14), Color("f2b76e") if p.fragile else Color("638caf")) | |
| \t\t\tif p.fragile: label("crumble", Vector2(p.x + 20, p.y - camera_y - 8), 14) | |
| \t\t\tif i == platforms.size() - 1: label("ROOF", Vector2(p.x + 180, p.y - camera_y - 15), 26, Color("71efc6")) | |
| \tdraw_circle(Vector2(player.x, player.y - camera_y), 12, Color("70edcf")) | |
| \theader("CRUMBLE CLIMBER", "Left / Right: steer / Bounce automatically / Orange ledges vanish after landing / Reach the roof / R") | |
| \tlabel("Height %.0f / 1495" % maxf(0, 590 - player.y), Vector2(28, 622)) | |
| \tif outcome != "": label(outcome, Vector2(265, 335), 27) | |
| '''), | |
| 55: ('Shadow Workshop', '''extends Node2D | |
| var light = Vector2(100, 350) | |
| var blocks: Array[Vector2] = [] | |
| var target_y = [300.0, 385.0, 330.0] | |
| var selected = -1 | |
| var matches = 0 | |
| var won = false | |
| func restart() -> void: | |
| \tblocks.assign([Vector2(300, 350), Vector2(420, 310), Vector2(540, 395)]) | |
| \tselected = -1 | |
| \tmatches = 0 | |
| \twon = false | |
| func projection(i: int, y: float) -> Rect2: | |
| \tvar ratio = 750.0 / (blocks[i].x - light.x) | |
| \treturn Rect2(836, light.y + (y - light.y) * ratio - 12 * ratio, 28, 24 * ratio) | |
| func game_input(event: InputEvent) -> void: | |
| \tif won: return | |
| \tif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\tselected = -1 | |
| \t\tif event.pressed: | |
| \t\t\tfor i in range(3): | |
| \t\t\t\tif Rect2(blocks[i] - Vector2(20, 24), Vector2(40, 48)).has_point(get_global_mouse_position()): selected = i | |
| func _process(_dt: float) -> void: | |
| \tif selected >= 0 and not won: blocks[selected].y = clampf(get_global_mouse_position().y, 280, 425) | |
| \tmatches = 0 | |
| \tfor i in range(3): | |
| \t\tif absf(projection(i, blocks[i].y).get_center().y - projection(i, target_y[i]).get_center().y) < 7: matches += 1 | |
| \twon = matches == 3 | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \tdraw_rect(Rect2(824, 105, 52, 490), Color("b8c6d6")) | |
| \tfor i in range(3): | |
| \t\tvar shadow = projection(i, blocks[i].y) | |
| \t\tdraw_colored_polygon(PackedVector2Array([light, Vector2(850, shadow.position.y), Vector2(850, shadow.end.y)]), Color(0.1, 0.15, 0.2, 0.22)) | |
| \t\tdraw_rect(shadow, Color("263449")) | |
| \t\tdraw_rect(projection(i, target_y[i]), Color("6effc2"), false, 3) | |
| \t\tdraw_rect(Rect2(blocks[i] - Vector2(12, 12), Vector2(24, 24)), Color.from_hsv(float(i) / 3, 0.55, 0.95)) | |
| \t\tlabel(str(i + 1), blocks[i] + Vector2(-6, -24), 20) | |
| \t\tlabel(str(i + 1), Vector2(885, projection(i, target_y[i]).get_center().y + 7), 20) | |
| \tdraw_circle(light, 18, Color("ffdc78")) | |
| \theader("SHADOW WORKSHOP", "Drag each numbered block up/down / Fit its projected shadow inside the green silhouette / R") | |
| \tlabel("Aligned shadows %d / 3" % matches, Vector2(310, 605), 24) | |
| \tif won: label("Silhouette matched! R to reset.", Vector2(275, 540), 25) | |
| '''), | |
| 56: ('Changing Wind', '''extends Node2D | |
| var boat = Vector2(85, 350) | |
| var heading = 0.0 | |
| var sail = 0.7 | |
| var elapsed = 0.0 | |
| var wind = 1 | |
| var buoys = [Vector2(220, 260), Vector2(390, 450), Vector2(560, 265), Vector2(735, 435), Vector2(870, 310)] | |
| var checkpoint = 0 | |
| var outcome = "" | |
| func upper(x: float) -> float: | |
| \treturn 165 + sin(x / 120) * 25 | |
| func lower(x: float) -> float: | |
| \treturn 555 + cos(x / 140) * 20 | |
| func restart() -> void: | |
| \tboat = Vector2(85, 350) | |
| \theading = 0 | |
| \tsail = 0.7 | |
| \telapsed = 0 | |
| \tcheckpoint = 0 | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\telapsed += dt | |
| \t\twind = 1 if int(elapsed / 9) % 2 == 0 else -1 | |
| \t\theading += Input.get_axis("ui_left", "ui_right") * 1.8 * dt | |
| \t\tif Input.is_physical_key_pressed(KEY_Q): sail -= dt * 1.6 | |
| \t\tif Input.is_physical_key_pressed(KEY_E): sail += dt * 1.6 | |
| \t\tsail = clampf(sail, -1.5, 1.5) | |
| \t\tvar drive = 45 + 85 * absf(cos(heading + sail)) | |
| \t\tboat += (Vector2.from_angle(heading) * drive + Vector2(wind * 28, 0)) * dt | |
| \t\tif boat.x < 18 or boat.x > 942 or boat.y < upper(boat.x) + 13 or boat.y > lower(boat.x) - 13: outcome = "Touched the shore! R to retry." | |
| \t\telif boat.distance_to(buoys[checkpoint]) < 34: | |
| \t\t\tcheckpoint += 1 | |
| \t\t\tif checkpoint == buoys.size(): outcome = "All buoys passed! R to sail again." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \tdraw_rect(Rect2(0, 95, 960, 545), Color("153e59")) | |
| \tfor x in range(0, 960, 12): | |
| \t\tdraw_rect(Rect2(x, 95, 12, upper(x) - 95), Color("5c7058")) | |
| \t\tdraw_rect(Rect2(x, lower(x), 12, 640 - lower(x)), Color("5c7058")) | |
| \tfor i in range(buoys.size()): | |
| \t\tdraw_circle(buoys[i], 14, Color("6de5bd") if i < checkpoint else Color("ffce77")) | |
| \t\tif i == checkpoint: draw_arc(buoys[i], 33, 0, TAU, 30, Color("ffe8a3"), 2) | |
| \t\tlabel(str(i + 1), buoys[i] + Vector2(-6, 7), 17, Color("23384c")) | |
| \tvar forward = Vector2.from_angle(heading) | |
| \tvar side = forward.orthogonal() | |
| \tdraw_colored_polygon(PackedVector2Array([boat + forward * 20, boat - forward * 15 + side * 10, boat - forward * 15 - side * 10]), Color("dfe7e6")) | |
| \tdraw_line(boat, boat + Vector2.from_angle(heading + sail) * 27, Color("ed879a"), 5) | |
| \theader("CHANGING WIND", "Left / Right: steer / Q / E: trim sail / Visit buoys in order, avoid shore / R: restart") | |
| \tlabel("Buoys %d / 5 Wind %s Sail %.0f degrees" % [checkpoint, "RIGHT" if wind == 1 else "LEFT", rad_to_deg(sail)], Vector2(30, 624), 21) | |
| \tif outcome != "": label(outcome, Vector2(280, 345), 25) | |
| '''), | |
| 57: ('Shared Turn Tanks', '''extends Node2D | |
| var player = Vector2i(1, 4) | |
| var enemy = Vector2i(5, 1) | |
| var player_dir = 0 | |
| var enemy_dir = 2 | |
| var player_hp = 2 | |
| var enemy_hp = 2 | |
| var walls = [Vector2i(3, 1), Vector2i(3, 2), Vector2i(3, 3), Vector2i(1, 2), Vector2i(5, 4)] | |
| var dirs = [Vector2i.UP, Vector2i.RIGHT, Vector2i.DOWN, Vector2i.LEFT] | |
| var rays = [] | |
| var ray_time = 0.0 | |
| var turns = 0 | |
| func middle(c: Vector2i) -> Vector2: | |
| \treturn Vector2(230, 125) + (Vector2(c) + Vector2(0.5, 0.5)) * 70 | |
| func valid(c: Vector2i) -> bool: | |
| \treturn c.x >= 0 and c.x < 7 and c.y >= 0 and c.y < 6 and not walls.has(c) | |
| func restart() -> void: | |
| \tplayer = Vector2i(1, 4) | |
| \tenemy = Vector2i(5, 1) | |
| \tplayer_dir = 0 | |
| \tenemy_dir = 2 | |
| \tplayer_hp = 2 | |
| \tenemy_hp = 2 | |
| \trays.clear() | |
| \tray_time = 0 | |
| \tturns = 0 | |
| func fire(origin: Vector2i, direction: int, hostile: bool) -> void: | |
| \tvar cell = origin | |
| \tfor i in range(10): | |
| \t\tcell += dirs[direction] | |
| \t\tif not valid(cell): break | |
| \t\tif hostile and cell == player: player_hp -= 1; break | |
| \t\tif not hostile and cell == enemy: enemy_hp -= 1; break | |
| \trays.append({"a":middle(origin), "b":middle(cell), "hostile":hostile}) | |
| \tray_time = 0.45 | |
| func enemy_turn() -> void: | |
| \tif enemy_hp <= 0: return | |
| \tvar offset = player - enemy | |
| \tvar desired = (1 if offset.x > 0 else 3) if abs(offset.x) >= abs(offset.y) else (2 if offset.y > 0 else 0) | |
| \tif enemy_dir != desired: enemy_dir = desired | |
| \telif player.x == enemy.x or player.y == enemy.y: fire(enemy, enemy_dir, true) | |
| \telif valid(enemy + dirs[enemy_dir]) and enemy + dirs[enemy_dir] != player: enemy += dirs[enemy_dir] | |
| \telse: | |
| \t\tenemy_dir = (enemy_dir + 1) % 4 | |
| \t\tif valid(enemy + dirs[enemy_dir]) and enemy + dirs[enemy_dir] != player: enemy += dirs[enemy_dir] | |
| func game_input(event: InputEvent) -> void: | |
| \tif player_hp <= 0 or enemy_hp <= 0 or not (event is InputEventKey): return | |
| \tif not event.pressed or event.echo: return | |
| \tvar acted = true | |
| \tif event.keycode == KEY_LEFT: player_dir = (player_dir + 3) % 4 | |
| \telif event.keycode == KEY_RIGHT: player_dir = (player_dir + 1) % 4 | |
| \telif event.keycode == KEY_UP: | |
| \t\tif valid(player + dirs[player_dir]) and player + dirs[player_dir] != enemy: player += dirs[player_dir] | |
| \telif event.keycode == KEY_SPACE: fire(player, player_dir, false) | |
| \telse: acted = false | |
| \tif acted: turns += 1; enemy_turn() | |
| func _process(dt: float) -> void: | |
| \tray_time -= dt | |
| \tif ray_time <= 0: rays.clear() | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("SHARED TURN TANKS", "Up: forward / Left-Right: rotate / Space: fire / Enemy acts after each turn / Two hits win / R") | |
| \tfor y in range(6): | |
| \t\tfor x in range(7): | |
| \t\t\tvar c = Vector2i(x, y) | |
| \t\t\tdraw_rect(Rect2(middle(c) - Vector2(33, 33), Vector2(66, 66)), Color("667381") if walls.has(c) else Color("283b4d")) | |
| \tfor ray in rays: draw_line(ray.a, ray.b, Color("f9899a") if ray.hostile else Color("ffe18a"), 4) | |
| \tfor i in range(2): | |
| \t\tvar c = middle(player if i == 0 else enemy) | |
| \t\tvar direction = player_dir if i == 0 else enemy_dir | |
| \t\tdraw_rect(Rect2(c - Vector2(19, 19), Vector2(38, 38)), Color("6ae2bc") if i == 0 else Color("ee7c8d")) | |
| \t\tdraw_line(c, c + Vector2(dirs[direction]) * 29, Color("e5ecde"), 7) | |
| \tlabel("Armor %d / 2 Enemy %d / 2 Turns %d" % [maxi(player_hp, 0), maxi(enemy_hp, 0), turns], Vector2(255, 600)) | |
| \tif enemy_hp <= 0 or player_hp <= 0: label("Duel won! R to replay." if enemy_hp <= 0 else "Tank disabled. R to retry.", Vector2(300, 635), 23) | |
| '''), | |
| 58: ('One Clean Divider', '''extends Node2D | |
| var red = [Vector2(200, 405), Vector2(270, 470), Vector2(330, 370), Vector2(385, 505), Vector2(425, 410), Vector2(295, 290)] | |
| var blue = [Vector2(525, 180), Vector2(650, 230), Vector2(725, 350), Vector2(590, 325), Vector2(805, 205), Vector2(665, 440)] | |
| var first = Vector2.ZERO | |
| var last = Vector2.ZERO | |
| var dragging = false | |
| var drawn = false | |
| var won = false | |
| var notice = "Draw one straight divider." | |
| func restart() -> void: | |
| \tfirst = Vector2.ZERO | |
| \tlast = Vector2.ZERO | |
| \tdragging = false | |
| \tdrawn = false | |
| \twon = false | |
| \tnotice = "Draw one straight divider." | |
| func check_cut() -> void: | |
| \tif not drawn or first.distance_to(last) < 40: notice = "Drag a longer divider first."; return | |
| \tvar direction = (last - first).normalized() | |
| \tvar side = 1 if direction.cross(red[0] - first) > 0 else -1 | |
| \tvar correct = true | |
| \tfor dot in red: | |
| \t\tif direction.cross(dot - first) * side < 14: correct = false | |
| \tfor dot in blue: | |
| \t\tif direction.cross(dot - first) * side > -14: correct = false | |
| \twon = correct | |
| \tnotice = "Perfect separation! R to replay." if won else "Colors overlap sides or touch the line. Draw again." | |
| func game_input(event: InputEvent) -> void: | |
| \tif won: return | |
| \tif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\tif event.pressed: | |
| \t\t\tfirst = get_global_mouse_position() | |
| \t\t\tlast = first | |
| \t\t\tdragging = true | |
| \t\t\tdrawn = false | |
| \t\telse: | |
| \t\t\tlast = get_global_mouse_position() | |
| \t\t\tdragging = false | |
| \t\t\tdrawn = true | |
| \tif event is InputEventKey and event.pressed and event.keycode == KEY_SPACE: check_cut() | |
| func _process(_dt: float) -> void: | |
| \tif dragging: last = get_global_mouse_position() | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \tif first.distance_to(last) > 1: | |
| \t\tvar direction = (last - first).normalized() | |
| \t\tdraw_line(first - direction * 1200, first + direction * 1200, Color("80e5c4") if won else Color("dfd5ab"), 3) | |
| \tfor dot in red: | |
| \t\tdraw_circle(dot, 14, Color("f27d93")) | |
| \t\tlabel("R", dot + Vector2(-6, 6), 15, Color("24334a")) | |
| \tfor dot in blue: | |
| \t\tdraw_circle(dot, 14, Color("76bcf2")) | |
| \t\tlabel("B", dot + Vector2(-6, 6), 15, Color("24334a")) | |
| \theader("ONE CLEAN DIVIDER", "Drag mouse: draw a straight divider / Space: confirm / Red on one side, blue on the other / R") | |
| \tlabel(notice, Vector2(185, 610), 24) | |
| '''), | |
| 59: ('Fruit Trail', '''extends Node2D | |
| var fruit = [] | |
| var trail: Array[Vector2] = [] | |
| var flashes: Array[Vector3] = [] | |
| var spawn_time = 0.4 | |
| var sequence = 0 | |
| var score = 0 | |
| var missed = 0 | |
| var outcome = "" | |
| func restart() -> void: | |
| \tfruit.clear() | |
| \ttrail.clear() | |
| \tflashes.clear() | |
| \tspawn_time = 0.4 | |
| \tsequence = 0 | |
| \tscore = 0 | |
| \tmissed = 0 | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func near_segment(p: Vector2, a: Vector2, b: Vector2) -> bool: | |
| \tvar line = b - a | |
| \tvar t = clampf((p - a).dot(line) / maxf(line.length_squared(), 0.01), 0, 1) | |
| \treturn p.distance_to(a + line * t) < 24 | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\tspawn_time -= dt | |
| \t\tif spawn_time <= 0: | |
| \t\t\tfruit.append({"p":Vector2(150 + (sequence * 173) % 660, 670), "v":Vector2(75 if sequence % 2 == 0 else -75, -650), "bomb":sequence % 5 == 4}) | |
| \t\t\tsequence += 1 | |
| \t\t\tspawn_time = 0.85 | |
| \t\tif Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): | |
| \t\t\ttrail.append(get_global_mouse_position()) | |
| \t\t\tif trail.size() > 8: trail.pop_front() | |
| \t\telse: trail.clear() | |
| \t\tfor i in range(fruit.size() - 1, -1, -1): | |
| \t\t\tvar f = fruit[i] | |
| \t\t\tf.v.y += 640 * dt | |
| \t\t\tf.p += f.v * dt | |
| \t\t\tvar cut = false | |
| \t\t\tfor j in range(1, trail.size()): | |
| \t\t\t\tif near_segment(f.p, trail[j - 1], trail[j]): cut = true | |
| \t\t\tif cut: | |
| \t\t\t\tif f.bomb: outcome = "Bomb sliced! R to try again." | |
| \t\t\t\telse: score += 1; flashes.append(Vector3(f.p.x, f.p.y, 0.35)) | |
| \t\t\t\tfruit.remove_at(i) | |
| \t\t\telif f.p.y > 710: | |
| \t\t\t\tif not f.bomb: missed += 1 | |
| \t\t\t\tfruit.remove_at(i) | |
| \t\tif score >= 15: outcome = "Fifteen fruit sliced! R to replay." | |
| \t\telif missed >= 3: outcome = "Three fruit missed. R to retry." | |
| \tfor i in range(flashes.size() - 1, -1, -1): | |
| \t\tflashes[i].z -= dt | |
| \t\tif flashes[i].z <= 0: flashes.remove_at(i) | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("FRUIT TRAIL", "Hold mouse and drag: slice fruit / Avoid dark bombs marked X / Fifteen fruit, at most two misses / R") | |
| \tfor f in fruit: | |
| \t\tdraw_circle(f.p, 22, Color("374155") if f.bomb else Color("efb96d")) | |
| \t\tif f.bomb: | |
| \t\t\tdraw_line(f.p - Vector2(9, 9), f.p + Vector2(9, 9), Color("ff8490"), 4) | |
| \t\t\tdraw_line(f.p + Vector2(-9, 9), f.p + Vector2(9, -9), Color("ff8490"), 4) | |
| \t\telse: draw_line(f.p + Vector2(0, -22), f.p + Vector2(5, -30), Color("76c583"), 5) | |
| \tif trail.size() > 1: draw_polyline(PackedVector2Array(trail), Color("a6f3db"), 4) | |
| \tfor flash in flashes: draw_arc(Vector2(flash.x, flash.y), 35 - flash.z * 30, 0, TAU, 16, Color("ffde8a"), 3) | |
| \tlabel("Fruit %d / 15 Misses %d / 3" % [score, missed], Vector2(290, 625)) | |
| \tif outcome != "": label(outcome, Vector2(265, 335), 26) | |
| '''), | |
| 60: ('Snowball Passage', '''extends Node2D | |
| var ball = Vector2(480, 90) | |
| var radius = 12.0 | |
| var piles: Array[Vector2] = [] | |
| var gates = [Vector3(350, 420, 40), Vector3(650, 760, 34), Vector3(460, 1100, 48)] | |
| var camera_y = 0.0 | |
| var outcome = "" | |
| func restart() -> void: | |
| \tball = Vector2(480, 90) | |
| \tradius = 12 | |
| \tpiles.clear() | |
| \tfor i in range(22): piles.append(Vector2(210 + (i * 157) % 540, 180 + i * 58)) | |
| \tfor i in range(5): piles.append(Vector2(380 + i * 45, 1210 + i * 45)) | |
| \tcamera_y = 0 | |
| \toutcome = "" | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\tball.x = clampf(ball.x + Input.get_axis("ui_left", "ui_right") * 310 * dt, radius + 20, 940 - radius) | |
| \t\tball.y += 130 * dt | |
| \t\tfor i in range(piles.size() - 1, -1, -1): | |
| \t\t\tif ball.distance_to(piles[i]) < radius + 6: radius += 2; piles.remove_at(i) | |
| \t\tfor gate in gates: | |
| \t\t\tif absf(ball.y - gate.y) < radius + 7 and absf(ball.x - gate.x) + radius > gate.z: outcome = "Too wide for the gap! R to retry." | |
| \t\tcamera_y = maxf(0, ball.y - 190) | |
| \t\tif ball.y >= 1500: outcome = "Finish reached with enough snow! R to replay." if radius >= 26 else "Too small at finish. Need size 26. R to retry." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \tdraw_rect(Rect2(20, 95, 920, 545), Color("345773")) | |
| \tfor pile in piles: | |
| \t\tvar p = Vector2(pile.x, pile.y - camera_y) | |
| \t\tif p.y > 100 and p.y < 660: draw_circle(p, 6, Color("cedee9")) | |
| \tfor gate in gates: | |
| \t\tvar y = gate.y - camera_y | |
| \t\tdraw_rect(Rect2(20, y - 7, gate.x - gate.z - 20, 14), Color("92a7b7")) | |
| \t\tdraw_rect(Rect2(gate.x + gate.z, y - 7, 940 - gate.x - gate.z, 14), Color("92a7b7")) | |
| \t\tlabel("Gap size %.0f" % gate.z, Vector2(gate.x - 50, y - 20), 16) | |
| \tdraw_line(Vector2(20, 1500 - camera_y), Vector2(940, 1500 - camera_y), Color("70edc3"), 8) | |
| \tdraw_circle(Vector2(ball.x, ball.y - camera_y), radius, Color("e8f4f5")) | |
| \theader("SNOWBALL PASSAGE", "Left / Right: steer downhill / Collect snow to grow / Choose pickups: narrow gaps reject large balls / R") | |
| \tlabel("Size %.0f / 26 minimum Distance %.0f / 1500" % [radius, minf(ball.y, 1500)], Vector2(230, 620)) | |
| \tif outcome != "": label(outcome, Vector2(165, 345), 25) | |
| '''), | |
| 61: ('Power Budget', '''extends Node2D | |
| var selected = [false, false, false, false, false, false] | |
| var costs = [5, 6, 4, 5, 4, 4] | |
| var supplies = [Vector3i(4, 0, 0), Vector3i(0, 5, 0), Vector3i(0, 0, 3), Vector3i(2, 3, 0), Vector3i(2, 0, 2), Vector3i(0, 2, 1)] | |
| var totals = Vector3i.ZERO | |
| var spent = 0 | |
| var count = 0 | |
| var won = false | |
| var notice = "Choose exactly three connections." | |
| func restart() -> void: | |
| \tselected = [false, false, false, false, false, false] | |
| \tspent = 0 | |
| \tcount = 0 | |
| \ttotals = Vector3i.ZERO | |
| \twon = false | |
| \tnotice = "Choose exactly three connections." | |
| func recalculate() -> void: | |
| \tspent = 0 | |
| \tcount = 0 | |
| \ttotals = Vector3i.ZERO | |
| \tfor i in range(6): | |
| \t\tif selected[i]: spent += costs[i]; count += 1; totals += supplies[i] | |
| func game_input(event: InputEvent) -> void: | |
| \tif won: return | |
| \tif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| \t\tfor i in range(6): | |
| \t\t\tif Rect2(560, 125 + i * 70, 350, 59).has_point(get_global_mouse_position()): | |
| \t\t\t\tif selected[i] or count < 3: selected[i] = not selected[i]; recalculate() | |
| \tif event is InputEventKey and event.pressed and event.keycode == KEY_SPACE: | |
| \t\twon = count == 3 and spent <= 13 and totals.x >= 4 and totals.y >= 5 and totals.z >= 3 | |
| \t\tnotice = "Every neighborhood powered! R to replay." if won else "Need three links, within budget, meeting every demand." | |
| func _process(_dt: float) -> void: | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("POWER BUDGET", "Click connection cards / Choose exactly three with energy cost at most thirteen / Space: test / R") | |
| \tvar station = Vector2(115, 340) | |
| \tfor i in range(6): | |
| \t\tif selected[i]: | |
| \t\t\tfor n in range(3): | |
| \t\t\t\tif supplies[i][n] > 0: draw_line(station, Vector2(440, 190 + n * 140), Color.from_hsv(float(i) / 7, 0.45, 0.8), 2 + i) | |
| \t\tdraw_rect(Rect2(560, 125 + i * 70, 350, 59), Color("426c61") if selected[i] else Color("33475f")) | |
| \t\tlabel("%d Cost %d Supply A%d B%d C%d" % [i + 1, costs[i], supplies[i].x, supplies[i].y, supplies[i].z], Vector2(574, 161 + i * 70), 17) | |
| \tdraw_circle(station, 45, Color("e7c477")) | |
| \tlabel("GEN", station + Vector2(-24, 8), 22, Color("26384a")) | |
| \tfor n in range(3): | |
| \t\tvar need = [4, 5, 3][n] | |
| \t\tvar center = Vector2(440, 190 + n * 140) | |
| \t\tdraw_circle(center, 38, Color("71dcb5") if totals[n] >= need else Color("7d8ea5")) | |
| \t\tlabel("%s %d/%d" % [["A", "B", "C"][n], totals[n], need], center + Vector2(-31, 7), 18, Color("172c40")) | |
| \tlabel("Connections %d / 3 Energy %d / 13" % [count, spent], Vector2(245, 590), 22) | |
| \tlabel(notice, Vector2(145, 628), 21) | |
| '''), | |
| 62: ('Crusher Return', '''extends Node2D | |
| var player = Vector2(75, 345) | |
| var corridors = [Rect2(40, 290, 880, 110), Rect2(170, 195, 80, 115), Rect2(360, 380, 80, 125), Rect2(550, 195, 80, 115), Rect2(740, 380, 80, 125)] | |
| var crushers = [270.0, 460.0, 650.0] | |
| var elapsed = 0.0 | |
| var has_key = false | |
| var outcome = "" | |
| func restart() -> void: | |
| \tplayer = Vector2(75, 345) | |
| \telapsed = 0 | |
| \thas_key = false | |
| \toutcome = "" | |
| func closed(index: int) -> bool: | |
| \treturn fmod(elapsed + index * 0.95, 3.4) < 1.2 | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\telapsed += dt | |
| \t\tvar target = player + Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 225 * dt | |
| \t\tvar inside = false | |
| \t\tfor floor_box in corridors: | |
| \t\t\tif floor_box.grow(-9).has_point(target): inside = true | |
| \t\tif inside: player = target | |
| \t\tfor i in range(3): | |
| \t\t\tif closed(i) and Rect2(crushers[i] - 14, 290, 28, 110).grow(9).has_point(player): outcome = "Crushed! R to try again." | |
| \t\tif player.distance_to(Vector2(875, 345)) < 25: has_key = true | |
| \t\tif has_key and player.distance_to(Vector2(75, 345)) < 25: outcome = "Key returned safely! R to replay." | |
| \t\tif elapsed > 75: outcome = "Time expired. R to retry." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("CRUSHER RETURN", "Arrows: walk / Wait in alcoves while crushers close / Take far key and return left / R: restart") | |
| \tfor floor_box in corridors: draw_rect(floor_box, Color("30485d")) | |
| \tfor i in range(3): | |
| \t\tvar x = crushers[i] | |
| \t\tif closed(i): draw_rect(Rect2(x - 14, 290, 28, 110), Color("ed778b")) | |
| \t\telse: | |
| \t\t\tdraw_rect(Rect2(x - 14, 290, 28, 13), Color("839aae")) | |
| \t\t\tdraw_rect(Rect2(x - 14, 387, 28, 13), Color("839aae")) | |
| \t\tdraw_circle(Vector2(x, 270), 7, Color("f68591") if closed(i) else Color("70eac0")) | |
| \tif not has_key: draw_circle(Vector2(875, 345), 12, Color("f5d377")) | |
| \tdraw_rect(Rect2(50, 320, 40, 50), Color("507b61")) | |
| \tdraw_circle(player, 9, Color("72d7f1")) | |
| \tif has_key: draw_circle(player + Vector2(0, -18), 5, Color("f5d377")) | |
| \tlabel("Key %s Time %.0f / 75" % ["collected: return LEFT" if has_key else "waiting at RIGHT", elapsed], Vector2(240, 565), 23) | |
| \tif outcome != "": label(outcome, Vector2(270, 616), 25) | |
| '''), | |
| 63: ('Telescope Rings', '''extends Node2D | |
| var hub = Vector2(480, 350) | |
| var outer_angle = PI / 2 | |
| var inner_angle = -PI / 2 | |
| var star_angle = 0.0 | |
| var exposure = 0.0 | |
| var aligned = false | |
| var won = false | |
| func restart() -> void: | |
| \touter_angle = PI / 2 | |
| \tinner_angle = -PI / 2 | |
| \tstar_angle = 0 | |
| \texposure = 0 | |
| \taligned = false | |
| \twon = false | |
| func game_input(_event: InputEvent) -> void: | |
| \tpass | |
| func _process(dt: float) -> void: | |
| \tif not won: | |
| \t\tstar_angle += dt * 0.23 | |
| \t\tif Input.is_physical_key_pressed(KEY_A): outer_angle -= dt * 1.25 | |
| \t\tif Input.is_physical_key_pressed(KEY_D): outer_angle += dt * 1.25 | |
| \t\tif Input.is_physical_key_pressed(KEY_J): inner_angle -= dt * 1.25 | |
| \t\tif Input.is_physical_key_pressed(KEY_L): inner_angle += dt * 1.25 | |
| \t\taligned = absf(wrapf(outer_angle - star_angle, -PI, PI)) < 0.18 and absf(wrapf(inner_angle - star_angle, -PI, PI)) < 0.18 | |
| \t\texposure = minf(2.5, exposure + dt) if aligned else maxf(0, exposure - dt * 0.6) | |
| \t\twon = exposure >= 2.5 | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("TELESCOPE RINGS", "A / D: outer ring / J / L: inner ring / Align both gaps with moving star for 2.5 seconds / R") | |
| \tdraw_circle(hub, 15, Color("48627d")) | |
| \tvar star = hub + Vector2.from_angle(star_angle) * 220 | |
| \tdraw_line(hub, star, Color("7af1cc") if aligned else Color("4c586c"), 3) | |
| \tdraw_arc(hub, 150, outer_angle + 0.24, outer_angle + TAU - 0.24, 96, Color("8da6c0"), 15) | |
| \tdraw_arc(hub, 95, inner_angle + 0.24, inner_angle + TAU - 0.24, 72, Color("c193b8"), 15) | |
| \tfor i in range(4): | |
| \t\tvar offset = Vector2.from_angle(i * PI / 4) * 13 | |
| \t\tdraw_line(star - offset, star + offset, Color("ffe396"), 3) | |
| \tdraw_circle(star, 5, Color("fff3bd")) | |
| \tdraw_rect(Rect2(280, 602, 400, 15), Color("334359")) | |
| \tdraw_rect(Rect2(280, 602, 400 * exposure / 2.5, 15), Color("77ecc8")) | |
| \tlabel("Exposure %.1f / 2.5" % exposure, Vector2(365, 586), 22) | |
| \tif won: label("Star photographed! R to retry.", Vector2(285, 140), 26) | |
| '''), | |
| 64: ('Weight And Wings', '''extends Node2D | |
| var actors = [Vector2(90, 534), Vector2(150, 536)] | |
| var vertical = [0.0, 0.0] | |
| var grounded = [true, true] | |
| var selected = 0 | |
| var latched = false | |
| var bridge = false | |
| var outcome = "" | |
| func restart() -> void: | |
| \tactors = [Vector2(90, 534), Vector2(150, 536)] | |
| \tvertical = [0.0, 0.0] | |
| \tgrounded = [true, true] | |
| \tselected = 0 | |
| \tlatched = false | |
| \tbridge = false | |
| \toutcome = "" | |
| func game_input(event: InputEvent) -> void: | |
| \tif outcome != "" or not (event is InputEventKey): return | |
| \tif not event.pressed or event.echo: return | |
| \tif event.keycode == KEY_TAB: selected = 1 - selected | |
| \tif event.keycode == KEY_SPACE and selected == 1 and grounded[1]: vertical[1] = -475; grounded[1] = false | |
| func _process(dt: float) -> void: | |
| \tif outcome == "": | |
| \t\tbridge = latched or (absf(actors[0].x - 205) < 27 and actors[0].y > 510) | |
| \t\tvar floors = [Rect2(20, 550, 240, 90), Rect2(490, 550, 450, 90), Rect2(530, 430, 115, 16)] | |
| \t\tif bridge: floors.append(Rect2(260, 550, 230, 14)) | |
| \t\tfor i in range(2): | |
| \t\t\tvar half = 16 if i == 0 else 14 | |
| \t\t\tvar before = actors[i].y + half | |
| \t\t\tif i == selected: actors[i].x = clampf(actors[i].x + Input.get_axis("ui_left", "ui_right") * (160 if i == 0 else 150) * dt, half + 20, 940 - half) | |
| \t\t\tvertical[i] += 850 * dt | |
| \t\t\tactors[i].y += vertical[i] * dt | |
| \t\t\tgrounded[i] = false | |
| \t\t\tfor floor_box in floors: | |
| \t\t\t\tif actors[i].x + half > floor_box.position.x and actors[i].x - half < floor_box.end.x and before <= floor_box.position.y + 2 and actors[i].y + half >= floor_box.position.y and vertical[i] >= 0: | |
| \t\t\t\t\tactors[i].y = floor_box.position.y - half | |
| \t\t\t\t\tvertical[i] = 0 | |
| \t\t\t\t\tgrounded[i] = true | |
| \t\t\tif actors[i].y > 700: outcome = "A partner fell. R to retry." | |
| \t\tif actors[1].x > 530 and actors[1].x < 645 and absf(actors[1].y + 14 - 430) < 3: latched = true | |
| \t\tif actors[0].x > 840 and actors[1].x > 840 and grounded[0] and grounded[1]: outcome = "Both partners home! R to replay." | |
| \tqueue_redraw() | |
| func _draw() -> void: | |
| \theader("WEIGHT AND WINGS", "Tab: switch partner / Left-Right: move / Space: light partner jumps / Heavy holds switch / R") | |
| \tdraw_rect(Rect2(20, 550, 240, 90), Color("536a80")) | |
| \tdraw_rect(Rect2(490, 550, 450, 90), Color("536a80")) | |
| \tdraw_rect(Rect2(530, 430, 115, 16), Color("738da5")) | |
| \tdraw_rect(Rect2(180, 538, 50, 12), Color("75e2b8") if bridge else Color("d6a66c")) | |
| \tif bridge: draw_rect(Rect2(260, 550, 230, 14), Color("80b6a2")) | |
| \tdraw_circle(Vector2(587, 413), 9, Color("70efba") if latched else Color("efbc79")) | |
| \tdraw_rect(Rect2(837, 490, 85, 60), Color("365e55")) | |
| \tlabel("GOAL", Vector2(848, 518), 18) | |
| \tfor i in range(2): | |
| \t\tvar half = 16 if i == 0 else 14 | |
| \t\tdraw_rect(Rect2(actors[i] - Vector2(half, half), Vector2(half * 2, half * 2)), Color("caa789") if i == 0 else Color("74d5ef")) | |
| \t\tif selected == i: draw_arc(actors[i], half + 6, 0, TAU, 24, Color.WHITE, 2) | |
| \tlabel("Reach the upper latch with the jumper to keep the bridge open for both.", Vector2(110, 145), 20) | |
| \tlabel("Latch %s / Controlling %s" % ["ON" if latched else "off", "heavy" if selected == 0 else "jumper"], Vector2(220, 615)) | |
| \tif outcome != "": label(outcome, Vector2(290, 330), 26) | |
| '''), | |
| }) | |
| def main(): | |
| prompt_path = ROOT / 'data/teacher_prompts.jsonl' | |
| prompts = {int(row['id'].rsplit('-', 1)[1]): row for row in [json.loads(line) for line in prompt_path.read_text().splitlines() if line.strip()]} | |
| rows, audit = [], [] | |
| for number, (title, script) in sorted(GAMES.items()): | |
| files = {'project.godot': PROJECT % title, 'main.tscn': SCENE, 'main.gd': script + COMMON} | |
| output = json.dumps({'files': files}, ensure_ascii=False, sort_keys=True) | |
| parse_project(output) | |
| rows.append({'instruction': prompts[number]['instruction'], 'output': output}) | |
| audit.append({'prompt_id':prompts[number]['id'], 'title':title, | |
| 'output_sha256':hashlib.sha256(output.encode()).hexdigest(), | |
| 'gd_characters':len(files['main.gd']), 'static_project_contract':'passed', | |
| 'engine_validation':'not run; requires Linux Godot import and clean 60-second run'}) | |
| destination = ROOT / 'data/codex_teacher_batch_a.jsonl' | |
| temporary = destination.with_suffix('.tmp') | |
| temporary.write_text(''.join(json.dumps(row, ensure_ascii=False) + '\n' for row in rows)) | |
| temporary.replace(destination) | |
| provenance = {'author':'OpenAI Codex assistant in this build session', 'method':'Original in-session strong-teacher code authoring; not Qwen14B output', | |
| 'date':'2026-10-03', 'license':'Apache-2.0', 'upstream_game_code_used':False, | |
| 'prompt_source':'data/teacher_prompts.jsonl', 'prompt_source_sha256':hashlib.sha256(prompt_path.read_bytes()).hexdigest(), | |
| 'candidate_count':len(rows), 'admission_status':'candidates only; compile and runtime validation required', 'candidates':audit} | |
| p = ROOT / 'data/codex_teacher_batch_a_provenance.json' | |
| p.with_suffix('.tmp').write_text(json.dumps(provenance, indent=2) + '\n') | |
| p.with_suffix('.tmp').replace(p) | |
| print(json.dumps({'candidates':len(rows), 'path':str(destination), 'max_gd_characters':max(r['gd_characters'] for r in audit)})) | |
| if __name__ == '__main__': | |
| main() | |