Duas coisas sempre enchem a alma com nova e cada vez mais forte surpresa e reverência, quanto mais vezes pensamos nelas - esse é o céu estrelado acima de mim e a lei moral em mim. Immanuel Kant
JS1k é uma competição anual em que você precisa acomodar uma demonstração, um jogo ou qualquer coisa, em 1024 caracteres em JavaScript. Este ano, minha demo ficou em quarto lugar (até o terceiro não foram suficientes dois pontos). Você pode assistir à demonstração no site JS1k . Quem não abre ou não trabalha, deve ficar assim:
O código fonte minificado e completo encontra-se no github . E, sob o corte, há uma análise de como o JavaScript está sendo minificado agora para esses concursos.
Isenção de responsabilidade
A principal beleza da demo é um shader de fragmento de Pablo Roman Andrioli. Pablo é um artista que trabalha com fractais e, no fórum fractalforums, fornece alguns detalhes dos cálculos. Minha tarefa era empacotar um sombreador e um código WebGL de 1024 bytes.
Inicialização WebGL
O wrapper JS1k no início da demonstração fornece um contexto WebGL na variável global g . Apesar disso, trabalhar com o WebGL é muito detalhado. Por exemplo, para adicionar um sombreador de vértice a um programa, são necessários 159 caracteres:
Para resolver esse problema, todas as soluções JS1k dos últimos anos usam um truque com sinônimos de funções:
for(i in g){ g[i[0] + i[6]] = g[i]; }
O loop adiciona um sinônimo para cada função (e para qualquer membro) do contexto WebGL, que consiste na primeira e na 7 letras. Por exemplo, o programa certo torna-se cP , maior valor, etc. Além disso, ao enquadrar todo o código with(g)
(que não pode ser usada nesses projetos), obtemos:
with(g){ p=cP(); sS(s=cS(35633),'attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}'); ce(s); aS(p,s); }
Minificação de sombreador
O sombreador original ocupa 1100 caracteres. As principais abreviações: remoção de variáveis desnecessárias e combinação de fragmentos semelhantes. Afinal, passei o código pelo minificador online . Como resultado, restavam pouco mais de 500 bytes do sombreador.
JSCrush
JSCrush é o padrão de fato para compactar código em tais competições. O utilitário transforma o código aproximadamente na seguinte sequência:
_ = '(estilo i = ... _='(i a.style="widMj%;hEjvh;:left",g)g[i[0]+i[6]]=g[i];wiMO.u=g.G1f,x=y=k=g)p=cP(35633"tribute 2 p gl_Posit=4?FN"precis mediump ;G Zt,a,x,y Uf`ord.rg/64!-.f.=a;Zc=+xz,v=+yz;m2 m$cc-cc)s$vv-vv)fJf#Ur`Q,,r+`t*2.,t,-2.rJr#Zg=.1,b=Q;Ui`!Kl=Rl<2Rl++){Uo=r+f*;oQ)-mod(o,2.))Ze,n=e=!;Kd=Rd<2Rd++)oo)/dot(o,o)-3,n+o)-ee=oif(l>6)Q-max(!,.3-i+=b+g,g,g)*n*5*b;.73;g+=.1;}i=mix(i)i,.85lor=4(i*.01.lo?ug?bfO=34962,cB()eV(0vA(2,5120bDO,Tw Int8Array([|,|]35044o=,(Lt@-oa@TrHE/TrWidMx@xy@ydr(6,3requestAnimFrame(L)})(down=upk^=1},movek&&(xX,yY)};),3=funct(e){uOf?,"flo}@ce(saS?,slengM(onmouse ;void ma(){Tw De/1e5);incos(for=abs(gl_FragCo,1g*(sS(s=cS(n*n*.001at.5vecionb*=s(=e.page0,!0.#.r=s;$=m2(?(p@"EeightGunimJ.rm;K(t MthO(gQ1.R0;TneU Z `=j:100z/50!|-3';for(Y in $='|zj`ZUTRQOMKJGE@?$#! ')with(_.split($[Y]))_=join(pop());eval(_)
O princípio do JSCrush pode ser visualizado na ferramenta para conversão reversa de código . Ou leia em detalhes no artigo . Em geral, isso é codificado com um dicionário:
- Encontramos um caractere que não é usado no código
- Encontramos fragmentos repetidos no código que substituímos pelo caractere do primeiro parágrafo
- Substitua a sequência por um caractere
- Repita 1-3 até o resultado ser menor que o código-fonte.
Otimização
Depois de todas as operações, eu ainda tinha cerca de 30 caracteres, o que poderia ser usado para otimizar o desempenho. Ao carregar a demonstração, você pode especificar o tamanho da tela: tela cheia ou tamanho fixo. O lançamento em tela cheia é lindo, mas o sombreador de fragmento é chamado para cada pixel e funciona lentamente. A solução foi solicitar um tamanho fixo da tela JS1k (escolhi 640x640) e depois aumentá-lo para tela cheia no código:
a.style='width:100%;height:100vh;float:left';
Em seguida, a imagem ocupa a tela inteira, mas o sombreador é executado apenas para cada pixel do tamanho da tela original.