-- Natural co-op test: P1 bot chases puck and passes to P2; P2 bot skates to open ice ahead of P1 and waits. local sp=manager.machine.devices[":maincpu"].spaces["program"] local ports=manager.machine.ioport.ports local function F(p,b) return ports[":ctrl"..p..":mdpad:PAD"].fields["P"..p.." "..b] end local K={} for p=1,2 do K[p]={U=F(p,"Up"),D=F(p,"Down"),L=F(p,"Left"),R=F(p,"Right"),B=F(p,"B"),C=F(p,"C")} end local function w(a) local v=sp:read_u16(a); if v>=0x8000 then v=v-0x10000 end return v end local function ob(i) return 0xffb04a+i*0x80 end local function pos(i) return w(ob(i)),w(ob(i)+0x14) end local lf=io.open(os.getenv("BOTOUT") or "bot2.txt","w") local fr=0; local maxp=tonumber(os.getenv("MAXPASS") or "40"); local seed=tonumber(os.getenv("SEED") or "1") math.randomseed(seed) AIMJ=nil local jit=tonumber(os.getenv("AIMJIT") or "0") local p2move=os.getenv("P2MOVE") local function clr(p) for _,k in pairs(K[p]) do k:clear_value() end end local function steer(p,dx,dy,dead) for _,k in ipairs({"U","D","L","R"}) do K[p][k]:clear_value() end dead=dead or 0 if dx*dx+dy*dy<=dead*dead then return end if p==1 and AIMJ then local a=math.rad(AIMJ); local c,sn=math.cos(a),math.sin(a); dx,dy=dx*c-dy*sn,dx*sn+dy*c end local ax,ay=math.abs(dx),math.abs(dy) if ax>ay*0.41 then if dx>0 then K[p].R:set_value(1) else K[p].L:set_value(1) end end if ay>ax*0.41 then if dy>0 then K[p].U:set_value(1) else K[p].D:set_value(1) end end end local ph="chase"; local t=0; local passes=0; local tgt=-1; local p2spot={0,0}; local spot_t=0 local stats={n=0,tgtP2=0,recvP2=0,recvCPU=0,recvOpp=0,loose=0} local cur={} emu.register_frame_done(function() fr=fr+1; if fr<450 then return end local c1=sp:read_u16(0xffc320); local c2=sp:read_u16(0xffc322); local own=w(0xffb7aa) if c1>11 or c2>11 then clr(1); clr(2); return end local x1,y1=pos(c1); local x2,y2=pos(c2); local px,py=w(0xffb74a),w(0xffb75e) -- attack direction: team1 attacks +y in period 1 (goalie1 at -y) local gy=select(2,pos(5)); local dirY=(gy<0) and 1 or -1 -- P2 bot: pick a spot ahead of P1 every ~90 frames, skate there and wait spot_t=spot_t-1 if ph~="track" then if spot_t<=0 or (p2move and math.abs(p2spot[1]-x2)+math.abs(p2spot[2]-y2)<16) then spot_t=90 p2spot={math.max(-100,math.min(100,x1+math.random(-90,90))), math.max(-200,math.min(200,y1+dirY*math.random(40,110)))} end if own==c2 then steer(2,0,dirY*50) else steer(2,p2spot[1]-x2,p2spot[2]-y2,8) end else clr(2) end if ph=="chase" then K[1].B:clear_value() if own==c1 and c1~=5 and c1~=11 then ph="carry"; t=0 elseif own==c2 then steer(1,0,dirY*50) else steer(1,px-x1,py-y1) end elseif ph=="carry" then t=t+1 if own~=c1 then ph="chase"; return end local d=math.sqrt((x2-x1)^2+(y2-y1)^2) if t>15 and d>35 and d<170 then ph="aim"; t=0; AIMJ=(jit>0) and math.random(-jit,jit) or nil else steer(1,0,dirY*50) end if t>120 then ph="chase" end elseif ph=="aim" then t=t+1; steer(1,x2-x1,y2-y1) if t==4 then K[1].B:set_value(1) end if t==7 then K[1].B:clear_value(); AIMJ=nil; ph="track"; t=0; passes=passes+1 cur={fr=fr,p1=c1,p2=c2,d=math.floor(math.sqrt((x2-x1)^2+(y2-y1)^2))} end elseif ph=="track" then t=t+1; clr(1) if t==3 then cur.tgt=w(0xffbee0) end local dd=math.sqrt((px-x2)^2+(py-y2)^2); if not cur.md or dd=0 and own<12 and t>3) or t>75 then local who if own==cur.p2 then who="P2" elseif own>=0 and own<=5 then who="CPUmate" elseif own>=6 and own<=11 then who="OPP" else who="loose" end if own==cur.p1 then who="P1self" end stats.n=stats.n+1 if cur.tgt==cur.p2 then stats.tgtP2=stats.tgtP2+1 end if who=="P2" then stats.recvP2=stats.recvP2+1 elseif who=="CPUmate" then stats.recvCPU=stats.recvCPU+1 elseif who=="OPP" then stats.recvOpp=stats.recvOpp+1 else stats.loose=stats.loose+1 end lf:write(string.format("pass %d fr=%d p1=%d p2=%d dist=%d target=%d result=%s t=%d mind=%d at=%d\n",passes,cur.fr,cur.p1,cur.p2,cur.d,cur.tgt or -9,who,t,math.floor(cur.md or -1),cur.mdt or -1)); lf:flush() ph="chase" if passes>=maxp then lf:write(string.format("SUMMARY n=%d targetP2=%d recvP2=%d recvCPU=%d recvOpp=%d loose=%d\n",stats.n,stats.tgtP2,stats.recvP2,stats.recvCPU,stats.recvOpp,stats.loose)); lf:close() manager.machine:exit() end end end end)