#!/usr/bin/env python3 """Eight original in-session Codex teacher games. Admission still needs Godot.""" from pathlib import Path import hashlib import json import sys ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / 'src')) from vibegame.seeds import PROJECT, SCENE, UI from vibegame.project import parse_project SCRIPTS = [ '''extends Node2D var lane = 0 var progress = 0.0 var lost = false var gaps = [1,0,1,1,0,0,1,0,1,0,0,1] func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): lane = 0 progress = 0.0 lost = false if not lost and progress < 12.0: if Input.is_action_just_pressed("ui_left"): lane = 0 if Input.is_action_just_pressed("ui_right"): lane = 1 var before = int(progress) progress += dt * 0.7 if int(progress) > before and lane == gaps[before]: lost = true queue_redraw() func _draw() -> void: backdrop("RAIL SWITCH", "Left / Right: select rail | Avoid dark gaps | R: restart") for i in range(12): var y = 490.0 + (progress - float(i + 1)) * 145.0 if y > 80 and y < 640: for track in range(2): var color = Color("354866") if gaps[i] != track else Color("090b16") draw_rect(Rect2(300 + track * 260, y - 80, 100, 110), color) var finish_y = 490.0 + (progress - 12.0) * 145.0 if finish_y > 100: draw_line(Vector2(280, finish_y), Vector2(690, finish_y), Color("ffdc75"), 8) draw_circle(Vector2(350 + lane * 260, 490), 22, Color("5ef2d0")) draw_line(Vector2(350 + lane * 260, 490), Vector2(350 + lane * 260, 490) + Vector2.from_angle(progress * 8) * 18, Color.WHITE, 3) text_at("Sections %d / 12" % mini(int(progress), 12), Vector2(26, 620)) if lost or progress >= 12: text_at("Missing rail! R to retry" if lost else "Finish reached! R to replay", Vector2(245, 320), 30) ''', '''extends Node2D var shield = Rect2(345, 385, 270, 15) var drag_start = Vector2.ZERO var dragging = false var umbrella = true var drops: Array[Vector3] = [] var spawn = 0.0 var elapsed = 0.0 var health = 5 var water = 7.0 func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): drops.clear() elapsed = 0.0 health = 5 water = 7.0 shield = Rect2(345, 385, 270, 15) umbrella = true spawn = 0 dragging = false if health > 0 and water > 0 and elapsed < 30: elapsed += dt water -= dt * 0.5 var mouse = get_global_mouse_position().clamp(Vector2(230, 145), Vector2(730, 485)) if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): if not dragging: drag_start = mouse dragging = true shield = Rect2(drag_start, mouse - drag_start).abs() shield.size = shield.size.max(Vector2(24, 12)) else: dragging = false if Input.is_action_just_pressed("ui_accept"): umbrella = not umbrella spawn -= dt if spawn <= 0: spawn = 0.45 drops.append(Vector3(randf_range(250, 710), 105, 1 if randf() < 0.65 else 0)) for i in range(drops.size() - 1, -1, -1): drops[i].y += 180 * dt if shield.has_point(Vector2(drops[i].x, drops[i].y)): if (umbrella and drops[i].z == 0) or (not umbrella and drops[i].z == 1): drops.remove_at(i) continue if drops[i].y > 550: if drops[i].z == 0: health -= 1 else: water = minf(12, water + 1) drops.remove_at(i) queue_redraw() func _draw() -> void: backdrop("GARDEN GUARD", "Drag mouse: draw shield | Space: stone mesh / rain cover | R: restart") draw_rect(Rect2(230, 555, 500, 45), Color("456735")) for x in range(270, 720, 50): draw_line(Vector2(x, 555), Vector2(x, 525), Color("5ef2d0"), 5) for drop in drops: draw_circle(Vector2(drop.x, drop.y), 10, Color("69baff") if drop.z == 1 else Color("ad8790")) draw_rect(shield, Color("ffdc75") if umbrella else Color("69baff"), false, 4) text_at("%s | Health %d | Water %.1f | %.0f / 30s" % ["Stone mesh" if umbrella else "Rain cover", health, water, elapsed], Vector2(30, 625), 21) if health <= 0 or water <= 0 or elapsed >= 30: text_at("Garden saved!" if elapsed >= 30 else "Garden lost. R to retry", Vector2(300, 270), 30) ''', '''extends Node2D var a = Vector2(70, 350) var b = Vector2(340, 60) var goal_a = Vector2(340, 60) var goal_b = Vector2(70, 350) var walls_a = [Rect2(140, 170, 130, 28), Rect2(70, 260, 80, 28)] var walls_b = [Rect2(180, 235, 150, 28), Rect2(110, 95, 28, 90)] var won = false func move_piece(p: Vector2, motion: Vector2, walls: Array) -> Vector2: for axis in range(2): var candidate = p candidate[axis] += motion[axis] candidate = candidate.clamp(Vector2(16, 16), Vector2(394, 424)) var blocked = false for wall in walls: if wall.grow(12).has_point(candidate): blocked = true if not blocked: p = candidate return p func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): a = Vector2(70, 350) b = Vector2(340, 60) won = false if not won: var motion = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 170 * dt a = move_piece(a, motion, walls_a) b = move_piece(b, -motion, walls_b) won = a.distance_to(goal_a) < 20 and b.distance_to(goal_b) < 20 queue_redraw() func _draw() -> void: backdrop("MIRROR PARTNERS", "Arrows move both in opposite directions | Reach both gold pads | R: restart") var offsets = [Vector2(35, 135), Vector2(515, 135)] for i in range(2): var off = offsets[i] draw_rect(Rect2(off, Vector2(410, 440)), Color("172541")) var walls = walls_a if i == 0 else walls_b for wall in walls: draw_rect(Rect2(off + wall.position, wall.size), Color("546783")) draw_circle(off + (goal_a if i == 0 else goal_b), 23, Color("ffdc75")) draw_circle(off + (a if i == 0 else b), 12, Color("5ef2d0") if i == 0 else Color("e69fff")) text_at("Both pads occupied!" if won else "Walls stop each piece independently. Find the route.", Vector2(90, 625), 24) ''', '''extends Node2D var rider = Vector2(100, 330) var parcels = [Vector2(170, 170), Vector2(360, 480), Vector2(480, 230)] var doors = [Vector2(820, 170), Vector2(800, 450), Vector2(660, 300)] var colors = [Color("ff647e"), Color("5ef2d0"), Color("ffdc75")] var delivered = [false, false, false] var carried = -1 var remaining = 45.0 var count = 0 func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): rider = Vector2(100, 330) delivered = [false, false, false] carried = -1 remaining = 45.0 count = 0 if remaining > 0 and count < 3: remaining = maxf(0, remaining - dt) rider += Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 210 * dt rider = rider.clamp(Vector2(25, 115), Vector2(935, 580)) if carried < 0: for i in range(3): if not delivered[i] and rider.distance_to(parcels[i]) < 26: carried = i break elif rider.distance_to(doors[carried]) < 35: delivered[carried] = true carried = -1 count += 1 queue_redraw() func _draw() -> void: backdrop("PARCEL PEDAL", "Arrows: bicycle | Touch parcel, then matching door | Carry one | R: restart") for i in range(3): draw_rect(Rect2(doors[i] - Vector2(24, 32), Vector2(48, 64)), colors[i].darkened(0.3)) text_at("OK" if delivered[i] else str(i + 1), doors[i] + Vector2(-15, 6), 22) if not delivered[i] and carried != i: draw_rect(Rect2(parcels[i] - Vector2(12, 12), Vector2(24, 24)), colors[i]) draw_circle(rider + Vector2(-12, 10), 9, Color.WHITE, false, 3) draw_circle(rider + Vector2(12, 10), 9, Color.WHITE, false, 3) draw_line(rider + Vector2(-12, 10), rider + Vector2(0, -8), Color("69baff"), 4) draw_line(rider + Vector2(0, -8), rider + Vector2(12, 10), Color("69baff"), 4) if carried >= 0: draw_rect(Rect2(rider + Vector2(-8, -26), Vector2(16, 16)), colors[carried]) text_at("Delivered %d / 3 | Time %.1f" % [count, remaining], Vector2(25, 625)) if remaining <= 0 or count == 3: text_at("All delivered!" if count == 3 else "Time ran out. R to retry", Vector2(295, 370), 30) ''', '''extends Node2D var angle = 0.0 var direction = 1.0 var objects: Array[Vector3] = [] var spawn = 0.0 var stars = 0 var lives = 3 var center = Vector2(480, 360) func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): stars = 0 lives = 3 angle = 0 spawn = 0 direction = 1 objects.clear() if lives > 0 and stars < 8: if Input.is_action_just_pressed("ui_accept"): direction = -direction angle += dt * direction * 1.7 spawn -= dt if spawn <= 0: spawn = 1.1 objects.append(Vector3(randf_range(-PI, PI), 320, 1 if randf() < 0.65 else 0)) var satellite = center + Vector2.from_angle(angle) * 185 for i in range(objects.size() - 1, -1, -1): objects[i].y -= dt * 90 var p = center + Vector2.from_angle(objects[i].x) * objects[i].y if satellite.distance_to(p) < 26: if objects[i].z == 1: stars += 1 else: lives -= 1 objects.remove_at(i) elif objects[i].y < 50: objects.remove_at(i) queue_redraw() func _draw() -> void: backdrop("ORBIT FLIP", "Space: reverse direction | Collect 8 gold stars, avoid red debris | R: restart") draw_circle(center, 185, Color("334970"), false, 2) draw_circle(center, 55, Color("4267a0")) for obj in objects: var p = center + Vector2.from_angle(obj.x) * obj.y if obj.z == 1: draw_colored_polygon(PackedVector2Array([p + Vector2(0,-13), p + Vector2(10,0), p + Vector2(0,13), p + Vector2(-10,0)]), Color("ffdc75")) else: draw_circle(p, 14, Color("ff647e")) draw_circle(center + Vector2.from_angle(angle) * 185, 12, Color("5ef2d0")) text_at("Stars %d / 8 | Lives %d" % [stars, lives], Vector2(25, 625)) if lives <= 0 or stars >= 8: text_at("Orbit mastered!" if stars >= 8 else "Satellite lost. R to retry", Vector2(305, 365), 26) ''', '''extends Node2D var p = Vector2(75, 520) var velocity = 0.0 var gravity = 1.0 var deaths = 0 var won = false var platforms = [Rect2(0, 600, 960, 18), Rect2(0, 100, 960, 18), Rect2(170, 410, 240, 18), Rect2(470, 270, 230, 18), Rect2(760, 460, 160, 18)] var spikes = [Rect2(230, 580, 150, 20), Rect2(530, 118, 145, 20)] var goal = Rect2(860, 365, 48, 80) func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): p = Vector2(75, 520) velocity = 0 gravity = 1 deaths = 0 won = false if not won: if Input.is_action_just_pressed("ui_accept"): gravity = -gravity velocity = 0 p.x = clampf(p.x + Input.get_axis("ui_left", "ui_right") * 220 * dt, 12, 948) var old_y = p.y velocity = clampf(velocity + gravity * 500 * dt, -300, 300) p.y += velocity * dt for ledge in platforms: if p.x + 12 > ledge.position.x and p.x - 12 < ledge.end.x: if velocity > 0 and old_y + 12 <= ledge.position.y and p.y + 12 >= ledge.position.y: p.y = ledge.position.y - 12 velocity = 0 elif velocity < 0 and old_y - 12 >= ledge.end.y and p.y - 12 <= ledge.end.y: p.y = ledge.end.y + 12 velocity = 0 for spike in spikes: if spike.grow(11).has_point(p): p = Vector2(75, 520) velocity = 0 gravity = 1 deaths += 1 won = goal.has_point(p) queue_redraw() func _draw() -> void: backdrop("GRAVITY SWITCH", "Left / Right: move | Space: reverse gravity | Reach green door | R: restart") for ledge in platforms: draw_rect(ledge, Color("506383")) for spike in spikes: draw_rect(spike, Color("ff647e")) draw_rect(goal, Color("28795c")) draw_rect(Rect2(p - Vector2(12,12), Vector2(24,24)), Color("5ef2d0")) text_at("Falls %s | Resets %d" % ["down" if gravity > 0 else "up", deaths], Vector2(25, 633), 21) if won: text_at("Door reached! R to replay", Vector2(275, 350), 30) ''', '''extends Node2D var parcels: Array[Vector2] = [] var junction = 0 var spawn = 0.0 var quota = 0 var misses = 0 var colors = [Color("ff647e"), Color("5ef2d0"), Color("ffdc75")] func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): parcels.clear() quota = 0 misses = 0 spawn = 0 junction = 0 if quota < 12 and misses < 5: if Input.is_action_just_pressed("ui_up"): junction = maxi(0, junction - 1) if Input.is_action_just_pressed("ui_down"): junction = mini(2, junction + 1) spawn -= dt if spawn <= 0: spawn = 1.6 parcels.append(Vector2(30, randi_range(0, 2))) for i in range(parcels.size() - 1, -1, -1): parcels[i].x += 155 * dt if parcels[i].x >= 725: if int(parcels[i].y) == junction: quota += 1 else: misses += 1 parcels.remove_at(i) queue_redraw() func _draw() -> void: backdrop("SORTING SHIFT", "Up / Down: choose bin before object reaches junction | 12 correct to win | R: restart") draw_rect(Rect2(20, 320, 710, 65), Color("354866")) for i in range(3): draw_line(Vector2(725, 350), Vector2(835, 185 + i * 150), colors[i].darkened(0.5), 8) draw_rect(Rect2(825, 145 + i * 150, 110, 85), colors[i].darkened(0.3)) text_at(["CIRCLE", "SQUARE", "TRIANGLE"][i], Vector2(830, 194 + i * 150), 17) draw_line(Vector2(725,350), Vector2(835,185 + junction * 150), Color.WHITE, 4) for parcel in parcels: var p = Vector2(parcel.x, 350) if parcel.y == 0: draw_circle(p, 17, colors[0]) elif parcel.y == 1: draw_rect(Rect2(p - Vector2(16,16), Vector2(32,32)), colors[1]) else: draw_colored_polygon(PackedVector2Array([p + Vector2(0,-19), p + Vector2(-19,17), p + Vector2(19,17)]), colors[2]) text_at("Quota %d / 12 | Mistakes %d / 5" % [quota, misses], Vector2(25, 625)) if quota >= 12 or misses >= 5: text_at("Shift complete!" if quota >= 12 else "Too many mistakes. R to retry", Vector2(225, 245), 28) ''', '''extends Node2D var a = Vector2(100, 230) var b = Vector2(100, 460) var switch_a = Vector2(440, 230) var switch_b = Vector2(440, 460) var unlocked = false var won = false func _process(dt: float) -> void: if Input.is_physical_key_pressed(KEY_R): a = Vector2(100,230) b = Vector2(100,460) unlocked = false won = false if not won: var wasd = Vector2(float(Input.is_physical_key_pressed(KEY_D)) - float(Input.is_physical_key_pressed(KEY_A)), float(Input.is_physical_key_pressed(KEY_S)) - float(Input.is_physical_key_pressed(KEY_W))) a += wasd.limit_length() * 220 * dt b += Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * 220 * dt a = a.clamp(Vector2(35,140), Vector2(925 if unlocked else 665,310)) b = b.clamp(Vector2(35,370), Vector2(925 if unlocked else 665,540)) if a.distance_to(switch_a) < 25 and b.distance_to(switch_b) < 25: unlocked = true won = unlocked and a.x > 850 and b.x > 850 queue_redraw() func _draw() -> void: backdrop("TWO BOXES, ONE PLAN", "Cyan: WASD | Purple: arrows | Occupy both switches, then exits | R: restart") for y in [125,355]: draw_rect(Rect2(20,y,920,200), Color("172541")) draw_circle(switch_a, 26, Color("5ef2d0") if a.distance_to(switch_a) < 25 else Color("60583b")) draw_circle(switch_b, 26, Color("e69fff") if b.distance_to(switch_b) < 25 else Color("60583b")) for y in [140,370]: draw_rect(Rect2(850,y,75,170), Color("28795c")) if not unlocked: draw_rect(Rect2(680,y,18,170), Color("ff647e")) draw_rect(Rect2(a - Vector2(14,14), Vector2(28,28)), Color("5ef2d0")) draw_rect(Rect2(b - Vector2(14,14), Vector2(28,28)), Color("e69fff")) text_at("Both escaped!" if won else ("Gates latched open. Reach both exits." if unlocked else "Leave one box on its switch while moving the other."), Vector2(70,615), 23) ''' ] def main(): prompts = [json.loads(line) for line in (ROOT / 'data/teacher_prompts.jsonl').read_text().splitlines()][:8] rows = [] provenance = [] for prompt, script in zip(prompts, SCRIPTS): files = {'project.godot': PROJECT % prompt['id'], 'main.tscn': SCENE, 'main.gd': script + UI} output = json.dumps({'files': files}, ensure_ascii=False) parse_project(output) rows.append({'instruction': prompt['instruction'], 'output': output}) provenance.append({'id': prompt['id'], 'output_sha256': hashlib.sha256(output.encode()).hexdigest(), 'author': 'in-session Codex assistant', 'license': 'Apache-2.0', 'origin': 'Original gameplay authored here; fixed original project/scene/UI helper. Not a Qwen teacher output.', 'validation': 'Static project contract only; requires separate Linux Godot import and full 60s run before training.'}) out = ROOT / 'data/codex_teacher_batch_root.jsonl' out.write_text(''.join(json.dumps(row, ensure_ascii=False) + '\n' for row in rows)) out.with_name('codex_teacher_batch_root_provenance.json').write_text(json.dumps(provenance, indent=2) + '\n') print('Wrote', len(rows), 'original teacher candidates; runtime admission pending') if __name__ == '__main__': main()