"""Vacuum cuts in the real pixel frame loop with active browser workers and hardware graphics.""" import pathlib import sys sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / 'tools')) from use_venv import ensure_playwright; ensure_playwright() from browser import launch, consume_gpu_option, capabilities consume_gpu_option() ROOT = pathlib.Path(__file__).resolve().parents[1] with launch(viewport={'width': 1440, 'height': 760}) as (_, page, url): errors = [] page.on('pageerror', lambda e: errors.append(str(e))) page.goto(url) caps = capabilities(page) assert caps['isolated'] and caps['webgpu'], caps print('GPU', caps, flush=True) page.evaluate("""() => { const enter = enterGame; enterGame = function() { enter(); GAME.paused = true; }; SET.workerCap=2; SET.fog=false; SET.lighting='off'; SET.cameraLead=false; SET.autosave=0; startNewWorld(7,768,600); }""") page.wait_for_function('GAME.running', timeout=60000) page.evaluate((ROOT / 'test/pile-cut.js').read_text()) page.evaluate("""() => { pileCutWorld(); pileCutSetup(160,false); pileCutSetup(416,true); bot.x=700;bot.y=100;for(let k=0;k<600;k++)simTick(); renderDirty.fill(1);GPU.world=null;SM.surf=-1; }""") page.evaluate('startThreads()') page.evaluate("""() => { // Tiny isolated fixtures otherwise run inline below the production dispatch threshold. simPool.force=true; window.cutTicks=0;window.cutEnd=0; const tool=toolTick;toolTick=()=>{ tool(); if(cutEnd && tick===cutEnd+180) window.cutResult={tick,cutEnd, stone:pileCutProfile(160),mixed:pileCutProfile(416),helpers:simPool.activeMax}; if(cutTicks<140){pileCutStroke(cutTicks%2?416:160);cutTicks++;if(cutTicks===140)cutEnd=tick+1;} }; document.querySelectorAll('.card,.toast').forEach(e=>e.remove()); $('hud').hidden=true;GAME.hidePreviews=true;SET.zoom=3;resizeRender(); bot.x=288-bot.w/2;bot.y=190-bot.h/2;snapCamera(); // Keep both heaps in view while the bot follows each vacuum stroke. updateCamera=()=>{}; GAME.paused=false; }""") page.wait_for_function('cutEnd>0', timeout=30000) for delay in (0, 60, 120, 180): page.wait_for_function(f'tick>=cutEnd+{delay}', timeout=15000) result = page.evaluate('({tick,cutEnd,stone:pileCutProfile(160),mixed:pileCutProfile(416),helpers:simPool.activeMax})') print('profile', delay, result, flush=True) page.screenshot(path=str(ROOT / f'shots/t93b-time-{delay}.png')) page.wait_for_function('window.cutResult', timeout=15000) result = page.evaluate('cutResult') page.evaluate('GAME.paused=true') page.screenshot(path=str(ROOT / 'shots/t93b-after.png')) assert result['helpers'] > 0, result assert result['stone']['cliff'] <= 2 and result['mixed']['cliff'] <= 2, result assert not errors, errors print('PASS vacuum-cut stone and mixed faces slump within 180 real-loop ticks with browser helpers')