I was looking for some Javascript contest the other day and found this awesome JS1K contest http://js1k.com/2011-dysentery/. This contest basically challenges people to write cool games with no more than 1024 bytes.
I decided to call in sick and made the day a hacking day for me. And I came up with this pool game in which users have to aim and shoot the cue ball against the walls to hit a red target ball without touching other balls. My entry: http://js1k.com/2011-dysentery/demo/946
I first wrote a full version of the game, and along the way optimizing, and reducing the code to be smaller and smaller, I stripped off ‘nice-to-have’ features without making the game too boring. It took me around 8 hours from sketching out the game on paper to submitting the entry to the contest. The most fun part is when I have to look for ways to optimize the code into a definitely unreadable version.
Original code:
/* GAME: Tactical 8-ball Pool -------------------------- Author: Totti Anh Nguyen (http://iamtotti.com) Controls: - Move mouse to aim. - Left mouse click to shoot. */ for(p in a) a[p[d = o = V = W = 0]+(p[6]||'')]=a[p]; u=50; // unit X=w=c.width=667; h=c.height=377; v=w/2; // half width (center) P=v-10; Q=v+10; G=8.5; D=70; // max speed z='#'; // level : b M=Math; p=M.abs; // Table boundaries R = 621; // Left = Top T = 57; B = 331; setInterval(function(){ // we'll need the context a lot with(a){ A=function(c,x,y,r,_){ ba(); // a is the abbreviated form of arc a(x,y,r,0,7,0); (_&&s())||(strokeStyle=fillStyle=z+'900fff000'.substr(c*3,3),f()); }; C=function(c,x,y,w,h){fillStyle=z+c;fc(x,y,w,h)}; Y=function(q,w,e,r,t){ba();m(q,w);l(e,r);l(t,w);s();}; // draw the pool table C('4c1f22',0, 0, w, h); C('0e617f',32, 32, 603, 313); // Table Inner C('07516c',43, 43, 581, 291); // Table Shadow C(r='139ebd',46, 46, 575, 285); lineWidth=30; lineJoin="bevel"; strokeStyle = '#'+r; // Draw top-center gate Y(P,t=67,v,37,Q); // Draw bottom-center gate Y(P,306,v,343,Q); // Draw top-left gates Y(61,64,35,35,t); // Draw bottom-left gates Y(61,313,35,341,70); // Draw top-right gates Y(600,62,631,37,607); // Draw bottom-right gates Y(601,313,631,341,606); // Draw holes H = 18; // Two on the left A(2,35,35,H); A(2,35,342,H); // Two on the right A(2,632,35, H); A(2,632,342, H); // Two in the middle O=14.5; A(2,v, 28, O); A(2,v, 349, O); // Draw Score font = "90px Arial"; fillStyle = g = "rgba(0,0,0,0.5)"; fx(o, v, h/2); if(X){ X = 0; // Init E balls for (E = [], x=b=0; b < 9 && b < o+2; ++b) { x += 60; y = M.random()*x + 20; y=y>B?B-9:y<T?T+9:y; x=x>R?R-9:x<T?T+9:x; E.push({x:x,y:y}); } } else { // Falling to Holes detection if ((x < t || x > 611 || (x > P && x < Q+6)) && (y < t || y > 321)) X = 1, d = o = 0; // Ball collision detection for(i = 0; i < b-1; ++i) { r = E[i]; // (x > r.x - 13) && (x < r.x + 13) && (y > r.y - 13) && (y < r.y + 13)){ if (p(x-r.x) < 13 && p(y-r.y) < 13){ o = i ? 0 : o+1; X = 1; d = 0; } // Draw E ball A(i ? 2 : 0, r.x-6, r.y-6, G); } } // ~~ Math.floor d -= M.max(~~(d/10), .2); // Draw white ball A(1, x-6, y-6, X ? 7 : G); // If balls moving if (d > 0) { // Draw running balls if (K) x += F ? d : -d, y = k * x + n; else y += F ? d : -d, x = (y - n) / k; // Check boundaries // Bottom if (y > B) N(0,B); // Top else if (y < T) N(0,T); // Right else if (x > R) N(R); // Left else if (x < T) N(T); } else // Draw the aiming lines to shot if (V > T && V < R && W > T && W < B) lineWidth=1, // Expected ball fillStyle = g, A(3, V-6, W-6, G, 1), // Direction line m(V-6, W-6), l(x-6, y-6), s(); } }, u); /** * Reflect ball against wall */ function N(i,j){ // Update line scope // Reflection happens // Reset starting position Z = i ? (U=I, j = k*i + n, 2*j - J) : (i = (j - n) / k, U = 2*i - I, J); S(I = x = i, J = y = j); } function S(x, y) { // Figure out where to go ? k = (y - Z) / (x - U); n = y - k*x; F = (K = p(U-x) > p(Z-y)) ? U > x : Z > y; } onmouseup=function(e){ if (d <= 0) // Power d = e.altKey ? D : D/2, // Target U = e.pageX, Z = e.pageY, // Compute line scope S(I = x, J = y); } // Track mouse position when aiming onmousemove=function(e){ V = e.pageX; W = e.pageY; }
And this is the final version of 1024 bytes:
for(p in a)a[p[b=d=o=0]+(p[6]||'')]=a[p];w=c.width=550;h=c.height=310;M=Math;p=M.abs;R=522;t=63;T=43;V=W=B=282;setInterval(function(){with(a){A=function(c,y,x,r){ba();c>9?stroke(m(c,x),l(y,r)):(strokeStyle=fillStyle='#'+'9000fff3119b'.substr(c,3),c>5?fc(x,x,y,r):f(a(X=x||X,y,G=r||G,0,7,0)))};A(7,w,0,h);A(9,487,32,246);A(1,u,u,18);A(1,v=w/2);A(1,u,q=516);A(1,v);A(1,28,v,O=15);A(1,B);if(!b)for(E=[],x=0;b<o+2&&b<8;++b)x+=t,y=M.random()*h,y=y>B?B-9:y<T?T:y,E[b]={x:x,y:y};for(i=0;i<b-1;++i)r=E[i],p(x-r.x)<O&&p(y-r.y)<O?(o=i?0:o+1,b=d=0):A(i?0:2,r.y-7,r.x-7,8);(x<t||x>q||(x>268&&x<288))&&(y<t||y>274)&&d>0?(b=d=o=0):y>B?N(0,B):y<T?N(0,T):x>R?N(R):x<T?N(T):0;d-=M.max(~~(d/9),.2);A(4,g=y-7,z=x-7);fx(o,v,t);d>0?(K?(x+=F?d:-d,y=k*x+n):(y+=F?d:-d,x=(y-n)/k)):A(C=V-7,z,D=W-7,g,A(4,D,C))}},u=35);function N(s,e){Z=s?(U=I,e=k*s+n,2*e-J):(s=(e-n)/k,U=2*s-I,J);S(x=s,y=e)}function S(){I=x,J=y,k=(y-Z)/(x-U),n=y-k*x,F=(K=p(U-x)>p(Z-y))?U>x:Z>y}onmousemove=function(j){V=j.pageX;W=j.pageY};onmouseup=function(){d<=0&&S(d=u,U=V,Z=W)}
The entry did not make it to the top 10 list but this is really a good fresh hacking day on Javascript for me. I learned a lot, and I would recommend attend such an event as you never have a chance to hack out such an insane code at work :)
Cheers,