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