"""Private gameplay setup/trace for verify_four_goalie.py; actual hardware input."""
from pathlib import Path
import json
p=Path(outdir);spec=json.loads((p/'spec.json').read_text());trace=(p/'trace.jsonl').open('w')
side=spec['side'];team=1 if side==5 else 2;base=0 if side==5 else 6
active=spec['active'];pad=spec['pad'];mode=spec['mode'];caught=None;pressed=None;last=None
def rw(a):return (wram[ram(a)]<<8)|wram[ram(a+1)]
def ww(a,v):v&=65535;wram[ram(a)]=v>>8;wram[ram(a+1)]=v&255
def sw(a):v=rw(a);return v-65536 if v&32768 else v
def rb(a,n):return bytes(wram[ram(a+i)] for i in range(n))
def controls():
 values=[rw(0xc320+i*2) for i in range(4)];bank=rw(0xf0d4)
 if bank==1:values[2]=values[0];values[0]=rw(0xf0d6)
 if bank==2:values[3]=values[1];values[1]=rw(0xf0d8)
 return values
def test_tick(f):
 global caught,pressed,last
 if f<100:return
 g=0xb04a+side*128
 if f==100:
  ww(0xf0d4,0)
  if mode=='dive':
   old=Path(spec['dive_wram']).read_bytes()
   for addr,size in [(0xb04a,0x600),(0xb74a,0x80),(0xbeda,0x14)]:
    for a in range(addr,addr+size):wram[ram(a)]=old[ram(a)]
  ww(0xd046,1);ww(0xd04a,{1:1,2:3,3:5,4:6}[len(active)]);ww(0xf07c,len(active) if len(active)>2 else 0)
  ww(0xd052,0);ww(0xd05a,0);ww(0xd05c,0);ww(0xf064,0);ww(0xf066,0)
  for i in range(12):
   o=0xb04a+i*128;wram[ram(o+0x62)]&=247
   if mode!='dive' and i not in [5,11]:ww(o,130);ww(o+0x14,0);ww(o+0x28,0);ww(o+0x2a,0)
  if mode!='dive':ww(g,0);ww(g+0x14,-235 if side==5 else 235);ww(g+0x28,0);ww(g+0x2a,0)
  for i in range(4):
   ww(0xc328+i*2,team if i+1 in active else 0);ww(0xc320+i*2,base+i if i+1 in active else 65535)
   if i+1 in active:
    o=0xb04a+(base+i)*128;wram[ram(o+0x62)]|=8
    if mode!='dive':ww(o,[-70,0,70,-110][i]);ww(o+0x14,sw(g+0x14)+[70,100,70,0][i]*(1 if side==5 else -1))
  if mode!='dive':
   ww(0xb7aa,65535);ww(0xbee0,65535);ww(0xbeda,65535);ww(0xb74a,sw(g));ww(0xb75e,sw(g+0x14))
   for addr in [0xb74c,0xb760,0xb762,0xb772,0xb774,0xb776]:ww(addr,0)
  wram[ram(0xc2ea)]&=238;wram[ram(0xc2f2)]&=251
  if mode in ['y','steal']:
   # Isolate takeover/hold from a random CPU pass and skater pickup.
   ww(0xb7aa,65535);ww(0xb74a,0);ww(0xb75e,0)
  if spec.get('handoff_pad'):
   hand=spec['handoff_pad']-1;previous=rw(0xc320+hand*2)
   wram[ram(0xb04a+previous*128+0x62)]&=~8
   ww(0xc320+hand*2,side);wram[ram(g+0x62)]|=8
  if mode=='early':held[(pad-1,IDS['B'])]=spec['duration'];pressed=f
 if rw(0xb7aa)==side and caught is None:caught=f
 if mode in ['y','steal'] and f==150:held[(pad-1,IDS['Y'])]=5;pressed=f
 if mode=='steal' and f==165:held[(spec['second']-1,IDS['Y'])]=5
 if mode not in ['y','steal','early','hold'] and caught is not None and pressed is None and f>=caught+spec['delay']:
  held[(pad-1,IDS['Y'] if mode=='ypass' else IDS['B'])]=spec['duration'];pressed=f
  if mode=='simultaneous':
   for i in active:held[(i-1,IDS['B'])]=spec['duration']
 if mode=='duel' and pressed is not None and f==pressed+1:held[(spec['second']-1,IDS['B'])]=5
 if mode=='mash' and pressed is not None and pressed<f<pressed+45 and (f-pressed)%6==0:held[(pad-1,IDS['B'])]=2
 row=dict(objects=[[sw(0xb04a+i*128),sw(0xb04a+i*128+0x14),sw(0xb04a+i*128+0x34),rw(0xb04a+i*128+0x62)] for i in range(12)],owner=rw(0xb7aa),controls=controls(),bank=rw(0xf0d4),sides=[rw(0xc328+i*2) for i in range(4)],pending=[rw(0xf064),rw(0xf066)],flags=rw(g+0x62),timer=rw(g+0x48),anim=rw(g+0x58),puck=[sw(0xb74a),sw(0xb75e)],gk=[sw(g),sw(g+0x14)],passer=rw(0xbeda),target=rw(0xbee0),caught=caught,pressed=pressed,velocity=[sw(0xb772),sw(0xb774)],humans=[[sw(0xb04a+(base+i)*128),sw(0xb04a+(base+i)*128+0x14)] for i in range(4)],transient=rb(0xf058,8).hex(),magic=rw(0xf0ca),whistle=rw(0xc2ea))
 if row!=last:trace.write(json.dumps([f,row])+'\n');trace.flush();last=row
