"""Nine setup frames, then unmodified native players, clock, AI and puck."""
from pathlib import Path
import json,math
p=Path(outdir);s=json.loads((p/'spec.json').read_text());rows=[];team=s['team'];direction=s['direction'];own=team*6;opp=(1-team)*6;kind=s['kind'];carrier=(opp if kind=='pressure' else own)+2
rw=lambda a:(wram[ram(a)]<<8)|wram[ram(a+1)]
def sw(a):v=rw(a);return v-65536 if v&32768 else v
def ww(a,v):v&=65535;wram[ram(a)]=v>>8;wram[ram(a+1)]=v&255
def obj(i):return 0xb04a+128*i
def put(i,x,y):
 b=obj(i)
 for off,v in [(0,x),(2,0),(0x14,y*direction),(0x16,0),(0x18,0),(0x1a,0),(0x28,0),(0x2a,0),(0x2c,0),(0x2e,-1),(0x30,0),(0x32,0),(0x36,0),(0x40,0),(0x42,8),(0x44,0),(0x46,230*direction),(0x48,16),(0x58,0),(0x5a,0),(0x5c,0),(0x5e,0)]:ww(b+off,v)
 for off in [0x60,0x61]:wram[ram(b+off)]=255
 role=[5,3,4,2,1,0][i%6];ww(b+0x34,role);ww(b+0x52,i)
 wram[ram(b+0x62)]=(64 if i//6 else 0)+(128 if (direction if i//6==team else -direction)==1 else 0);wram[ram(b+0x63)]=0;wram[ram(b+0x64)]=0
 wram[ram(b+0x38)]={5:4,3:4,4:6,2:2,1:2,0:14}[role]
def test_tick(f):
 if f<100:return
 if f<=108:
  held.clear();ww(0xd046,1);ww(0xd04a,2);ww(0xf07c,0);ww(0xf0d4,0)
  for i in range(12):put(i,140 if i%2 else -140,0)
  put(own+5,0,-246);put(opp+5,0,246)
  if kind=='pressure':
   put(carrier,90,-140);put(own+2,40,-220);put(own+3,-100,-220);put(own+4,-130,-230);human=carrier;px,depth=90,-140
   wram[ram(obj(own+2)+0x38)]=17
   for i in range(own,own+5):wram[ram(obj(i)+0x38)]={0:3,1:3,2:17,3:2,4:2}[i%6]
  elif kind=='point':
   px,depth=s.get('x',60),112;put(carrier,px,depth);human=opp+2
   if s.get('blocked'):put(opp,px//2,188)
   if s.get('isolate_point'):
    participants={carrier,own+5,opp+2,opp+5}
    if s.get('blocked'):participants.add(opp)
    for actor in range(12):
     if actor not in participants:ww(obj(actor)+0x34,-1)
  elif kind=='finish':
   px,depth=110,180;put(carrier,px,depth);put(own,0,220);ww(obj(own)+0x54,2);human=opp+2
  else:
   # Lane to the finisher is initially blocked; the point relay is open.
   px,depth=110,100;put(carrier,px,depth);put(own,s.get('relay_x',80),148);put(own+1,-8,224);put(opp,45,162);human=opp+2
   ww(obj(own)+0x54,3 if direction==1 else 1);ww(obj(own+1)+0x54,2)
   # Point relay remains a CPU; ordinary base assignments choose all actions.
  for i in range(4):ww(0xc328+2*i,0);ww(0xc320+2*i,65535)
  ww(0xc328,human//6+1);ww(0xc320,human);wram[ram(obj(human)+0x62)]|=8
  wram[ram(obj(carrier)+0x38)]=16;wram[ram(obj(carrier)+0x39)]=17;ww(obj(carrier)+0x36,0)
  home_direction=direction if team==0 else -direction
  wram[ram(0xc2ea)]&=~0x53
  if home_direction==-1:wram[ram(0xc2ea)]|=2
  for addr,mask in [(0xc2ec,128),(0xc2ee,128),(0xc2f2,4),(0xc2fa,5)]:wram[ram(addr)]&=~mask
  ww(0xf07a,s.get('difficulty',1));ww(0xd050,0);ww(0xc468,290);ww(0xd058,1);ww(0xd052,0);ww(0xb7aa,carrier);ww(0xb74a,px);ww(0xb75e,depth*direction)
  for addr in [0xb74c,0xb760,0xb762,0xb764,0xb772,0xb774,0xb776]:ww(addr,0)
  for addr in [0xbee0,0xbeda,0xbed8,0xbf6c]:ww(addr,-1)
  ww(0xf060,0);ww(0xf002,0)
 if f>108:
  jobs=[wram[ram(obj(i)+0x38+min(rw(obj(i)+0x36),7))] for i in range(12)]
  positions=[[sw(obj(i)),sw(obj(i)+0x14)*direction] for i in range(12)]
  cx,cy=positions[carrier];nearest=min(math.hypot(x-cx,y-cy) for x,y in positions[own:own+5])
  rows.append(dict(frame=f-108,owner=rw(0xb7aa),carrier=carrier,passer=rw(0xbeda),target=rw(0xbee0),shooter=rw(0xbed8),shot_clock=rw(0xbeec),puck=[sw(0xb74a),sw(0xb75e)*direction],height=sw(0xb762),positions=positions,nearest=round(nearest,3),jobs=jobs,cpu_pending=rw(0xf088) if rw(0xf086)==0x5042 else 0,shot_power=rw(0xbede),animations=[rw(obj(i)+0x58) for i in range(12)],one_timer=[bool(wram[ram(obj(i)+0x64)]&8) for i in range(12)],clock=rw(0xc468),sprite_bytes=rw(0xc2e8),controls=[rw(0xc320),rw(0xc322)],controller_teams=[rw(0xc328),rw(0xc32a)],score=[rw(0xc6da),rw(0xca3e)]))
 if f==708:(p/'trajectory.json').write_text(json.dumps(rows,indent=2)+'\n')
