VibeGame-8B / code /scripts /create_teacher_batch_a.py
khursheed's picture
Release pipeline source, tests, notebook, and licenses
ae36cfb verified
Raw History Blame Contribute Delete
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()