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_b.py from khursheed/VibeGame-8B: direct link, hf CLI and curl.
- Browser
- Download file 79.2 kB
-
https://huggingface.co/khursheed/VibeGame-8B/resolve/main/code/scripts/create_teacher_batch_b.py
- Command line
-
hf download hf://khursheed/VibeGame-8B/code/scripts/create_teacher_batch_b.py
-
curl -L -o create_teacher_batch_b.py https://huggingface.co/khursheed/VibeGame-8B/resolve/main/code/scripts/create_teacher_batch_b.py
79.2 kB
| #!/usr/bin/env python3 | |
| """Materialize 32 original in-session Codex-authored games; no model/API call. | |
| This is authorship tooling, not a claim of executed Godot validation. Admission | |
| requires the separate Linux engine import and full 60-second runtime check. | |
| """ | |
| import hashlib | |
| import json | |
| from pathlib import Path | |
| import sys | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| from vibegame.project import parse_project, project_sha256 | |
| PROJECT = '''config_version=5 | |
| [application] | |
| config/name="%s" | |
| run/main_scene="res://main.tscn" | |
| [display] | |
| window/size/viewport_width=960 | |
| window/size/viewport_height=640 | |
| [rendering] | |
| renderer/rendering_method="gl_compatibility" | |
| environment/defaults/default_clear_color=Color(0.035, 0.047, 0.09, 1) | |
| ''' | |
| SCENE = '''[gd_scene load_steps=2 format=3] | |
| [ext_resource type="Script" path="res://main.gd" id="1"] | |
| [node name="Game" type="Node2D"] | |
| script = ExtResource("1") | |
| ''' | |
| HELPERS = ''' | |
| func text_at(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 backdrop(title: String, controls: String) -> void: | |
| \tdraw_rect(Rect2(0, 0, 960, 88), Color("121d35")) | |
| \ttext_at(title, Vector2(24, 35), 27, Color("5ef2d0")) | |
| \ttext_at(controls, Vector2(24, 68), 17, Color("c1cfe8")) | |
| ''' | |
| GAMES = {} | |
| GAMES[21] = ("Four Floor Courier", '''extends Node2D | |
| var people: Array[Dictionary] = [] | |
| var cabin: float = 520.0 | |
| var remaining: float = 100.0 | |
| var delivered: int = 0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| people.clear() | |
| for i in range(8): | |
| people.append({"floor": i % 4, "dest": (i % 4 + 1 + i / 4) % 4, "state": 0}) | |
| cabin = 520.0 | |
| remaining = 100.0 | |
| delivered = 0 | |
| outcome = "" | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| remaining -= dt | |
| cabin = clampf(cabin + Input.get_axis("ui_up", "ui_down") * 170.0 * dt, 160, 520) | |
| for floor in range(4): | |
| if absf(cabin - (520.0 - floor * 120.0)) < 9.0: | |
| for p in people: | |
| if p["state"] == 1 and p["dest"] == floor: | |
| p["state"] = 2 | |
| delivered += 1 | |
| var onboard: int = 0 | |
| for p in people: | |
| if p["state"] == 1: | |
| onboard += 1 | |
| for p in people: | |
| if p["state"] == 0 and p["floor"] == floor and onboard < 2: | |
| p["state"] = 1 | |
| onboard += 1 | |
| if delivered == 8: | |
| outcome = "All eight passengers delivered! R: new shift" | |
| elif remaining <= 0: | |
| outcome = "Shift ended before all deliveries. R: retry" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("FOUR FLOOR COURIER", "Up / Down: elevator | Automatic pickup and drop-off | Capacity 2 | R: restart") | |
| for floor in range(4): | |
| var y: float = 520.0 - floor * 120.0 | |
| draw_line(Vector2(90, y + 22), Vector2(750, y + 22), Color("46617d"), 3) | |
| text_at("FLOOR %d" % (floor + 1), Vector2(760, y), 20) | |
| draw_rect(Rect2(447, 115, 86, 452), Color("30455f"), false, 3) | |
| draw_rect(Rect2(452, cabin - 44, 76, 66), Color("5ef2d0"), false, 4) | |
| var seat: int = 0 | |
| for i in range(people.size()): | |
| var p: Dictionary = people[i] | |
| if p["state"] == 2: | |
| continue | |
| var pos: Vector2 = Vector2(180 + (i / 4) * 75, 520 - int(p["floor"]) * 120) | |
| if p["state"] == 1: | |
| pos = Vector2(468 + seat * 32, cabin) | |
| seat += 1 | |
| draw_circle(pos, 12, Color("ffcd6b")) | |
| text_at(str(int(p["dest"]) + 1), pos + Vector2(-6, -20), 20) | |
| text_at("Delivered %d / 8 Time %d" % [delivered, ceili(remaining)], Vector2(28, 606)) | |
| if outcome != "": | |
| text_at(outcome, Vector2(170, 110), 23) | |
| ''') | |
| GAMES[22] = ("Counterweight", '''extends Node2D | |
| var balls: Array[float] = [-100.0, 0.0, 100.0] | |
| var speeds: Array[float] = [10.0, -14.0, 6.0] | |
| var weight: float = 0.0 | |
| var angle: float = 0.0 | |
| var spin: float = 0.0 | |
| var timer: float = 30.0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| balls = [-100.0, 0.0, 100.0] | |
| speeds = [10.0, -14.0, 6.0] | |
| weight = 0.0 | |
| angle = 0.0 | |
| spin = 0.0 | |
| timer = 30.0 | |
| outcome = "" | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| timer -= dt | |
| weight = clampf(weight + Input.get_axis("ui_left", "ui_right") * 160.0 * dt, -195, 195) | |
| var torque: float = (balls[0] + balls[1] + balls[2] + weight * 3.0) * 0.0007 | |
| spin += (torque - angle * 0.5 - spin * 1.5) * dt | |
| angle = clampf(angle + spin * dt, -0.6, 0.6) | |
| for i in range(3): | |
| speeds[i] += (sin(angle) * 220.0 - speeds[i] * 0.08) * dt | |
| balls[i] += speeds[i] * dt | |
| if absf(balls[i]) > 220: | |
| outcome = "A ball fell! Move the counterweight earlier. R: retry" | |
| if timer <= 0 and outcome == "": | |
| outcome = "Balanced for thirty seconds! R: play again" | |
| queue_redraw() | |
| func point(x: float, y: float = 0.0) -> Vector2: | |
| return Vector2(480, 350) + Vector2(x, y).rotated(angle) | |
| func _draw() -> void: | |
| backdrop("COUNTERWEIGHT", "Left / Right: move the gold weight | Keep all three balls aboard for 30 seconds | R: restart") | |
| draw_colored_polygon(PackedVector2Array([Vector2(450, 460), Vector2(510, 460), Vector2(480, 350)]), Color("46617d")) | |
| draw_line(point(-230), point(230), Color("c1cfe8"), 12) | |
| for i in range(3): | |
| draw_circle(point(balls[i], -20), 14, Color.from_hsv(i * 0.2 + 0.45, 0.7, 1.0)) | |
| draw_circle(point(weight, 28), 18, Color("ffcd6b")) | |
| text_at("Time left %.1f Plank tilt %.1f degrees" % [maxf(timer, 0), rad_to_deg(angle)], Vector2(180, 530)) | |
| if outcome != "": | |
| text_at(outcome, Vector2(130, 580), 23) | |
| ''') | |
| GAMES[23] = ("Two Pan Kitchen", '''extends Node2D | |
| var heat: Array[float] = [0.0, 0.0] | |
| var cooking: Array[bool] = [false, false] | |
| var plates: int = 0 | |
| var mistakes: int = 0 | |
| var time_left: float = 60.0 | |
| var message: String = "Click a cold pan to begin. Click again to serve." | |
| var outcome: String = "" | |
| func pan_rect(i: int) -> Rect2: | |
| return Rect2(190 + i * 340, 170, 220, 230) | |
| func reset() -> void: | |
| heat = [0.0, 0.0] | |
| cooking = [false, false] | |
| plates = 0 | |
| mistakes = 0 | |
| time_left = 60.0 | |
| outcome = "" | |
| message = "Click a cold pan to begin. Click again to serve." | |
| func _input(event: InputEvent) -> void: | |
| if event is InputEventKey and event.pressed and event.keycode == KEY_R: | |
| reset() | |
| if outcome != "": | |
| return | |
| if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| for i in range(2): | |
| if pan_rect(i).has_point(event.position): | |
| if not cooking[i]: | |
| cooking[i] = true | |
| elif heat[i] >= 65 and heat[i] <= 85: | |
| plates += 1 | |
| message = "Perfectly served!" | |
| cooking[i] = false | |
| heat[i] = 0 | |
| else: | |
| mistakes += 1 | |
| message = "Too cold or too hot!" | |
| cooking[i] = false | |
| heat[i] = 0 | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| time_left -= dt | |
| for i in range(2): | |
| if cooking[i]: | |
| heat[i] += (20.0 + i * 7.0) * dt | |
| if heat[i] >= 100: | |
| mistakes += 1 | |
| cooking[i] = false | |
| heat[i] = 0 | |
| message = "Burned!" | |
| if plates >= 6: | |
| outcome = "Six plates served. Kitchen complete!" | |
| elif mistakes >= 3 or time_left <= 0: | |
| outcome = "Kitchen closed. R: try another shift" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("TWO PAN KITCHEN", "Click pan: start / serve | Serve between 65 and 85 degrees | Six plates, fewer than 3 mistakes | R: restart") | |
| for i in range(2): | |
| var r: Rect2 = pan_rect(i) | |
| draw_rect(r, Color("23344c")) | |
| draw_circle(r.position + Vector2(110, 90), 67, Color("ff875b") if cooking[i] else Color("718199")) | |
| text_at("PAN %d" % (i + 1), r.position + Vector2(65, 95)) | |
| draw_rect(Rect2(r.position + Vector2(10, 174), Vector2(200, 26)), Color("142037")) | |
| draw_rect(Rect2(r.position + Vector2(140, 174), Vector2(40, 26)), Color("25855f")) | |
| draw_line(r.position + Vector2(10 + heat[i] * 2, 170), r.position + Vector2(10 + heat[i] * 2, 205), Color.WHITE, 4) | |
| text_at("%.0f degrees" % heat[i], r.position + Vector2(43, 225)) | |
| text_at("Plates %d / 6 Mistakes %d / 3 Time %d" % [plates, mistakes, ceili(maxf(0, time_left))], Vector2(220, 470)) | |
| text_at(message, Vector2(180, 520)) | |
| text_at(outcome, Vector2(170, 575), 25, Color("ffcd6b")) | |
| ''') | |
| GAMES[24] = ("Echo Pads", '''extends Node2D | |
| var sequence: Array[int] = [0, 2] | |
| var showing: bool = true | |
| var show_clock: float = -0.8 | |
| var response: int = 0 | |
| var flash: int = -1 | |
| var flash_time: float = 0.0 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| sequence = [0, 2] | |
| showing = true | |
| show_clock = -0.8 | |
| response = 0 | |
| flash = -1 | |
| outcome = "" | |
| func pad(i: int) -> Rect2: | |
| return Rect2(200 + (i % 2) * 300, 160 + (i / 2) * 175, 260, 145) | |
| func press(i: int) -> void: | |
| if showing or outcome != "": | |
| return | |
| flash = i | |
| flash_time = 0.25 | |
| if i != sequence[response]: | |
| outcome = "Wrong pad. R: listen to a new round" | |
| return | |
| response += 1 | |
| if response == sequence.size(): | |
| if sequence.size() == 8: | |
| outcome = "Eight-note sequence remembered! R: replay" | |
| else: | |
| sequence.append(randi_range(0, 3)) | |
| showing = true | |
| show_clock = -1.0 | |
| response = 0 | |
| func _input(event: InputEvent) -> void: | |
| if event is InputEventKey and event.pressed and not event.echo: | |
| if event.keycode == KEY_R: | |
| reset() | |
| for i in range(4): | |
| if event.keycode == KEY_1 + i: | |
| press(i) | |
| if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| for i in range(4): | |
| if pad(i).has_point(event.position): | |
| press(i) | |
| func _process(dt: float) -> void: | |
| if outcome == "" and showing: | |
| show_clock += dt | |
| var index: int = floori(show_clock / 0.8) | |
| flash = -1 | |
| if index >= sequence.size(): | |
| showing = false | |
| elif index >= 0 and fmod(show_clock, 0.8) < 0.5: | |
| flash = sequence[index] | |
| elif not showing: | |
| flash_time -= dt | |
| if flash_time <= 0: | |
| flash = -1 | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("ECHO PADS", "Watch the flashes, then repeat at any speed | Click pads or press 1 / 2 / 3 / 4 | R: restart") | |
| for i in range(4): | |
| draw_rect(pad(i), Color.from_hsv(i * 0.23, 0.7, 1.0 if flash == i else 0.35)) | |
| text_at(str(i + 1), pad(i).position + Vector2(116, 90), 46) | |
| text_at("WATCH" if showing else "YOUR TURN - no timing requirement", Vector2(230, 125), 25) | |
| text_at("Sequence length %d / 8 Entered %d" % [sequence.size(), response], Vector2(240, 555)) | |
| text_at(outcome, Vector2(180, 610), 23, Color("ffcd6b")) | |
| ''') | |
| GAMES[25] = ("Crane Stack", '''extends Node2D | |
| var stack: Array[Rect2] = [] | |
| var crane: float = 180.0 | |
| var direction: float = 1.0 | |
| var falling: bool = false | |
| var package_pos: Vector2 = Vector2.ZERO | |
| var velocity: float = 0.0 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| stack.clear() | |
| crane = 180 | |
| direction = 1 | |
| falling = false | |
| outcome = "" | |
| func _input(event: InputEvent) -> void: | |
| if event is InputEventKey and event.pressed and not event.echo: | |
| if event.keycode == KEY_R: | |
| reset() | |
| if event.keycode == KEY_SPACE and not falling and outcome == "": | |
| falling = true | |
| package_pos = Vector2(crane - 58, 130) | |
| velocity = 0 | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| if not falling: | |
| crane += direction * (190 + stack.size() * 23) * dt | |
| if crane < 120 or crane > 840: | |
| direction *= -1 | |
| crane = clampf(crane, 120, 840) | |
| else: | |
| velocity += 570 * dt | |
| package_pos.y += velocity * dt | |
| var target: float = 550 - stack.size() * 52 | |
| if package_pos.y + 52 >= target: | |
| package_pos.y = target - 52 | |
| var overlap: float = 116 | |
| if not stack.is_empty(): | |
| overlap = minf(package_pos.x + 116, stack[-1].end.x) - maxf(package_pos.x, stack[-1].position.x) | |
| if package_pos.x < 350 or package_pos.x + 116 > 610 or overlap < 78: | |
| outcome = "Unstable placement! Stay inside the outline and overlap. R: retry" | |
| else: | |
| stack.append(Rect2(package_pos, Vector2(116, 52))) | |
| if stack.size() == 6: | |
| outcome = "Six stable packages! Tower complete. R: replay" | |
| falling = false | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("CRANE STACK", "Space: release package | Stay inside the tower and overlap the previous box | R: restart") | |
| draw_rect(Rect2(350, 208, 260, 344), Color("5ef2d0"), false, 3) | |
| draw_line(Vector2(80, 550), Vector2(880, 550), Color("c1cfe8"), 4) | |
| draw_line(Vector2(100, 115), Vector2(860, 115), Color("46617d"), 5) | |
| for i in range(stack.size()): | |
| draw_rect(stack[i], Color.from_hsv(i * 0.1, 0.6, 0.95)) | |
| draw_rect(stack[i], Color.WHITE, false, 2) | |
| if outcome == "": | |
| var pos: Vector2 = package_pos if falling else Vector2(crane - 58, 130) | |
| draw_rect(Rect2(pos, Vector2(116, 52)), Color("ffcd6b")) | |
| if not falling: | |
| draw_line(Vector2(crane, 115), Vector2(crane, 130), Color.WHITE, 3) | |
| text_at("Stable placements %d / 6" % stack.size(), Vector2(310, 590)) | |
| text_at(outcome, Vector2(75, 625), 20) | |
| ''') | |
| GAMES[26] = ("Trail Territory", '''extends Node2D | |
| const W: int = 14 | |
| const H: int = 10 | |
| const STEP: int = 38 | |
| var owned: Dictionary = {} | |
| var trail: Array[Vector2i] = [] | |
| var player: Vector2i = Vector2i(2, 5) | |
| var enemy: Vector2i = Vector2i(11, 5) | |
| var move_clock: float = 0.0 | |
| var enemy_clock: float = 0.0 | |
| var time_left: float = 90.0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| owned.clear() | |
| trail.clear() | |
| for y in range(H): | |
| for x in range(3): | |
| owned[Vector2i(x, y)] = true | |
| player = Vector2i(2, 5) | |
| enemy = Vector2i(11, 5) | |
| move_clock = 0 | |
| enemy_clock = 0 | |
| time_left = 90 | |
| outcome = "" | |
| func inside(p: Vector2i) -> bool: | |
| return p.x >= 0 and p.y >= 0 and p.x < W and p.y < H | |
| func reconnect() -> void: | |
| for p in trail: | |
| owned[p] = true | |
| trail.clear() | |
| var reached: Dictionary = {enemy: true} | |
| var queue: Array[Vector2i] = [enemy] | |
| var cursor: int = 0 | |
| while cursor < queue.size(): | |
| var cell: Vector2i = queue[cursor] | |
| cursor += 1 | |
| for direction in [Vector2i.LEFT, Vector2i.RIGHT, Vector2i.UP, Vector2i.DOWN]: | |
| var next: Vector2i = cell + direction | |
| if inside(next) and not owned.has(next) and not reached.has(next): | |
| reached[next] = true | |
| queue.append(next) | |
| for y in range(H): | |
| for x in range(W): | |
| var cell: Vector2i = Vector2i(x, y) | |
| if not reached.has(cell): | |
| owned[cell] = true | |
| if owned.size() >= 92: | |
| outcome = "Territory secured! R: another expedition" | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| time_left -= dt | |
| move_clock -= dt | |
| var direction: Vector2i = Vector2i.ZERO | |
| if Input.is_action_pressed("ui_left"): | |
| direction = Vector2i.LEFT | |
| elif Input.is_action_pressed("ui_right"): | |
| direction = Vector2i.RIGHT | |
| elif Input.is_action_pressed("ui_up"): | |
| direction = Vector2i.UP | |
| elif Input.is_action_pressed("ui_down"): | |
| direction = Vector2i.DOWN | |
| if move_clock <= 0 and direction != Vector2i.ZERO: | |
| move_clock = 0.13 | |
| var next: Vector2i = player + direction | |
| if inside(next): | |
| if trail.has(next): | |
| outcome = "You crossed your own trail. R: retry" | |
| else: | |
| player = next | |
| if owned.has(player): | |
| if not trail.is_empty(): | |
| reconnect() | |
| else: | |
| trail.append(player) | |
| enemy_clock -= dt | |
| if enemy_clock <= 0: | |
| enemy_clock = 0.22 | |
| var directions: Array[Vector2i] = [Vector2i.LEFT, Vector2i.RIGHT, Vector2i.UP, Vector2i.DOWN] | |
| var next: Vector2i = enemy + directions[randi_range(0, 3)] | |
| if inside(next) and not owned.has(next): | |
| enemy = next | |
| if trail.has(enemy): | |
| outcome = "Enemy cut the active trail! R: retry" | |
| elif time_left <= 0: | |
| outcome = "Time expired. R: retry" | |
| queue_redraw() | |
| func center(p: Vector2i) -> Vector2: | |
| return Vector2(215, 155) + Vector2(p) * STEP + Vector2.ONE * STEP * 0.5 | |
| func _draw() -> void: | |
| backdrop("TRAIL TERRITORY", "Arrows: step | Leave blue safety, draw a loop, reconnect | Avoid the red enemy | R: restart") | |
| for y in range(H): | |
| for x in range(W): | |
| var p: Vector2i = Vector2i(x, y) | |
| var color: Color = Color("287a94") if owned.has(p) else Color("182439") | |
| if trail.has(p): | |
| color = Color("ffcd6b") | |
| draw_rect(Rect2(center(p) - Vector2.ONE * 18, Vector2(36, 36)), color) | |
| draw_circle(center(enemy), 13, Color("ff6176")) | |
| draw_circle(center(player), 11, Color.WHITE) | |
| text_at("Claim %d / 92 tiles Time %d" % [owned.size(), ceili(maxf(0, time_left))], Vector2(240, 575)) | |
| text_at(outcome, Vector2(220, 620), 23) | |
| ''') | |
| GAMES[27] = ("Charge Duel", '''extends Node2D | |
| var hp: int = 5 | |
| var foe_hp: int = 5 | |
| var energy: int = 2 | |
| var foe_energy: int = 2 | |
| var rounds: int = 0 | |
| var report: String = "Choose an action. The opponent commits without seeing your choice." | |
| var outcome: String = "" | |
| var names: Array[String] = ["ATTACK", "BLOCK", "CHARGE"] | |
| func reset() -> void: | |
| hp = 5 | |
| foe_hp = 5 | |
| energy = 2 | |
| foe_energy = 2 | |
| rounds = 0 | |
| report = "Attack costs 1 energy. Block defeats attack. Charge restores 1." | |
| outcome = "" | |
| func button(i: int) -> Rect2: | |
| return Rect2(150 + i * 230, 420, 200, 72) | |
| func choose(action: int) -> void: | |
| if outcome != "": | |
| return | |
| if action == 0 and energy == 0: | |
| report = "No energy: block or charge first." | |
| return | |
| var rival: int = 2 if foe_energy == 0 else randi_range(0, 2) | |
| rounds += 1 | |
| if action == 0: | |
| energy -= 1 | |
| if rival == 0: | |
| foe_energy -= 1 | |
| if action == 2: | |
| energy = mini(energy + 1, 4) | |
| if rival == 2: | |
| foe_energy = mini(foe_energy + 1, 4) | |
| if action == 0: | |
| if rival == 1: | |
| hp -= 1 | |
| else: | |
| foe_hp -= 1 | |
| if rival == 0: | |
| if action == 1: | |
| foe_hp -= 1 | |
| else: | |
| hp -= 1 | |
| report = "You: %s Rival: %s Round %d" % [names[action], names[rival], rounds] | |
| if hp <= 0 and foe_hp <= 0: | |
| outcome = "Mutual knockout! R: rematch" | |
| elif foe_hp <= 0: | |
| outcome = "You win the duel! R: rematch" | |
| elif hp <= 0: | |
| outcome = "Rival wins. R: rematch" | |
| func _input(event: InputEvent) -> void: | |
| if event is InputEventKey and event.pressed and not event.echo: | |
| if event.keycode == KEY_R: | |
| reset() | |
| for i in range(3): | |
| if event.keycode == KEY_1 + i: | |
| choose(i) | |
| if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| for i in range(3): | |
| if button(i).has_point(event.position): | |
| choose(i) | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("CHARGE DUEL", "Click an action or press 1 / 2 / 3 | Attack beats charge; block counters attack | R: restart") | |
| draw_circle(Vector2(280, 235), 70, Color("5ef2d0")) | |
| draw_circle(Vector2(690, 235), 70, Color("ff6176")) | |
| text_at("YOU", Vector2(252, 244), 25, Color("121d35")) | |
| text_at("RIVAL", Vector2(650, 244), 25) | |
| text_at("HP %d Energy %d / 4" % [hp, energy], Vector2(180, 345)) | |
| text_at("HP %d Energy %d / 4" % [foe_hp, foe_energy], Vector2(570, 345)) | |
| for i in range(3): | |
| draw_rect(button(i), Color("334966")) | |
| text_at("%d: %s" % [i + 1, names[i]], button(i).position + Vector2(18, 45)) | |
| text_at(report, Vector2(70, 550), 20) | |
| text_at(outcome, Vector2(280, 610), 26, Color("ffcd6b")) | |
| ''') | |
| GAMES[28] = ("Binary Wires", '''extends Node2D | |
| var lamps: int = 0 | |
| var target: int = 5 | |
| var masks: Array[int] = [3, 6, 7] | |
| var moves: int = 0 | |
| var puzzles: int = 0 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| lamps = 0 | |
| target = 5 | |
| moves = 0 | |
| puzzles = 0 | |
| outcome = "" | |
| func toggle(i: int) -> void: | |
| if outcome != "": | |
| return | |
| lamps = lamps ^ masks[i] | |
| moves += 1 | |
| if lamps == target: | |
| puzzles += 1 | |
| if puzzles == 4: | |
| outcome = "Four circuits repaired! R: restart" | |
| else: | |
| lamps = target | |
| target = (target + 3) % 8 | |
| moves = 0 | |
| elif moves >= 6: | |
| outcome = "Six moves used. Circuit locked! R: retry" | |
| func switch_rect(i: int) -> Rect2: | |
| return Rect2(150 + 240 * i, 390, 180, 75) | |
| func _input(event: InputEvent) -> void: | |
| if event is InputEventKey and event.pressed and not event.echo: | |
| if event.keycode == KEY_R: | |
| reset() | |
| for i in range(3): | |
| if event.keycode == KEY_1 + i: | |
| toggle(i) | |
| if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: | |
| for i in range(3): | |
| if switch_rect(i).has_point(event.position): | |
| toggle(i) | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("BINARY WIRES", "Click switches or press 1 / 2 / 3 | Match the target in at most six toggles | R: restart") | |
| text_at("TARGET", Vector2(45, 174), 22) | |
| text_at("LAMPS", Vector2(45, 278), 22) | |
| for j in range(3): | |
| var x: float = 240 + 240 * j | |
| draw_circle(Vector2(x, 165), 24, Color("5ef2d0") if target & (1 << j) else Color("26394e")) | |
| text_at(str((target >> j) & 1), Vector2(x - 6, 172), 21, Color.WHITE) | |
| draw_circle(Vector2(x, 265), 35, Color("ffcd6b") if lamps & (1 << j) else Color("26394e")) | |
| text_at(str((lamps >> j) & 1), Vector2(x - 7, 273), 24, Color.WHITE) | |
| for i in range(3): | |
| for j in range(3): | |
| if masks[i] & (1 << j): | |
| draw_line(Vector2(240 + 240 * i, 390), Vector2(240 + 240 * j, 305), Color.from_hsv(i * 0.24, 0.5, 0.6), 2) | |
| draw_rect(switch_rect(i), Color("334966")) | |
| text_at("SWITCH %d" % (i + 1), switch_rect(i).position + Vector2(23, 45)) | |
| text_at("Repairs %d / 4 Moves %d / 6 Wires show affected lamps" % [puzzles, moves], Vector2(140, 535)) | |
| text_at(outcome, Vector2(250, 600), 25) | |
| ''') | |
| GAMES[29] = ("Water Gates", '''extends Node2D | |
| var opened: Array[bool] = [false, false] | |
| var levels: Array[int] = [0, 0] | |
| var drops: Array[Dictionary] = [] | |
| var spawn_clock: float = 0.0 | |
| var time_left: float = 65.0 | |
| var outcome: String = "" | |
| const TARGET: int = 20 | |
| func reset() -> void: | |
| opened = [false, false] | |
| levels = [0, 0] | |
| drops.clear() | |
| spawn_clock = 0 | |
| time_left = 65 | |
| outcome = "" | |
| func gate(i: int) -> Rect2: | |
| return Rect2(270 + i * 320, 210, 100, 55) | |
| func _input(event: InputEvent) -> void: | |
| if event is InputEventKey and event.pressed and event.keycode == KEY_R: | |
| reset() | |
| if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT and outcome == "": | |
| for i in range(2): | |
| if gate(i).has_point(event.position): | |
| opened[i] = not opened[i] | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| time_left -= dt | |
| spawn_clock -= dt | |
| if spawn_clock <= 0: | |
| spawn_clock = 0.25 | |
| for i in range(2): | |
| if opened[i]: | |
| drops.append({"p": Vector2(320 + i * 320, 265), "v": 0.0, "tank": i}) | |
| for k in range(drops.size() - 1, -1, -1): | |
| var d: Dictionary = drops[k] | |
| d["v"] += 440.0 * dt | |
| d["p"] += Vector2(0, float(d["v"]) * dt) | |
| var i: int = d["tank"] | |
| if d["p"].y >= 555 - levels[i] * 8: | |
| levels[i] += 1 | |
| drops.remove_at(k) | |
| if levels[i] > TARGET: | |
| outcome = "A tank overflowed its marked level! R: retry" | |
| if levels[0] == TARGET and levels[1] == TARGET and drops.is_empty() and not opened[0] and not opened[1]: | |
| outcome = "Both tanks precisely filled! R: another run" | |
| elif time_left <= 0 and outcome == "": | |
| outcome = "Water service timed out. R: retry" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("WATER GATES", "Click gates: open / close | Fill BOTH tanks to 20 drops | Close early: falling drops still count | R: restart") | |
| draw_rect(Rect2(250, 112, 460, 65), Color("2588b5")) | |
| text_at("WATER SOURCE", Vector2(380, 155)) | |
| for i in range(2): | |
| var x: float = 320 + i * 320 | |
| draw_line(Vector2(x, 177), Vector2(x, 210), Color("6fbde8"), 12) | |
| draw_rect(gate(i), Color("287b65") if opened[i] else Color("9e5263")) | |
| text_at("OPEN" if opened[i] else "CLOSED", gate(i).position + Vector2(7, 36), 20) | |
| draw_rect(Rect2(x - 100, 330, 200, 225), Color("182f49"), false, 4) | |
| draw_rect(Rect2(x - 95, 555 - levels[i] * 8, 190, levels[i] * 8), Color("329fd2")) | |
| draw_line(Vector2(x - 105, 395), Vector2(x + 105, 395), Color("ffcd6b"), 3) | |
| text_at("%d / 20" % levels[i], Vector2(x - 35, 590)) | |
| for d in drops: | |
| draw_circle(d["p"], 7, Color("79d7ff")) | |
| text_at("Time %d" % ceili(maxf(0, time_left)), Vector2(25, 120), 20) | |
| text_at(outcome, Vector2(190, 626), 23) | |
| ''') | |
| GAMES[30] = ("Shuttle Platforms", '''extends Node2D | |
| var player: Vector2 = Vector2(60, 465) | |
| var vy: float = 0.0 | |
| var platforms: Array[Rect2] = [] | |
| var grounded: int = -2 | |
| var clock: float = 0.0 | |
| var carrying: bool = true | |
| var lives: int = 3 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| player = Vector2(60, 465) | |
| vy = 0 | |
| clock = 0 | |
| carrying = true | |
| lives = 3 | |
| grounded = -2 | |
| outcome = "" | |
| platforms = [Rect2(230, 430, 100, 16), Rect2(425, 360, 100, 16), Rect2(620, 430, 100, 16)] | |
| func _physics_process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| clock += dt | |
| for i in range(3): | |
| var previous_x: float = platforms[i].position.x | |
| platforms[i].position.x = 230 + i * 195 + sin(clock * 1.2 + i * 1.8) * 38 | |
| if grounded == i: | |
| player.x += platforms[i].position.x - previous_x | |
| if Input.is_action_just_pressed("ui_accept") and grounded != -1: | |
| vy = -445 | |
| grounded = -1 | |
| player.x = clampf(player.x + Input.get_axis("ui_left", "ui_right") * 235 * dt, 0, 935) | |
| var old_bottom: float = player.y + 35 | |
| vy += 900 * dt | |
| player.y += vy * dt | |
| grounded = -1 | |
| var surfaces: Array[Rect2] = platforms.duplicate() | |
| surfaces.append(Rect2(0, 500, 155, 30)) | |
| surfaces.append(Rect2(815, 500, 145, 30)) | |
| if vy >= 0: | |
| for i in range(surfaces.size()): | |
| var r: Rect2 = surfaces[i] | |
| if old_bottom <= r.position.y + 3 and player.y + 35 >= r.position.y and player.x + 25 > r.position.x and player.x < r.end.x: | |
| player.y = r.position.y - 35 | |
| vy = 0 | |
| grounded = i if i < 3 else -2 | |
| if player.y > 650: | |
| lives -= 1 | |
| player = Vector2(60 if carrying else 875, 465) | |
| vy = 0 | |
| grounded = -2 | |
| if lives == 0: | |
| outcome = "Three falls. R: try the delivery again" | |
| if carrying and player.x > 870 and grounded == -2: | |
| carrying = false | |
| if not carrying and player.x < 85 and grounded == -2: | |
| outcome = "Delivered and returned safely! R: new route" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("SHUTTLE PLATFORMS", "Left / Right: move | Space: jump | Deliver the box at right, then return left | R: restart") | |
| draw_rect(Rect2(0, 500, 155, 140), Color("334966")) | |
| draw_rect(Rect2(815, 500, 145, 140), Color("334966")) | |
| for p in platforms: | |
| draw_rect(p, Color("5ef2d0")) | |
| draw_rect(Rect2(player, Vector2(25, 35)), Color("79bbff")) | |
| if carrying: | |
| draw_rect(Rect2(player + Vector2(3, -20), Vector2(20, 20)), Color("ffcd6b")) | |
| else: | |
| draw_rect(Rect2(898, 472, 28, 28), Color("ffcd6b")) | |
| text_at("HOME", Vector2(35, 555), 20) | |
| text_at("DEPOT", Vector2(845, 555), 20) | |
| text_at("Lives %d | %s" % [lives, "Carry box to depot" if carrying else "Box delivered - return HOME"], Vector2(230, 125)) | |
| text_at(outcome, Vector2(260, 605), 24) | |
| ''') | |
| GAMES[31] = ("Driveway Plow", '''extends Node2D | |
| const W: int = 10 | |
| const H: int = 7 | |
| const CELL: int = 56 | |
| var snow: Dictionary = {} | |
| var piles: Dictionary = {} | |
| var player: Vector2i = Vector2i(4, 6) | |
| var moves: int = 0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| snow.clear() | |
| piles.clear() | |
| for y in range(1, 6): | |
| piles[Vector2i(0, y)] = 0 | |
| piles[Vector2i(9, y)] = 0 | |
| for x in [2, 3, 6, 7]: | |
| snow[Vector2i(x, y)] = true | |
| player = Vector2i(4, 6) | |
| moves = 0 | |
| outcome = "" | |
| func inside(p: Vector2i) -> bool: | |
| return p.x >= 0 and p.y >= 0 and p.x < W and p.y < H | |
| func step(direction: Vector2i) -> void: | |
| if outcome != "": | |
| return | |
| var next: Vector2i = player + direction | |
| if not inside(next) or piles.has(next): | |
| return | |
| if snow.has(next): | |
| var chain: Array[Vector2i] = [] | |
| var end: Vector2i = next | |
| while snow.has(end): | |
| chain.append(end) | |
| end += direction | |
| if not inside(end): | |
| return | |
| if (end.y == 0 or end.y == 6) and (end.x == 4 or end.x == 5): | |
| outcome = "Snow blocked the garage exit corridor. R: retry" | |
| return | |
| for i in range(chain.size() - 1, -1, -1): | |
| var dest: Vector2i = chain[i] + direction | |
| snow.erase(chain[i]) | |
| if piles.has(dest): | |
| piles[dest] += 1 | |
| else: | |
| snow[dest] = true | |
| player = next | |
| moves += 1 | |
| if snow.is_empty(): | |
| outcome = "Every driveway tile cleared; exit remains open! R: restart" | |
| func _input(event: InputEvent) -> void: | |
| if event is InputEventKey and event.pressed and not event.echo: | |
| if event.keycode == KEY_R: | |
| reset() | |
| elif event.keycode == KEY_LEFT: | |
| step(Vector2i.LEFT) | |
| elif event.keycode == KEY_RIGHT: | |
| step(Vector2i.RIGHT) | |
| elif event.keycode == KEY_UP: | |
| step(Vector2i.UP) | |
| elif event.keycode == KEY_DOWN: | |
| step(Vector2i.DOWN) | |
| queue_redraw() | |
| func center(p: Vector2i) -> Vector2: | |
| return Vector2(200, 155) + Vector2(p) * CELL + Vector2.ONE * 28 | |
| func _draw() -> void: | |
| backdrop("DRIVEWAY PLOW", "Tap arrows: move and push snow | Push into green side piles | Keep the yellow exit open | R: restart") | |
| for y in range(H): | |
| for x in range(W): | |
| var p: Vector2i = Vector2i(x, y) | |
| var color: Color = Color("28394e") | |
| if piles.has(p): | |
| color = Color("26785e") | |
| elif (y == 0 or y == 6) and (x == 4 or x == 5): | |
| color = Color("827035") | |
| draw_rect(Rect2(center(p) - Vector2.ONE * 27, Vector2(54, 54)), color) | |
| if snow.has(p): | |
| draw_circle(center(p), 21, Color("d5ebff")) | |
| if piles.has(p): | |
| text_at(str(piles[p]), center(p) + Vector2(-6, 7), 19) | |
| draw_rect(Rect2(center(player) - Vector2(17, 17), Vector2(34, 34)), Color("ff955b")) | |
| text_at("Snow tiles left %d / 20 Moves %d" % [snow.size(), moves], Vector2(270, 585)) | |
| text_at(outcome, Vector2(145, 625), 21) | |
| ''') | |
| GAMES[32] = ("Shepherd Gate", '''extends Node2D | |
| var sheep: Array[Vector2] = [] | |
| var secured: Array[bool] = [] | |
| var shepherd: Vector2 = Vector2(160, 340) | |
| var time_left: float = 90.0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| sheep = [Vector2(240, 235), Vector2(360, 295), Vector2(270, 415), Vector2(410, 440), Vector2(460, 215)] | |
| secured = [false, false, false, false, false] | |
| shepherd = Vector2(160, 340) | |
| time_left = 90 | |
| outcome = "" | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| time_left -= dt | |
| shepherd = get_global_mouse_position() | |
| shepherd.x = clampf(shepherd.x, 35, 660) | |
| shepherd.y = clampf(shepherd.y, 125, 575) | |
| # The marked entrance is reserved for sheep: shepherd stays outside it. | |
| if shepherd.x > 595 and shepherd.y > 265 and shepherd.y < 415: | |
| shepherd.x = 595 | |
| for i in range(sheep.size()): | |
| if secured[i]: | |
| continue | |
| var velocity: Vector2 = Vector2.ZERO | |
| var away: Vector2 = sheep[i] - shepherd | |
| if away.length() < 145: | |
| velocity += away.normalized() * (145 - away.length()) * 1.1 | |
| for j in range(sheep.size()): | |
| if i != j and not secured[j]: | |
| var separation: Vector2 = sheep[i] - sheep[j] | |
| if separation.length() < 38: | |
| velocity += separation.normalized() * 35 | |
| var next: Vector2 = sheep[i] + velocity.limit_length(110) * dt | |
| next.y = clampf(next.y, 140, 555) | |
| next.x = maxf(next.x, 50) | |
| if next.x > 680 and (next.y < 285 or next.y > 395): | |
| next.x = 680 | |
| if next.x >= 690 and next.y >= 285 and next.y <= 395: | |
| secured[i] = true | |
| next = Vector2(750 + (i % 2) * 85, 230 + (i / 2) * 80) | |
| sheep[i] = next | |
| var count: int = 0 | |
| for done in secured: | |
| if done: | |
| count += 1 | |
| if count == sheep.size(): | |
| outcome = "Every sheep penned; gate corridor clear! R: replay" | |
| elif time_left <= 0: | |
| outcome = "Time ran out. Approach from behind each sheep. R: retry" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("SHEPHERD GATE", "Mouse: shepherd repels nearby sheep | Herd them through the right-side gate | Keep corridor clear | R: restart") | |
| draw_rect(Rect2(30, 120, 650, 465), Color("1a3b37")) | |
| draw_rect(Rect2(700, 160, 220, 380), Color("285746")) | |
| draw_rect(Rect2(595, 285, 105, 110), Color("746532")) | |
| draw_line(Vector2(690, 120), Vector2(690, 278), Color("c1cfe8"), 8) | |
| draw_line(Vector2(690, 402), Vector2(690, 585), Color("c1cfe8"), 8) | |
| text_at("PEN", Vector2(775, 145)) | |
| var count: int = 0 | |
| for i in range(sheep.size()): | |
| draw_circle(sheep[i], 16, Color.WHITE) | |
| draw_circle(sheep[i] + Vector2(10, 0), 7, Color("68798b")) | |
| if secured[i]: | |
| count += 1 | |
| draw_circle(shepherd, 15, Color("ff9568")) | |
| draw_arc(shepherd, 145, 0, TAU, 48, Color(1, 0.7, 0.4, 0.18), 1) | |
| text_at("Penned %d / 5 Time %d" % [count, ceili(maxf(time_left, 0))], Vector2(220, 610)) | |
| text_at(outcome, Vector2(120, 108), 22) | |
| ''') | |
| def main(): | |
| # Access only the explicitly assigned training prompt IDs; no blind file read. | |
| wanted = {f"vg8b-teacher-v1-{i:03d}" for i in list(range(21, 33)) + list(range(65, 85))} | |
| prompts = {} | |
| with (ROOT / "data/teacher_prompts.jsonl").open() as handle: | |
| for line in handle: | |
| row = json.loads(line) | |
| if row["id"] in wanted: | |
| prompts[row["id"]] = row["instruction"] | |
| if set(prompts) != wanted: | |
| raise RuntimeError("Missing assigned training prompts") | |
| rows, provenance = [], [] | |
| for number, (title, source) in sorted(GAMES.items()): | |
| identity = f"vg8b-teacher-v1-{number:03d}" | |
| files = {"project.godot": PROJECT % title, "main.tscn": SCENE, "main.gd": source + HELPERS} | |
| output = json.dumps({"files": files}, ensure_ascii=False, separators=(",", ":")) | |
| parse_project(output) | |
| rows.append({"instruction": prompts[identity], "output": output}) | |
| provenance.append({"prompt_id": identity, "title": title, "project_sha256": project_sha256(files), | |
| "output_sha256": hashlib.sha256(output.encode()).hexdigest(), | |
| "gdscript_characters": len(files["main.gd"]), "static_contract_pass": True}) | |
| output_path = ROOT / "data/codex_teacher_batch_b.jsonl" | |
| temporary = output_path.with_suffix(".tmp") | |
| temporary.write_text("".join(json.dumps(row, ensure_ascii=False) + "\n" for row in rows)) | |
| temporary.replace(output_path) | |
| report = {"authorship": "Original in-session Codex teacher authorship; not generated by the local 14B sampler.", | |
| "source": "Independently implemented game mechanics responding to assigned training prompts 021–032 and 065–084.", | |
| "license": "Apache-2.0", "assets": "Procedural Godot drawing and bundled default font only; no third-party game assets.", | |
| "shared_boilerplate": "Canonical Godot project/scene and text drawing helpers follow the repository's original seeds.", | |
| "blind_prompts_read": False, "engine_validation": "NOT RUN. Pending Linux import and full 60-second check; these are candidates, not admitted training data.", | |
| "candidates_sha256": hashlib.sha256(output_path.read_bytes()).hexdigest(), "count": len(rows), "games": provenance} | |
| (ROOT / "data/codex_teacher_batch_b_provenance.json").write_text(json.dumps(report, indent=2) + "\n") | |
| print(json.dumps({"candidates": len(rows), "static_contract": "passed", "max_gdscript_characters": max(x["gdscript_characters"] for x in provenance), "engine_validation": "pending"})) | |
| GAMES[65] = ("Landing Lines", '''extends Node2D | |
| var planes: Array[Dictionary] = [] | |
| var zones: Array[Vector2] = [Vector2(770, 210), Vector2(770, 480)] | |
| var selected: int = -1 | |
| var created: int = 0 | |
| var landed: int = 0 | |
| var spawn: float = 0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| planes.clear() | |
| selected = -1 | |
| created = 0 | |
| landed = 0 | |
| spawn = 0 | |
| outcome = "" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and e.keycode == KEY_R: | |
| reset() | |
| if e is InputEventMouseButton and e.button_index == MOUSE_BUTTON_LEFT: | |
| if e.pressed: | |
| selected = -1 | |
| for i in range(planes.size()): | |
| if planes[i]["p"].distance_to(e.position) < 25: | |
| selected = i | |
| planes[i]["path"] = [] | |
| else: | |
| selected = -1 | |
| if e is InputEventMouseMotion and selected >= 0 and selected < planes.size(): | |
| var path: Array = planes[selected]["path"] | |
| if path.is_empty() or path[-1].distance_to(e.position) > 18: | |
| path.append(e.position) | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| spawn -= dt | |
| if spawn <= 0 and created < 8: | |
| planes.append({"p": Vector2(50, 170 + (created % 2) * 320), "zone": created % 2, "path": []}) | |
| created += 1 | |
| spawn = 5.5 | |
| for i in range(planes.size() - 1, -1, -1): | |
| var p: Dictionary = planes[i] | |
| var path: Array = p["path"] | |
| if not path.is_empty(): | |
| p["p"] = p["p"].move_toward(path[0], 85 * dt) | |
| if p["p"].distance_to(path[0]) < 2: | |
| path.pop_front() | |
| else: | |
| p["p"] += Vector2(23 * dt, 0) | |
| if p["p"].distance_to(zones[p["zone"]]) < 24: | |
| planes.remove_at(i) | |
| selected = -1 | |
| landed += 1 | |
| elif p["p"].x > 950 or p["p"].y < 90 or p["p"].y > 630: | |
| outcome = "Plane left the airspace! R: retry" | |
| for i in range(planes.size()): | |
| for j in range(i): | |
| if planes[i]["p"].distance_to(planes[j]["p"]) < 27: | |
| outcome = "Loss of separation! R: retry" | |
| if landed == 8: | |
| outcome = "Eight safe landings! R: another shift" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("LANDING LINES", "Drag from a plane to draw its route | Match landing-zone color | Keep planes separated | R: restart") | |
| for i in range(2): | |
| draw_circle(zones[i], 38, Color("286c90") if i == 0 else Color("806631")) | |
| text_at(str(i + 1), zones[i] + Vector2(-8, 9), 28) | |
| for p in planes: | |
| var previous: Vector2 = p["p"] | |
| for point in p["path"]: | |
| draw_line(previous, point, Color("526d88"), 2) | |
| previous = point | |
| draw_circle(p["p"], 12, Color("79d7ff") if p["zone"] == 0 else Color("ffcd6b")) | |
| text_at("Landed %d / 8" % landed, Vector2(35, 610)) | |
| text_at(outcome, Vector2(330, 610), 24) | |
| ''') | |
| GAMES[66] = ("Six Plot Farm", '''extends Node2D | |
| var plants: Array[Dictionary] = [] | |
| var water: int = 12 | |
| var harvest: int = 0 | |
| var wilted: int = 0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| plants.clear() | |
| for i in range(6): | |
| plants.append({"stage": 0, "moisture": 0.0, "growth": 0.0}) | |
| water = 12 | |
| harvest = 0 | |
| wilted = 0 | |
| outcome = "" | |
| func plot(i: int) -> Rect2: | |
| return Rect2(170 + (i % 3) * 215, 175 + (i / 3) * 185, 185, 155) | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and e.keycode == KEY_R: | |
| reset() | |
| if outcome == "" and e is InputEventMouseButton and e.pressed and e.button_index == MOUSE_BUTTON_LEFT: | |
| for i in range(6): | |
| if plot(i).has_point(e.position): | |
| var p: Dictionary = plants[i] | |
| if p["stage"] == 0: | |
| p["stage"] = 1 | |
| p["moisture"] = 70.0 | |
| elif p["stage"] == 2: | |
| p["stage"] = 3 | |
| harvest += 1 | |
| elif p["stage"] == 1 and water > 0: | |
| p["moisture"] = minf(100, p["moisture"] + 55) | |
| water -= 1 | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| for p in plants: | |
| if p["stage"] == 1: | |
| p["moisture"] -= 8 * dt | |
| p["growth"] += 6 * dt | |
| if p["moisture"] <= 0: | |
| p["stage"] = 4 | |
| wilted += 1 | |
| elif p["growth"] >= 100: | |
| p["stage"] = 2 | |
| if harvest == 6: | |
| outcome = "All six crops harvested! R: replant" | |
| elif wilted > 0: | |
| outcome = "A plant wilted. R: restart the farm" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("SIX PLOT FARM", "Click empty plot: plant | Click growing crop: spend water | Click gold crop: harvest | R: restart") | |
| for i in range(6): | |
| var p: Dictionary = plants[i] | |
| var r: Rect2 = plot(i) | |
| draw_rect(r, Color("684b38")) | |
| if p["stage"] == 1 or p["stage"] == 2: | |
| draw_circle(r.get_center() - Vector2(0, 14), 15 + p["growth"] * 0.2, Color("ffcd6b") if p["stage"] == 2 else Color("5ed27b")) | |
| draw_rect(Rect2(r.position + Vector2(10, 115), Vector2(p["moisture"] * 1.65, 12)), Color("79d7ff")) | |
| text_at("Growth %d%%" % int(p["growth"]), r.position + Vector2(22, 149), 17) | |
| else: | |
| text_at(["PLANT", "", "", "HARVESTED", "WILTED"][p["stage"]], r.position + Vector2(23, 80), 20) | |
| text_at("Water %d / 12 Harvest %d / 6" % [water, harvest], Vector2(300, 570)) | |
| text_at(outcome, Vector2(250, 620), 24) | |
| ''') | |
| GAMES[67] = ("Three Barrier Bankshot", '''extends Node2D | |
| var barriers: Array[Rect2] = [] | |
| var ball: Vector2 = Vector2(125, 490) | |
| var velocity: Vector2 = Vector2(210, -150) | |
| var running: bool = false | |
| var bounces: int = 0 | |
| var horizontal: bool = true | |
| var outcome: String = "" | |
| var timer: float = 0 | |
| func reset() -> void: | |
| barriers.clear() | |
| ball = Vector2(125, 490) | |
| velocity = Vector2(210, -150) | |
| running = false | |
| bounces = 0 | |
| timer = 0 | |
| outcome = "" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and not e.echo: | |
| if e.keycode == KEY_R: | |
| reset() | |
| elif e.keycode == KEY_SPACE and barriers.size() == 3 and outcome == "": | |
| running = true | |
| if e is InputEventMouseButton and e.pressed and not running: | |
| if e.button_index == MOUSE_BUTTON_RIGHT: | |
| horizontal = not horizontal | |
| elif e.button_index == MOUSE_BUTTON_LEFT and barriers.size() < 3 and e.position.y > 160: | |
| var size: Vector2 = Vector2(120, 18) if horizontal else Vector2(18, 120) | |
| var rect: Rect2 = Rect2(e.position - size / 2, size) | |
| if not rect.grow(25).has_point(ball) and not rect.grow(35).has_point(Vector2(785, 425)): | |
| barriers.append(rect) | |
| func _process(dt: float) -> void: | |
| if running and outcome == "": | |
| timer += dt | |
| var previous: Vector2 = ball | |
| ball += velocity * dt | |
| if ball.x < 55 or ball.x > 905: | |
| velocity.x *= -1 | |
| ball.x = clampf(ball.x, 55, 905) | |
| bounces += 1 | |
| if ball.y < 155 or ball.y > 565: | |
| velocity.y *= -1 | |
| ball.y = clampf(ball.y, 155, 565) | |
| bounces += 1 | |
| for r in barriers: | |
| if r.grow(9).has_point(ball) and not r.grow(9).has_point(previous): | |
| if previous.x < r.position.x - 9 or previous.x > r.end.x + 9: | |
| velocity.x *= -1 | |
| else: | |
| velocity.y *= -1 | |
| ball = previous | |
| bounces += 1 | |
| if ball.distance_to(Vector2(785, 425)) < 30: | |
| outcome = "Target reached in %d bounces! R: rebuild" % bounces | |
| elif bounces > 5 or timer > 25: | |
| outcome = "Route failed. R: place a new layout" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("THREE BARRIER BANKSHOT", "Left click: place | Right click: rotate next barrier | Place three then Space: launch | R: restart") | |
| draw_rect(Rect2(45, 145, 870, 430), Color("46617d"), false, 3) | |
| draw_circle(Vector2(785, 425), 30, Color("287d60")) | |
| for r in barriers: | |
| draw_rect(r, Color("ffcd6b")) | |
| draw_circle(ball, 9, Color.WHITE) | |
| if not running: | |
| draw_line(ball, ball + velocity.normalized() * 75, Color("79d7ff"), 3) | |
| text_at("Barriers %d / 3 Bounces %d / 5 Next: %s" % [barriers.size(), bounces, "horizontal" if horizontal else "vertical"], Vector2(110, 122), 20) | |
| text_at(outcome, Vector2(215, 618), 24) | |
| ''') | |
| GAMES[68] = ("Merge To 128", '''extends Node2D | |
| var board: Array[int] = [] | |
| var score: int = 0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| reset() | |
| func reset() -> void: | |
| board.clear() | |
| board.resize(16) | |
| board.fill(0) | |
| score = 0 | |
| outcome = "" | |
| spawn() | |
| spawn() | |
| func spawn() -> void: | |
| var empty: Array[int] = [] | |
| for i in range(16): | |
| if board[i] == 0: | |
| empty.append(i) | |
| if not empty.is_empty(): | |
| board[empty[randi_range(0, empty.size() - 1)]] = 2 | |
| func slide(direction: int) -> void: | |
| if outcome != "": | |
| return | |
| var before: Array[int] = board.duplicate() | |
| for line in range(4): | |
| var ids: Array[int] = [] | |
| for n in range(4): | |
| var index: int = line * 4 + n | |
| if direction == 1: | |
| index = line * 4 + 3 - n | |
| elif direction == 2: | |
| index = n * 4 + line | |
| elif direction == 3: | |
| index = (3 - n) * 4 + line | |
| ids.append(index) | |
| var values: Array[int] = [] | |
| for index in ids: | |
| if board[index] > 0: | |
| values.append(board[index]) | |
| var merged: Array[int] = [] | |
| var n: int = 0 | |
| while n < values.size(): | |
| if n + 1 < values.size() and values[n] == values[n + 1]: | |
| merged.append(values[n] * 2) | |
| score += values[n] * 2 | |
| n += 2 | |
| else: | |
| merged.append(values[n]) | |
| n += 1 | |
| for i in range(4): | |
| board[ids[i]] = merged[i] if i < merged.size() else 0 | |
| if board != before: | |
| spawn() | |
| var possible: bool = board.has(0) | |
| for y in range(4): | |
| for x in range(4): | |
| var i: int = y * 4 + x | |
| if board[i] >= 128: | |
| outcome = "128 reached! R: a new board" | |
| if x < 3 and board[i] == board[i + 1]: | |
| possible = true | |
| if y < 3 and board[i] == board[i + 4]: | |
| possible = true | |
| if not possible and outcome == "": | |
| outcome = "No legal moves. R: retry" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and not e.echo: | |
| if e.keycode == KEY_R: | |
| reset() | |
| elif e.keycode == KEY_LEFT: | |
| slide(0) | |
| elif e.keycode == KEY_RIGHT: | |
| slide(1) | |
| elif e.keycode == KEY_UP: | |
| slide(2) | |
| elif e.keycode == KEY_DOWN: | |
| slide(3) | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("MERGE TO 128", "Arrow keys: slide | Equal neighbors merge once per move | Reach 128 | R: restart") | |
| for i in range(16): | |
| var p: Vector2 = Vector2(270 + (i % 4) * 108, 120 + (i / 4) * 108) | |
| draw_rect(Rect2(p, Vector2(100, 100)), Color("273b55") if board[i] == 0 else Color.from_hsv(log(float(board[i])) * 0.06, 0.55, 0.9)) | |
| if board[i] > 0: | |
| text_at(str(board[i]), p + Vector2(30, 61), 29) | |
| text_at("Score %d" % score, Vector2(95, 160)) | |
| text_at(outcome, Vector2(280, 606), 25) | |
| ''') | |
| GAMES[69] = ("Fragile Freight", '''extends Node2D | |
| var cart: float = 110 | |
| var speed: float = 0 | |
| var cargo: float = 0 | |
| var cargo_speed: float = 0 | |
| var timer: float = 25 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| cart = 110 | |
| speed = 0 | |
| cargo = 0 | |
| cargo_speed = 0 | |
| timer = 25 | |
| outcome = "" | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| timer -= dt | |
| var acceleration: float = Input.get_axis("ui_left", "ui_right") * 95 - speed * 0.15 | |
| speed = clampf(speed + acceleration * dt, -150, 150) | |
| cart += speed * dt | |
| cargo_speed += (-acceleration * 0.25 - cargo_speed * 0.85 - cargo * 0.2) * dt | |
| cargo += cargo_speed * dt | |
| if absf(cargo) > 65 or cart < 70: | |
| outcome = "Cargo fell or cart left the road! R: retry" | |
| elif cart >= 830 and absf(speed) < 28: | |
| outcome = "Cargo delivered intact! R: drive again" | |
| elif cart > 920 or timer <= 0: | |
| outcome = "Missed the stopping zone! R: retry" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("FRAGILE FREIGHT", "Left / Right: accelerate or brake | Carry the loose ball into green zone and stop | R: restart") | |
| draw_rect(Rect2(800, 370, 120, 150), Color("214f41")) | |
| draw_line(Vector2(50, 500), Vector2(930, 500), Color("c1cfe8"), 5) | |
| draw_rect(Rect2(cart - 70, 450, 140, 20), Color("79bbff")) | |
| draw_circle(Vector2(cart - 45, 483), 16, Color("62758f")) | |
| draw_circle(Vector2(cart + 45, 483), 16, Color("62758f")) | |
| draw_circle(Vector2(cart + cargo, 432), 17, Color("ffcd6b")) | |
| text_at("Speed %.0f Cargo offset %.0f Time %.1f" % [speed, cargo, maxf(timer, 0)], Vector2(240, 170)) | |
| text_at("DESTINATION", Vector2(795, 355), 18) | |
| text_at(outcome, Vector2(240, 590), 25) | |
| ''') | |
| GAMES[70] = ("One Thing Changed", '''extends Node2D | |
| var objects: Array[Vector2] = [] | |
| var changed: int = 0 | |
| var stage: int = 0 | |
| var clock: float = 0 | |
| var solved: int = 0 | |
| var mistakes: int = 0 | |
| var outcome: String = "" | |
| func _ready() -> void: | |
| new_scene() | |
| func new_scene() -> void: | |
| objects.clear() | |
| for i in range(6): | |
| objects.append(Vector2(235 + (i % 3) * 230, 230 + (i / 3) * 210)) | |
| changed = randi_range(0, 5) | |
| stage = 0 | |
| clock = 0 | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and e.keycode == KEY_R: | |
| solved = 0 | |
| mistakes = 0 | |
| outcome = "" | |
| new_scene() | |
| if e is InputEventMouseButton and e.pressed and e.button_index == MOUSE_BUTTON_LEFT and stage == 2 and outcome == "": | |
| if e.position.distance_to(objects[changed]) < 40: | |
| solved += 1 | |
| else: | |
| mistakes += 1 | |
| if solved == 5: | |
| outcome = "Five changes spotted! R: play again" | |
| elif mistakes == 3: | |
| outcome = "Three misses. R: look more closely" | |
| else: | |
| new_scene() | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| clock += dt | |
| if stage == 0 and clock > 2.7: | |
| stage = 1 | |
| clock = 0 | |
| elif stage == 1 and clock > 0.7: | |
| objects[changed].x += 65 if changed % 2 == 0 else -65 | |
| stage = 2 | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("ONE THING CHANGED", "Memorize the scene | After the brief curtain, click the object that moved | R: restart") | |
| text_at(["MEMORIZE", "CURTAIN", "WHICH OBJECT MOVED?"][stage], Vector2(330, 125), 25) | |
| if stage != 1: | |
| for i in range(objects.size()): | |
| var color: Color = Color.from_hsv(i * 0.15, 0.65, 0.95) | |
| if i % 2 == 0: | |
| draw_circle(objects[i], 33, color) | |
| else: | |
| draw_rect(Rect2(objects[i] - Vector2.ONE * 31, Vector2.ONE * 62), color) | |
| text_at(str(i + 1), objects[i] + Vector2(-7, 8), 25) | |
| else: | |
| draw_rect(Rect2(140, 160, 670, 360), Color("27344c")) | |
| text_at("Found %d / 5 Misses %d / 3" % [solved, mistakes], Vector2(300, 565)) | |
| text_at(outcome, Vector2(280, 615), 25) | |
| ''') | |
| GAMES[71] = ("Gentle Tension", '''extends Node2D | |
| var angle: float = 0 | |
| var sweet: float = 0.45 | |
| var tension: float = 0 | |
| var opened: float = 0 | |
| var picks: int = 3 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| angle = 0 | |
| sweet = randf_range(-1.0, 1.0) | |
| tension = 0 | |
| opened = 0 | |
| picks = 3 | |
| outcome = "" | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| angle = clampf(angle + Input.get_axis("ui_left", "ui_right") * dt, -1.2, 1.2) | |
| if Input.is_action_pressed("ui_accept"): | |
| tension = minf(1, tension + dt * 0.75) | |
| if absf(angle - sweet) < 0.13: | |
| opened += dt * tension * 0.6 | |
| elif tension > 0.62: | |
| picks -= 1 | |
| tension = 0 | |
| opened = 0 | |
| else: | |
| tension = maxf(0, tension - dt * 2.5) | |
| if opened >= 1: | |
| outcome = "Lock opened without force! R: another lock" | |
| elif picks <= 0: | |
| outcome = "All three picks broke. R: retry gently" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("GENTLE TENSION", "Left / Right: rotate pick | Hold Space gently to test and turn | Release to ease tension | R: restart") | |
| draw_circle(Vector2(480, 345), 150, Color("52617a")) | |
| draw_circle(Vector2(480, 345), 105, Color("24344c")) | |
| var tip: Vector2 = Vector2(0, -160).rotated(angle) | |
| draw_line(Vector2(480, 345), Vector2(480, 345) + tip, Color("ffcd6b"), 8) | |
| draw_line(Vector2(480, 345), Vector2(480, 345) + Vector2(70, 0).rotated(opened * PI * 0.5), Color.WHITE, 12) | |
| var near: bool = absf(angle - sweet) < 0.13 | |
| text_at("The cylinder yields!" if near and tension > 0 else "Probe lightly; resistance means the angle is wrong.", Vector2(235, 145), 21) | |
| draw_rect(Rect2(300, 530, tension * 360, 20), Color("ff6176") if tension > 0.6 and not near else Color("5ef2d0")) | |
| text_at("Tension Picks %d Open %d%%" % [picks, int(opened * 100)], Vector2(290, 585)) | |
| text_at(outcome, Vector2(250, 625), 23) | |
| ''') | |
| GAMES[72] = ("Paper Airflow", '''extends Node2D | |
| var fans: Array[Dictionary] = [] | |
| var plane: Vector2 = Vector2(125, 440) | |
| var velocity: Vector2 = Vector2(65, 0) | |
| var facing: int = 3 | |
| var running: bool = false | |
| var timer: float = 0 | |
| var outcome: String = "" | |
| var directions: Array[Vector2] = [Vector2.RIGHT, Vector2.DOWN, Vector2.LEFT, Vector2.UP] | |
| func reset() -> void: | |
| fans.clear() | |
| plane = Vector2(125, 440) | |
| velocity = Vector2(65, 0) | |
| running = false | |
| timer = 0 | |
| outcome = "" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and not e.echo: | |
| if e.keycode == KEY_R: | |
| reset() | |
| elif e.keycode == KEY_SPACE and fans.size() == 2: | |
| running = true | |
| if e is InputEventMouseButton and e.pressed and not running: | |
| if e.button_index == MOUSE_BUTTON_RIGHT: | |
| facing = (facing + 1) % 4 | |
| elif e.button_index == MOUSE_BUTTON_LEFT and fans.size() < 2 and e.position.y > 150: | |
| fans.append({"p": e.position, "direction": facing}) | |
| func _process(dt: float) -> void: | |
| if running and outcome == "": | |
| timer += dt | |
| for fan in fans: | |
| if plane.distance_to(fan["p"]) < 125: | |
| velocity = velocity.move_toward(directions[fan["direction"]] * 95, 165 * dt) | |
| plane += velocity * dt | |
| if plane.distance_to(Vector2(790, 225)) < 35: | |
| outcome = "Mail delivered by airflow! R: redesign" | |
| elif plane.x < 30 or plane.x > 930 or plane.y < 125 or plane.y > 590 or timer > 18: | |
| outcome = "Paper missed the mailbox. R: redesign fans" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("PAPER AIRFLOW", "Left click: place fan | Right click: rotate next fan | Place TWO then Space: launch | R: restart") | |
| for fan in fans: | |
| draw_arc(fan["p"], 125, 0, TAU, 48, Color("345068"), 2) | |
| draw_circle(fan["p"], 20, Color("5ef2d0")) | |
| draw_line(fan["p"], fan["p"] + directions[fan["direction"]] * 48, Color.WHITE, 5) | |
| draw_rect(Rect2(760, 195, 60, 60), Color("ffcd6b")) | |
| text_at("MAIL", Vector2(761, 185), 20) | |
| draw_colored_polygon(PackedVector2Array([plane + Vector2(16, 0), plane + Vector2(-12, -9), plane + Vector2(-6, 0), plane + Vector2(-12, 9)]), Color.WHITE) | |
| text_at("Fans %d / 2 Next direction: %s" % [fans.size(), ["RIGHT", "DOWN", "LEFT", "UP"][facing]], Vector2(270, 125), 20) | |
| text_at(outcome, Vector2(230, 625), 25) | |
| ''') | |
| GAMES[73] = ("Marble Bend", '''extends Node2D | |
| var chain: Array[int] = [0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2] | |
| var progress: float = 0 | |
| var next_color: int = 0 | |
| var shot: Vector2 = Vector2.ZERO | |
| var shot_velocity: Vector2 = Vector2.ZERO | |
| var active: bool = false | |
| var removed: int = 0 | |
| var outcome: String = "" | |
| var colors: Array[Color] = [Color("ff786e"), Color("79d7ff"), Color("ffcd6b")] | |
| func reset() -> void: | |
| chain = [0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2] | |
| progress = 0 | |
| next_color = 0 | |
| active = false | |
| removed = 0 | |
| outcome = "" | |
| func path(t: float) -> Vector2: | |
| return Vector2(150 + t, 255 + sin(t / 130.0) * 70) | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and e.keycode == KEY_R: | |
| reset() | |
| if e is InputEventMouseButton and e.pressed and e.button_index == MOUSE_BUTTON_LEFT and not active and outcome == "": | |
| shot = Vector2(480, 540) | |
| shot_velocity = (e.position - shot).normalized() * 450 | |
| active = true | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| progress += 7 * dt | |
| if active: | |
| shot += shot_velocity * dt | |
| for i in range(chain.size()): | |
| if shot.distance_to(path(progress + i * 31)) < 28: | |
| chain.insert(i, next_color) | |
| var left: int = i | |
| var right: int = i | |
| while left > 0 and chain[left - 1] == next_color: | |
| left -= 1 | |
| while right + 1 < chain.size() and chain[right + 1] == next_color: | |
| right += 1 | |
| if right - left + 1 >= 3: | |
| removed += right - left + 1 | |
| for j in range(right - left + 1): | |
| chain.remove_at(left) | |
| next_color = (next_color + 1) % 3 | |
| active = false | |
| break | |
| if shot.y < 95 or shot.x < 10 or shot.x > 950: | |
| active = false | |
| if chain.is_empty(): | |
| outcome = "Chain cleared! R: replay" | |
| elif progress + (chain.size() - 1) * 31 > 680: | |
| outcome = "Marbles reached the drain. R: retry" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("MARBLE BEND", "Click: launch the displayed marble toward the chain | Insert to make runs of three | R: restart") | |
| for t in range(0, 680, 10): | |
| draw_line(path(t), path(t + 10), Color("3c506a"), 4) | |
| draw_circle(path(680), 26, Color("9e4259")) | |
| for i in range(chain.size()): | |
| draw_circle(path(progress + i * 31), 14, colors[chain[i]]) | |
| draw_circle(Vector2(480, 540), 23, colors[next_color]) | |
| if active: | |
| draw_circle(shot, 13, colors[next_color]) | |
| text_at("Removed %d Remaining %d" % [removed, chain.size()], Vector2(290, 125)) | |
| text_at(outcome, Vector2(310, 610), 25) | |
| ''') | |
| GAMES[74] = ("Moon Window", '''extends Node2D | |
| var angle: float = 0.0 | |
| var target: float = 4.0 | |
| var width: float = 0.8 | |
| var wins: int = 0 | |
| var misses: int = 0 | |
| var pause: float = 0 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| angle = 0 | |
| target = 4 | |
| width = 0.8 | |
| wins = 0 | |
| misses = 0 | |
| pause = 0 | |
| outcome = "" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and not e.echo: | |
| if e.keycode == KEY_R: | |
| reset() | |
| elif e.keycode == KEY_SPACE and outcome == "" and pause <= 0: | |
| var distance: float = absf(wrapf(angle - target, -PI, PI)) | |
| if distance < (width + 0.28) * 0.5: | |
| wins += 1 | |
| width *= 0.77 | |
| target = fposmod(target + 2.1, TAU) | |
| else: | |
| misses += 1 | |
| pause = 0.45 | |
| if wins == 6: | |
| outcome = "Six lunar alignments! R: replay" | |
| elif misses == 3: | |
| outcome = "Three missed windows. R: retry" | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| pause -= dt | |
| if pause <= 0: | |
| angle = fposmod(angle + (1.5 + wins * 0.22) * dt, TAU) | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("MOON WINDOW", "Space: stop the moving lit sector while it overlaps the fixed gold sector | R: restart") | |
| draw_circle(Vector2(480, 340), 156, Color("22334d")) | |
| draw_arc(Vector2(480, 340), 150, target - width / 2, target + width / 2, 40, Color("ffcd6b"), 24) | |
| draw_arc(Vector2(480, 340), 150, angle - 0.14, angle + 0.14, 20, Color("79d7ff"), 18) | |
| text_at("ALIGN", Vector2(431, 350), 26) | |
| text_at("Aligned %d / 6 Missed %d / 3" % [wins, misses], Vector2(300, 545)) | |
| text_at(outcome, Vector2(280, 610), 25) | |
| ''') | |
| GAMES[75] = ("Torch Return", '''extends Node2D | |
| var player: Vector2i = Vector2i(0, 4) | |
| var treasures: Array[Vector2i] = [Vector2i(3, 3), Vector2i(7, 6), Vector2i(10, 4)] | |
| var torch: float = 90 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| player = Vector2i(0, 4) | |
| treasures = [Vector2i(3, 3), Vector2i(7, 6), Vector2i(10, 4)] | |
| torch = 90 | |
| outcome = "" | |
| func wall(p: Vector2i) -> bool: | |
| return p.x < 0 or p.y < 0 or p.x >= 12 or p.y >= 9 or (p.x % 3 == 2 and p.y != 1 and p.y != 7) | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed: | |
| if e.keycode == KEY_R: | |
| reset() | |
| var direction: Vector2i = Vector2i.ZERO | |
| if e.keycode == KEY_LEFT: | |
| direction = Vector2i.LEFT | |
| elif e.keycode == KEY_RIGHT: | |
| direction = Vector2i.RIGHT | |
| elif e.keycode == KEY_UP: | |
| direction = Vector2i.UP | |
| elif e.keycode == KEY_DOWN: | |
| direction = Vector2i.DOWN | |
| if outcome == "" and not wall(player + direction): | |
| player += direction | |
| treasures.erase(player) | |
| if treasures.is_empty() and player == Vector2i(0, 4): | |
| outcome = "Three treasures brought home! R: explore again" | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| torch -= dt | |
| if torch <= 0: | |
| outcome = "Torch exhausted. R: try a shorter route" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("TORCH RETURN", "Arrows: explore the dark grid | Collect three treasures, then return to entrance | R: restart") | |
| for y in range(9): | |
| for x in range(12): | |
| var p: Vector2i = Vector2i(x, y) | |
| var r: Rect2 = Rect2(180 + x * 48, 135 + y * 48, 46, 46) | |
| if Vector2(p - player).length() <= 2.5: | |
| draw_rect(r, Color("52617a") if wall(p) else Color("293e55")) | |
| if treasures.has(p): | |
| draw_circle(r.get_center(), 12, Color("ffcd6b")) | |
| if p == Vector2i(0, 4): | |
| draw_rect(r.grow(-8), Color("5ef2d0"), false, 3) | |
| else: | |
| draw_rect(r, Color("0a101b")) | |
| draw_circle(Vector2(203, 158) + Vector2(player) * 48, 12, Color.WHITE) | |
| text_at("Treasures %d / 3 Torch %.0f" % [3 - treasures.size(), maxf(torch, 0)], Vector2(300, 605)) | |
| text_at(outcome, Vector2(180, 115), 21) | |
| ''') | |
| GAMES[76] = ("Four Piston Passage", '''extends Node2D | |
| var ball: Vector2 = Vector2(180, 480) | |
| var velocity: Vector2 = Vector2.ZERO | |
| var pressure: Array[float] = [0.0, 0.0, 0.0, 0.0] | |
| var corridor: Array[Rect2] = [Rect2(130, 415, 285, 130), Rect2(295, 235, 120, 310), Rect2(295, 235, 365, 120), Rect2(540, 125, 120, 230), Rect2(540, 125, 285, 110)] | |
| var hits: int = 0 | |
| var cooldown: float = 0 | |
| var outcome: String = "" | |
| var directions: Array[Vector2] = [Vector2.LEFT, Vector2.RIGHT, Vector2.UP, Vector2.DOWN] | |
| var actions: Array[String] = ["ui_left", "ui_right", "ui_up", "ui_down"] | |
| func reset() -> void: | |
| ball = Vector2(180, 480) | |
| velocity = Vector2.ZERO | |
| pressure = [0.0, 0.0, 0.0, 0.0] | |
| hits = 0 | |
| cooldown = 0 | |
| outcome = "" | |
| func safe(p: Vector2) -> bool: | |
| for offset in [Vector2(-12, 0), Vector2(12, 0), Vector2(0, -12), Vector2(0, 12)]: | |
| var ok: bool = false | |
| for r in corridor: | |
| if r.has_point(p + offset): | |
| ok = true | |
| if not ok: | |
| return false | |
| return true | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| cooldown -= dt | |
| for i in range(4): | |
| pressure[i] = move_toward(pressure[i], 1.0 if Input.is_action_pressed(actions[i]) else 0.0, dt * 3) | |
| velocity += directions[i] * pressure[i] * 250 * dt | |
| velocity *= exp(-3 * dt) | |
| var next: Vector2 = ball + velocity * dt | |
| if safe(next): | |
| ball = next | |
| else: | |
| velocity *= -0.3 | |
| if cooldown <= 0: | |
| hits += 1 | |
| cooldown = 0.7 | |
| if ball.distance_to(Vector2(785, 180)) < 28: | |
| outcome = "Ball delivered through all bends! R: replay" | |
| elif hits >= 8: | |
| outcome = "Eight pressure collisions. R: use gentler pulses" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("FOUR PISTON PASSAGE", "Hold arrows: independent directional pistons build pressure | Guide ball through the zigzag | R: restart") | |
| for r in corridor: | |
| draw_rect(r, Color("263e57")) | |
| draw_circle(Vector2(785, 180), 28, Color("25855f")) | |
| for i in range(4): | |
| var origin: Vector2 = ball - directions[i] * (52 - pressure[i] * 20) | |
| draw_line(origin - directions[i] * 22, origin, Color("ffcd6b"), 14) | |
| draw_circle(ball, 12, Color.WHITE) | |
| text_at("Wall strikes %d / 8" % hits, Vector2(60, 585)) | |
| text_at(outcome, Vector2(260, 625), 23) | |
| ''') | |
| GAMES[77] = ("One Then Two", '''extends Node2D | |
| var ball: Vector2 = Vector2(210, 430) | |
| var velocity: Vector2 = Vector2.ZERO | |
| var targets: Array[Vector2] = [Vector2(460, 300), Vector2(690, 330)] | |
| var pockets: Array[Vector2] = [Vector2(110, 150), Vector2(850, 150), Vector2(110, 550), Vector2(850, 550)] | |
| var order: int = 0 | |
| var dragging: bool = false | |
| var drag_start: Vector2 = Vector2.ZERO | |
| var shooting: bool = false | |
| var shots: int = 0 | |
| var note: String = "Hit 1, then 2, then any corner pocket." | |
| var outcome: String = "" | |
| var contact: int = -1 | |
| func reset_ball() -> void: | |
| ball = Vector2(210, 430) | |
| velocity = Vector2.ZERO | |
| order = 0 | |
| shooting = false | |
| contact = -1 | |
| func fail_shot(reason: String) -> void: | |
| note = reason + " Balls reset." | |
| reset_ball() | |
| if shots >= 5: | |
| outcome = "Five shots exhausted. R: retry" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and e.keycode == KEY_R: | |
| shots = 0 | |
| outcome = "" | |
| reset_ball() | |
| if e is InputEventMouseButton and e.button_index == MOUSE_BUTTON_LEFT and outcome == "" and not shooting: | |
| if e.pressed and e.position.distance_to(ball) < 30: | |
| dragging = true | |
| drag_start = e.position | |
| elif not e.pressed and dragging: | |
| dragging = false | |
| velocity = (drag_start - e.position).limit_length(220) * 3 | |
| if velocity.length() > 25: | |
| shooting = true | |
| shots += 1 | |
| func _process(dt: float) -> void: | |
| if shooting and outcome == "": | |
| ball += velocity * dt | |
| velocity *= exp(-0.16 * dt) | |
| if ball.x < 120 or ball.x > 840: | |
| velocity.x *= -1 | |
| ball.x = clampf(ball.x, 120, 840) | |
| if ball.y < 160 or ball.y > 540: | |
| velocity.y *= -1 | |
| ball.y = clampf(ball.y, 160, 540) | |
| var touching: int = -1 | |
| for i in range(2): | |
| if ball.distance_to(targets[i]) < 28: | |
| touching = i | |
| if contact != i: | |
| if i != order: | |
| fail_shot("Wrong ball order!") | |
| break | |
| order += 1 | |
| var normal: Vector2 = (ball - targets[i]).normalized() | |
| velocity = velocity.bounce(normal) | |
| ball = targets[i] + normal * 29 | |
| contact = touching | |
| for pocket in pockets: | |
| if ball.distance_to(pocket) < 30: | |
| if order == 2: | |
| outcome = "Correct order and pocket! R: replay" | |
| else: | |
| fail_shot("Pocketed before both contacts!") | |
| if shooting and velocity.length() < 20: | |
| fail_shot("Shot stopped short.") | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("ONE THEN TWO", "Drag backward from cue ball, release to shoot | Hit numbered balls in order, then a pocket | R: restart") | |
| draw_rect(Rect2(100, 140, 760, 420), Color("205945")) | |
| for p in pockets: | |
| draw_circle(p, 27, Color("101b26")) | |
| for i in range(2): | |
| draw_circle(targets[i], 16, Color("ffcd6b") if i == 0 else Color("ff786e")) | |
| text_at(str(i + 1), targets[i] + Vector2(-6, 7), 20) | |
| draw_circle(ball, 12, Color.WHITE) | |
| if dragging: | |
| draw_line(ball, ball + (drag_start - get_global_mouse_position()).limit_length(220), Color("79d7ff"), 3) | |
| text_at("Contacts %d / 2 Shots %d / 5" % [order, shots], Vector2(300, 120), 20) | |
| text_at(note if outcome == "" else outcome, Vector2(210, 612), 23) | |
| ''') | |
| GAMES[78] = ("Falling Symbols", '''extends Node2D | |
| var symbols: Array[String] = ["+", "X", "O", "#"] | |
| var target: Array[int] = [0, 2, 1, 3] | |
| var rows: Array[Dictionary] = [] | |
| var next: int = 0 | |
| var completed: int = 0 | |
| var misses: int = 0 | |
| var spawn: float = 0 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| rows.clear() | |
| next = 0 | |
| completed = 0 | |
| misses = 0 | |
| spawn = 0 | |
| outcome = "" | |
| func cell(row: Dictionary, column: int) -> Rect2: | |
| return Rect2(270 + column * 110, row["y"], 96, 46) | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and e.keycode == KEY_R: | |
| reset() | |
| if e is InputEventMouseButton and e.pressed and e.button_index == MOUSE_BUTTON_LEFT and outcome == "": | |
| for row in rows: | |
| for col in range(4): | |
| if cell(row, col).has_point(e.position) and not row["used"].has(col): | |
| if row["values"][col] == target[next]: | |
| row["used"].append(col) | |
| next += 1 | |
| if next == 4: | |
| completed += 1 | |
| next = 0 | |
| else: | |
| misses += 1 | |
| return | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| spawn -= dt | |
| if spawn <= 0: | |
| var values: Array[int] = [0, 1, 2, 3] | |
| values.shuffle() | |
| rows.append({"y": 175.0, "values": values, "used": []}) | |
| spawn = 3 | |
| for i in range(rows.size() - 1, -1, -1): | |
| rows[i]["y"] += 40 * dt | |
| if rows[i]["y"] > 550: | |
| if rows[i]["used"].size() < 4: | |
| misses += 1 | |
| rows.remove_at(i) | |
| if completed >= 6: | |
| outcome = "Six symbol sequences completed! R: replay" | |
| elif misses >= 5: | |
| outcome = "Five mistakes or escaped rows. R: retry" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("FALLING SYMBOLS", "Read target left to right | Click the next required symbol in any falling row | R: restart") | |
| for i in range(4): | |
| draw_rect(Rect2(300 + i * 95, 100, 70, 50), Color("99763e") if i == next else Color("334966")) | |
| text_at(symbols[target[i]], Vector2(322 + i * 95, 136), 30) | |
| for row in rows: | |
| for col in range(4): | |
| draw_rect(cell(row, col), Color("203343") if row["used"].has(col) else Color("476587")) | |
| if not row["used"].has(col): | |
| text_at(symbols[row["values"][col]], cell(row, col).position + Vector2(37, 32), 27) | |
| draw_line(Vector2(200, 597), Vector2(770, 597), Color("ff6176"), 3) | |
| text_at("Sequences %d / 6 Misses %d / 5" % [completed, misses], Vector2(280, 625), 20) | |
| text_at(outcome, Vector2(235, 575), 22) | |
| ''') | |
| GAMES[79] = ("Corner Runner", '''extends Node2D | |
| var distance: float = 0 | |
| var jump: float = 0 | |
| var tokens: Array[bool] = [false, false, false, false] | |
| var outcome: String = "" | |
| var obstacles: Array[float] = [220.0, 760.0, 1150.0, 1660.0] | |
| func reset() -> void: | |
| distance = 0 | |
| jump = 0 | |
| tokens = [false, false, false, false] | |
| outcome = "" | |
| func point(t: float) -> Vector2: | |
| t = fposmod(t, 1840) | |
| if t < 600: | |
| return Vector2(180 + t, 190) | |
| if t < 920: | |
| return Vector2(780, 190 + t - 600) | |
| if t < 1520: | |
| return Vector2(780 - (t - 920), 510) | |
| return Vector2(180, 510 - (t - 1520)) | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and not e.echo: | |
| if e.keycode == KEY_R: | |
| reset() | |
| elif e.keycode == KEY_SPACE and jump <= 0: | |
| jump = 0.65 | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| distance += 175 * dt | |
| jump = maxf(0, jump - dt) | |
| var checkpoints: Array[float] = [0.0, 600.0, 920.0, 1520.0] | |
| for i in range(4): | |
| if absf(fposmod(distance - checkpoints[i] + 920, 1840) - 920) < 18: | |
| tokens[i] = true | |
| for obstacle in obstacles: | |
| if absf(fposmod(distance - obstacle + 920, 1840) - 920) < 22 and jump < 0.1: | |
| outcome = "Hit an obstacle! R: jump a little earlier" | |
| if distance >= 1840 and not tokens.has(false): | |
| outcome = "Four corner tokens and a clean lap! R: replay" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("CORNER RUNNER", "Automatic perimeter run | Space: jump inward over red obstacles | Collect four corner tokens | R: restart") | |
| draw_rect(Rect2(180, 190, 600, 320), Color("52617a"), false, 7) | |
| for t in obstacles: | |
| draw_rect(Rect2(point(t) - Vector2.ONE * 15, Vector2.ONE * 30), Color("ff6176")) | |
| var corners: Array[float] = [0.0, 600.0, 920.0, 1520.0] | |
| for i in range(4): | |
| if not tokens[i]: | |
| draw_circle(point(corners[i]), 14, Color("ffcd6b")) | |
| var p: Vector2 = point(distance) | |
| if jump > 0: | |
| p += (Vector2(480, 350) - p).normalized() * sin(jump / 0.65 * PI) * 75 | |
| draw_rect(Rect2(p - Vector2.ONE * 11, Vector2.ONE * 22), Color("5ef2d0")) | |
| text_at("Lap %d%% Tokens %d / 4" % [int(minf(distance / 1840, 1) * 100), tokens.count(true)], Vector2(315, 140)) | |
| text_at(outcome, Vector2(220, 600), 24) | |
| ''') | |
| GAMES[80] = ("Drifting Radio", '''extends Node2D | |
| var settings: Array[float] = [0.25, 0.25] | |
| var target: Array[float] = [0.5, 0.65] | |
| var selected: int = -1 | |
| var clock: float = 0 | |
| var stable: float = 0 | |
| var outcome: String = "" | |
| func reset() -> void: | |
| settings = [0.25, 0.25] | |
| clock = 0 | |
| stable = 0 | |
| outcome = "" | |
| func slider(i: int) -> Rect2: | |
| return Rect2(180, 430 + i * 90, 600, 35) | |
| func adjust(p: Vector2) -> void: | |
| if selected >= 0: | |
| settings[selected] = clampf((p.x - 180) / 600, 0, 1) | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and e.keycode == KEY_R: | |
| reset() | |
| if e is InputEventMouseButton and e.button_index == MOUSE_BUTTON_LEFT: | |
| if e.pressed: | |
| for i in range(2): | |
| if slider(i).grow(14).has_point(e.position): | |
| selected = i | |
| adjust(e.position) | |
| else: | |
| selected = -1 | |
| if e is InputEventMouseMotion: | |
| adjust(e.position) | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| clock += dt | |
| target = [0.5 + sin(clock * 0.3) * 0.18, 0.5 + cos(clock * 0.19) * 0.15] | |
| var error: float = absf(settings[0] - target[0]) + absf(settings[1] - target[1]) | |
| stable = minf(8, stable + dt) if error < 0.09 else maxf(0, stable - dt * 2) | |
| if stable >= 8: | |
| outcome = "Signal locked for eight seconds! R: retune" | |
| elif clock > 90: | |
| outcome = "Transmission ended. R: retry" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("DRIFTING RADIO", "Drag FREQUENCY and PHASE sliders | Steady the waveform and track its drift for eight seconds | R: restart") | |
| var error: float = absf(settings[0] - target[0]) + absf(settings[1] - target[1]) | |
| var previous: Vector2 = Vector2(160, 280) | |
| for i in range(101): | |
| var x: float = i / 100.0 | |
| var y: float = sin(x * 30 + clock * error * 12) * error * 150 + sin(x * 10 + settings[1] * TAU) * 15 | |
| var point: Vector2 = Vector2(160 + x * 640, 280 + y) | |
| if i > 0: | |
| draw_line(previous, point, Color("5ef2d0") if error < 0.09 else Color("ff9568"), 3) | |
| previous = point | |
| for i in range(2): | |
| draw_rect(slider(i), Color("334966")) | |
| draw_circle(Vector2(180 + settings[i] * 600, 447 + i * 90), 17, Color("ffcd6b")) | |
| var guidance: String = "raise" if settings[i] < target[i] - 0.03 else "lower" if settings[i] > target[i] + 0.03 else "near lock" | |
| text_at(("FREQUENCY" if i == 0 else "PHASE") + " - " + guidance, Vector2(180, 416 + i * 90), 20) | |
| text_at("Stable %.1f / 8 seconds" % stable, Vector2(340, 140)) | |
| text_at(outcome, Vector2(255, 613), 23) | |
| ''') | |
| GAMES[81] = ("Three Plank Bridge", '''extends Node2D | |
| var gaps: Array[int] = [3, 4, 5, 8, 9, 12, 13, 14] | |
| var planks: Array[int] = [] | |
| var x: float = 105 | |
| var running: bool = false | |
| var outcome: String = "" | |
| func reset() -> void: | |
| planks.clear() | |
| x = 105 | |
| running = false | |
| outcome = "" | |
| func supported(cell: int) -> bool: | |
| if not gaps.has(cell): | |
| return true | |
| for start in planks: | |
| if cell >= start and cell < start + 3: | |
| return true | |
| return false | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and not e.echo: | |
| if e.keycode == KEY_R: | |
| reset() | |
| elif e.keycode == KEY_SPACE and outcome == "": | |
| running = true | |
| if e is InputEventMouseButton and e.pressed and not running and e.position.y > 270 and e.position.y < 520: | |
| var cell: int = clampi(floori((e.position.x - 84) / 44), 0, 15) | |
| if e.button_index == MOUSE_BUTTON_LEFT and planks.size() < 3: | |
| planks.append(cell) | |
| elif e.button_index == MOUSE_BUTTON_RIGHT: | |
| for i in range(planks.size() - 1, -1, -1): | |
| if cell >= planks[i] and cell < planks[i] + 3: | |
| planks.remove_at(i) | |
| break | |
| func _process(dt: float) -> void: | |
| if running and outcome == "": | |
| x += 105 * dt | |
| var cell: int = floori((x - 84) / 44) | |
| if not supported(cell): | |
| outcome = "Unsupported step! R: rebuild the bridge" | |
| elif x >= 855: | |
| outcome = "Bridge test passed within budget! R: rebuild" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("THREE PLANK BRIDGE", "Left click: place a three-tile plank starting there | Right click: remove | Space: test | R: restart") | |
| for i in range(18): | |
| var r: Rect2 = Rect2(84 + i * 44, 445, 44, 110) | |
| if not gaps.has(i): | |
| draw_rect(r, Color("46617d")) | |
| else: | |
| draw_rect(r, Color("142131")) | |
| text_at(str(i), Vector2(97 + i * 44, 590), 16) | |
| for start in planks: | |
| draw_rect(Rect2(84 + start * 44, 433, 132, 12), Color("ffcd6b")) | |
| draw_rect(Rect2(84 + start * 44, 433, 132, 12), Color("996b32"), false, 2) | |
| draw_rect(Rect2(x - 10, 405, 20, 28), Color("5ef2d0")) | |
| text_at("Budget: %d / 3 planks" % planks.size(), Vector2(335, 190), 25) | |
| text_at("Cover every dark gap. Unsupported steps fail immediately.", Vector2(205, 250), 20) | |
| text_at(outcome, Vector2(230, 625), 23) | |
| ''') | |
| GAMES[82] = ("Five Card Skirmish", '''extends Node2D | |
| var hp: int = 12 | |
| var enemy: int = 16 | |
| var energy: int = 3 | |
| var turn: int = 0 | |
| var intent: int = 0 | |
| var shield: int = 0 | |
| var note: String = "Choose a card; the previewed enemy action resolves next." | |
| var outcome: String = "" | |
| var names: Array[String] = ["STRIKE", "HEAVY", "SHIELD", "HEAL", "CHARGE"] | |
| var costs: Array[int] = [1, 2, 1, 2, 0] | |
| func reset() -> void: | |
| hp = 12 | |
| enemy = 16 | |
| energy = 3 | |
| turn = 0 | |
| intent = 0 | |
| shield = 0 | |
| outcome = "" | |
| func card(i: int) -> Rect2: | |
| return Rect2(50 + i * 174, 390, 160, 155) | |
| func play(i: int) -> void: | |
| if outcome != "": | |
| return | |
| if energy < costs[i]: | |
| note = "Not enough energy. Use CHARGE." | |
| return | |
| energy -= costs[i] | |
| shield = 0 | |
| match i: | |
| 0: enemy -= 3 | |
| 1: enemy -= 6 | |
| 2: shield = 5 | |
| 3: hp = mini(12, hp + 5) | |
| 4: energy = mini(6, energy + 3) | |
| if enemy <= 0: | |
| outcome = "Enemy defeated! R: a new skirmish" | |
| return | |
| if intent == 0: | |
| hp -= maxi(0, 3 - shield) | |
| note = "Enemy attacked for 3, reduced by this turn's shield." | |
| elif intent == 1: | |
| hp -= maxi(0, 5 - shield) | |
| note = "Enemy used its previewed heavy attack." | |
| else: | |
| enemy = mini(16, enemy + 2) | |
| note = "Enemy recovered 2 health." | |
| turn += 1 | |
| intent = turn % 3 | |
| if hp <= 0: | |
| outcome = "You were defeated. R: plan against the preview" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed and not e.echo: | |
| if e.keycode == KEY_R: | |
| reset() | |
| for i in range(5): | |
| if e.keycode == KEY_1 + i: | |
| play(i) | |
| if e is InputEventMouseButton and e.pressed and e.button_index == MOUSE_BUTTON_LEFT: | |
| for i in range(5): | |
| if card(i).has_point(e.position): | |
| play(i) | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("FIVE CARD SKIRMISH", "Click one of five cards or press 1–5 | Your action, then the previewed enemy turn | R: restart") | |
| text_at("YOU: %d / 12 HP Energy %d / 6" % [hp, energy], Vector2(80, 165), 24) | |
| text_at("ENEMY: %d / 16 HP" % enemy, Vector2(610, 165), 24) | |
| text_at("NEXT ENEMY: " + ["Attack 3", "Heavy attack 5", "Recover 2 HP"][intent], Vector2(285, 245), 26, Color("ff9568")) | |
| for i in range(5): | |
| draw_rect(card(i), Color("3a526e")) | |
| text_at(names[i], card(i).position + Vector2(14, 40), 22) | |
| text_at("Cost %d" % costs[i], card(i).position + Vector2(17, 83), 20) | |
| text_at(["3 damage", "6 damage", "Block 5", "Heal 5", "+3 energy"][i], card(i).position + Vector2(14, 125), 18) | |
| text_at(note if outcome == "" else outcome, Vector2(125, 610), 23) | |
| ''') | |
| GAMES[83] = ("Color Cargo Train", '''extends Node2D | |
| var train: Vector2i = Vector2i(1, 5) | |
| var direction: Vector2i = Vector2i.RIGHT | |
| var wagons: Dictionary = {Vector2i(3, 5): 0, Vector2i(6, 2): 1, Vector2i(9, 6): 2} | |
| var cargo: Array[int] = [] | |
| var target: Array[int] = [0, 1, 2] | |
| var history: Array[Vector2i] = [] | |
| var clock: float = 0 | |
| var outcome: String = "" | |
| var colors: Array[Color] = [Color("ff6176"), Color("79d7ff"), Color("ffcd6b")] | |
| func reset() -> void: | |
| train = Vector2i(1, 5) | |
| direction = Vector2i.RIGHT | |
| wagons = {Vector2i(3, 5): 0, Vector2i(6, 2): 1, Vector2i(9, 6): 2} | |
| cargo.clear() | |
| history.clear() | |
| clock = 0 | |
| outcome = "" | |
| func _input(e: InputEvent) -> void: | |
| if e is InputEventKey and e.pressed: | |
| if e.keycode == KEY_R: | |
| reset() | |
| var next: Vector2i = direction | |
| if e.keycode == KEY_LEFT: | |
| next = Vector2i.LEFT | |
| elif e.keycode == KEY_RIGHT: | |
| next = Vector2i.RIGHT | |
| elif e.keycode == KEY_UP: | |
| next = Vector2i.UP | |
| elif e.keycode == KEY_DOWN: | |
| next = Vector2i.DOWN | |
| if next != -direction: | |
| direction = next | |
| func _process(dt: float) -> void: | |
| if outcome == "": | |
| clock += dt | |
| if clock >= 0.35: | |
| clock = 0 | |
| history.push_front(train) | |
| if history.size() > 3: | |
| history.pop_back() | |
| train += direction | |
| if train.x < 0 or train.x >= 12 or train.y < 0 or train.y >= 8: | |
| outcome = "Train left the tracks! R: retry" | |
| elif wagons.has(train): | |
| cargo.append(wagons[train]) | |
| wagons.erase(train) | |
| if cargo[-1] != target[cargo.size() - 1]: | |
| outcome = "Wrong wagon order! R: reroute" | |
| if train == Vector2i(10, 1): | |
| outcome = "Matching train arrived! R: replay" if cargo == target else "Station rejected an incomplete sequence. R: retry" | |
| queue_redraw() | |
| func point(p: Vector2i) -> Vector2: | |
| return Vector2(215, 162) + Vector2(p) * 45 | |
| func _draw() -> void: | |
| backdrop("COLOR CARGO TRAIN", "Arrows: steer the moving train | Collect RED, BLUE, GOLD in order, then enter station S | R: restart") | |
| for y in range(8): | |
| for x in range(12): | |
| draw_rect(Rect2(point(Vector2i(x, y)) - Vector2.ONE * 21, Vector2.ONE * 42), Color("26394e"), false, 1) | |
| for p in wagons: | |
| draw_rect(Rect2(point(p) - Vector2.ONE * 15, Vector2.ONE * 30), colors[wagons[p]]) | |
| draw_rect(Rect2(point(Vector2i(10, 1)) - Vector2.ONE * 20, Vector2.ONE * 40), Color("31785a")) | |
| text_at("S", point(Vector2i(10, 1)) + Vector2(-7, 8), 24) | |
| for i in range(mini(cargo.size(), history.size())): | |
| draw_rect(Rect2(point(history[i]) - Vector2.ONE * 13, Vector2.ONE * 26), colors[cargo[i]]) | |
| draw_circle(point(train), 16, Color.WHITE) | |
| text_at("Wagons %d / 3 TARGET: RED > BLUE > GOLD" % cargo.size(), Vector2(230, 555)) | |
| text_at(outcome, Vector2(210, 610), 23) | |
| ''') | |
| GAMES[84] = ("Bubble Passage", '''extends Node2D | |
| var bubble: Vector2 = Vector2(160, 570) | |
| var air: float = 55 | |
| var walls: Array[Rect2] = [Rect2(20, 455, 395, 25), Rect2(445, 455, 495, 25), Rect2(20, 340, 625, 25), Rect2(675, 340, 265, 25), Rect2(20, 225, 305, 25), Rect2(335, 225, 605, 25)] | |
| var outcome: String = "" | |
| var radius: float = 16 | |
| func reset() -> void: | |
| bubble = Vector2(160, 570) | |
| air = 55 | |
| outcome = "" | |
| func _process(dt: float) -> void: | |
| if Input.is_physical_key_pressed(KEY_R): | |
| reset() | |
| if outcome == "": | |
| var shrink: bool = Input.is_action_pressed("ui_accept") | |
| radius = 4.0 if shrink else 16.0 | |
| air -= dt * (1.8 if shrink else 1.0) | |
| var movement: Vector2 = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 175 | |
| var next: Vector2 = bubble + movement * dt | |
| next.x = clampf(next.x, 25 + radius, 935 - radius) | |
| next.y = minf(600 - radius, next.y) | |
| var blocked: bool = false | |
| for r in walls: | |
| if r.grow(radius).has_point(next): | |
| blocked = true | |
| if not blocked: | |
| bubble = next | |
| if bubble.y < 130: | |
| outcome = "Surface reached with air to spare! R: dive again" | |
| elif air <= 0: | |
| outcome = "Air exhausted. R: shrink only at narrow gates" | |
| queue_redraw() | |
| func _draw() -> void: | |
| backdrop("BUBBLE PASSAGE", "Arrows: swim upward through the gates | Hold Space: shrink, using extra air | R: restart") | |
| draw_rect(Rect2(20, 95, 920, 35), Color("276c86")) | |
| text_at("SURFACE", Vector2(420, 120), 20) | |
| for r in walls: | |
| draw_rect(r, Color("46617d")) | |
| draw_circle(bubble, radius, Color("79d7ff")) | |
| draw_arc(bubble, radius, 0, TAU, 24, Color.WHITE, 2) | |
| text_at("Air %.1f %s" % [maxf(air, 0), "SHRUNK" if radius < 10 else "NORMAL"], Vector2(30, 625), 22) | |
| text_at(outcome, Vector2(320, 625), 22) | |
| ''') | |
| if __name__ == "__main__": | |
| main() | |