Elemental Master: juego en menos de 1K de Javascript

Tenía algunas horas libres y decidí codificar un juego agradable para la nueva edición del concurso JS1K, para el año 2016 el tema fue Elementos. Así que codifiqué Elemental Master.
La versión corregida puede jugarse aquí (post-concurso): Elemental Master.
El objetivo del juego es atrapar los átomos amarillos y evitar los rojos mientras se mueve en una pista rápida parecida a un tunel, inspirado por juegos vectoriales antiguos.
Como siempre, trabajé duro en este juego, desafortunadamente metí un error en el último paso de optimización haciendo que no fuera posible jugarlo en teléfonos móviles (olvidé cambiar width a W). Es muy probable que esto me impidiera entrar en el top 10. (más reciente descubrí que sólo funciona bien en iPhones y que es muy lento en Android)
La forma de uso es sencilla: utilice las flechas para moverse a la izquierda y derecha, y en teléfonos móviles oprima sobre la mitad izq. de la pantalla para ir a la izquierda, y sobre la mitad der. para ir a la derecha.

Código fuente

Note que el JS1K "provee" de algunas variables cargadas con datos importantes, similar a esto:
<canvas id="c">
</canvas><script>
var c=document.getElementById("c");
var a=c.getContext("2d");
</script>
<script src="elemental-master.js">
</script>
Aquí está mi código fuente original (y corregido). Note que el código fuente está comprimido para el JS1K usando el compresor JS de @aivopass disponible en http://www.iteral.com/jscrush/

        // Elemental master (Mar/13/2016) by Oscar Toledo G. http://nanochess.org/
        // Catch the yellow atoms, don't touch the red ones.
        // Use keyboard or touch your screen.
        // Size
        W = a.width / 2,
        H = a.height / 2,
        Z = W > H ? H : W,
        // Coming elements, lifes / difficulty
        O = [{t:2,d:L=R=1}],
        // Time for next one
        Q = new Date().valueOf() + 1e3,
        // Score
        S = 0,
        // Start angle
        G = 4,
        // Keys
        K = [];
        // Compression of <canvas> function names
        for(M in c)c[M[0]+(M[6]||M[2])]=c[M];

        onkeydown = function(w){K[w.which-37] = 1};
        a.addEventListener("touchstart", function(w){K[w.touches[0].clientX<W?0:2]=1}, 0);

        setInterval(function(w){
                    with (Math) {
                    P = PI / 18;
                    with(c){
                    sv(),
                    // Clear canvas
                    fc(0, 0, W*2, H*2),
                    // Show score
                    strokeStyle = shadowColor = "#0f0",
                    shadowBlur = 5,
                    lineCap = "round",
                    sT("Score: " + S, 16, 16),
                    // Prepare rotated canvas
                    ta(W, H),
                    rt(G * P - P * 4.5),
                    sa(Z, Z * .8),
                    // Draw border
                    U = new Date().valueOf(),
                    strokeStyle = "#" + (999 - U / 250 % 900 | 0),
                    i = .3 + U / 2e3 % .1;
                    while (i < 1)
                    lineWidth = 4 * i / Z,
                    ba(),
                    ac(0, 0, i, 0, P * 36),
                    sr(),
                    i += i / 4;
                    // Draw perspective lines
                    ba(),
                    lineWidth = 2 / Z,
                    i = P * 27;
                    while (i < P * 46)
                    mv(0, 0),
                    ln(sin(i), cos(i)),
                    i += P * 2;
                    sr(),
                    // Move player
                    K[0] && G && --G,
                    K[2] && G < 9 && ++G,
                    K[0] = K[2] = 0,
                    // Draw atoms
                    O[0].x = G,
                    j = O.length;
                    while (j-- > (L > 3))
                    fillStyle = "#f00#ff0#08f".substr(O[j].t * 4, 4),
                    i = O[j].x * P * 2 + P * 27,
                    ba(),
                    ac(sin(i) / O[j].d, cos(i) / O[j].d, 25 / Z / (j ? O[j].d : L), 0, P * 36),
                    fl(),
                    j && (O[j].d -= (U - T) / 60) < 1 && (
                    O[j].x == G & L < 3.5 ? O[j].t ? S++ : (L += .5) : 0, O.splice(j, 1));

                    re(),
                    T = U,
                    // Add new atoms
                    Q < T &&
                    (Q = T + random() * (L < 3.5 ? 1e3 / R : 100),
                    O[O.length] = {x: random() * 10 | 0, d: 25, t: random() > .7 | 0},
                     R += 1e-4);
                    }}
                    }, 15);

Enlaces útiles

Última modificación: 28-ene-2017