/** Local practice budget only. Published benchmark data are never written here. */ export class PracticeBudget { actions=0; private started:number; private endElapsed:number|null=null; private end=''; private now:()=>number; constructor(now:()=>number=()=>performance.now()){this.now=now;this.started=now();} elapsed(){return this.endElapsed??Math.max(0,(this.now()-this.started)/1000);} remaining(){return Math.max(0,720-this.elapsed());} ending(){ if(!this.end&&this.elapsed()>=720)this.finish('time_budget',720); return this.end; } recordAction(completedLevels:number){ if(this.ending())return false; this.actions++; // A sixth verified level completes the curriculum, including on action 512. if(completedLevels>=6)this.finish('won'); else if(this.actions>=512)this.finish('action_budget'); return true; } private finish(status:string,elapsed=this.elapsed()){this.end=status;this.endElapsed=elapsed;} }