06 Drift

No gun, just momentum. Coast around a wraparound screen collecting sparks, but every spark you grab births another rock.

Controls A/D or arrows rotate, W/Up/Space thrust. Touch: hold to steer toward your finger and thrust

1000 / 1024 bytes · 24 to spare

The 1000 bytes that ship
for(_='t[Ze.j),q()zRz,~,16V=>{U=(Lh.K))J*S(J-`Text(_n[0][^&&key,Sz-.5=.99*(jtype,o,f,iL)=>"#ff(u[3let c.fill,"Rect(Kcos(i180320c.strokeKhypot(]|u[Ksin(0,	;for(tZ0],Z1]Style==*joffset/a.clientWidth,=onpointerlocalStoragjdrift1kt,e,l,n,r,s,y,d,p,k,T,h=Math,S=Krandom,u=[],m=+||	R(k=`e,(T=`l)<70~g(~n.push([k,T]J,x{e=16	l=9	o=f=i=r=s=y=	n=[]=7;t--;)gz;^2]=^3]=0};xz,s=1,ondown=onupmovedownup=eUu[jCode]=<"u",dXpYt=jbuttons,/n$/.test()sy>30xz},setInterval(zUif(y++"#0b0b1066		32	q!s){i+=t?.15*Katan2(p-l,d-e)-i):.08*(968])-765]J;n=.09*88732]|t);oo+)*nqff+i)*nqeLe+o+)%32	lLl+f+)%}n.map((tLo?7:3JUL+Z2]+)%LZ1]+Z3]+)%18	o?4f6d":d23f-i-i,2*i,2*iq!s-e-l)<i+3(o?(s=1,y=	=m=Kmax(m,rJ:(r++,~=k=T,g(J)}q9f3f",c.beginPathz of[	2.5,-2.5,0])c.lineTo(e+8*+tql+8*i+tJ;z"#e9e7df",s_"DRIFT · TAP TO START"V	64)_r+"  HI "+mV	20)}V)';G=/[-^-`J-LUV~zqjZ]/.exec(_);)with(_.split(G))_=join(shift());eval(_)
Readable source
// DRIFT: coast, collect sparks, dodge rocks. Each spark births a rock.
let M = Math, R = M.random, K = [], H, X, Y, U, V, A, rocks, score, dead, t, tx, ty, p, q,
  best = +localStorage.drift1k || 0;

// pick a point at least 70px from the ship
let far = () => M.hypot((p = R() * 320) - X, (q = R() * 180) - Y) < 70 && far();
// rocks[0] is the spark (still); the rest are drifting rocks
let rock = () => (far(), rocks.push([p, q, R() - .5, R() - .5]));

let start = () => {
  X = 160; Y = 90; U = V = A = score = dead = t = 0;
  rocks = [];
  for (let i = 7; i--;) rock();
  rocks[0][2] = rocks[0][3] = 0; // first one is the (still) spark
};
start(); dead = 1;

// one handler for keys and pointers
onkeydown = onkeyup = onpointermove = onpointerdown = onpointerup = e => {
  K[e.keyCode] = e.type < 'keyu';
  tx = e.offsetX * 320 / a.clientWidth; ty = e.offsetY * 320 / a.clientWidth;
  H = e.buttons;
  /n$/.test(e.type) && dead && t > 30 && start(); // keydown / pointerdown
};

setInterval(() => {
  t++;
  c.fillStyle = '#0b0b1066';
  c.fillRect(0, 0, 320, 180);
  if (!dead) {
    if (H) A += M.sin(M.atan2(ty - Y, tx - X) - A) * .15;
    else A += ((K[39] | K[68]) - (K[37] | K[65])) * .08;
    // thrust + drag
    let th = (K[38] | K[87] | K[32] | H) * .09;
    U = (U + M.cos(A) * th) * .99;
    V = (V + M.sin(A) * th) * .99;
    X = (X + U + 320) % 320;
    Y = (Y + V + 180) % 180;
  }
  // r = rock, i = index (0 is the spark), s = half-size. Not a map() default param: map passes the array 3rd
  rocks.map((r, i, _, s = i ? 7 : 3) => {
    r[0] = (r[0] + r[2] + 320) % 320;
    r[1] = (r[1] + r[3] + 180) % 180;
    c.fillStyle = i ? '#ff4f6d' : '#ffd23f';
    c.fillRect(r[0] - s, r[1] - s, s * 2, s * 2);
    if (!dead && M.hypot(r[0] - X, r[1] - Y) < s + 3)
      if (i) {
        dead = 1; t = 0;
        localStorage.drift1k = best = M.max(best, score);
      } else score++, far(), r[0] = p, r[1] = q, rock();
  });
  // ship: vector triangle
  c.strokeStyle = '#ff9f3f';
  c.beginPath();
  for (let d of [0, 2.5, -2.5, 0]) c.lineTo(X + M.cos(A + d) * 8, Y + M.sin(A + d) * 8);
  c.stroke();
  c.fillStyle = '#e9e7df';
  if (dead) c.fillText('DRIFT · TAP TO START', 160, 64);
  c.fillText(score + '  HI ' + best, 160, 20);
}, 16);