C=64;M=Math.min;function K(x,y,r,u,v){y?a.beginPath()||a.fill(a.arc(x,y,r,u||0,v||7,0)):a.fillStyle='#'+'a000b4bbfff5'.substr(x,3)}function P(e){c.height=innerHeight;K(e?1:8);a[F='fillRect'](0,0,E=C*4,E);if(e){for(i=16;i--;)i-S&&a[I='putImageData'](D[X[i]],i%4*C,i/4<<6);if(S+1)a.globalAlpha=.5,a[F](x=O[0],y=O[1],C,C),a[I](D[T],x-5,y-5)}else{K(6);a[F](0,0,E,99);K(1);K(148,27,14);K(x=153,15,10,1,4);K(135,26,10);for(K(x,60,i=y=24);i+9;)K(183+i,74-i*i--/40,4);K(9);K(x,y,4,4);K(x,y,4,8,5);K(3);for(i=205;i--;a[F](128-j,i+10,j*2,1))j=i%49*(i%21)/y+i/2.4;K(0);for(i=18;--i;)K((C+i%6*i-3*i)*2,y+i*11,4);K(8);for(i=C;--i;K(k%E,(k*i)%E+1,2))k=i*i+A/3;A++}}P(A=0);X=[];for(D=[x=16];x--;X.splice(x*127%15,0,x))D[x]=a.getImageData(x%4*C,x/4<<6,62,62);P(R=S=-1);setInterval(c.onmousedown=c.onmousemove=b.onmouseup=function(e){if((f=e?e.type:P(R))&&R){for(i in O=[x=e.clientX,y=e.clientY])O[i]=M(5-M(32-O[i],0),192);z=x/C|y/C<<2;f[8]=='n'?T=X[S=z]:C;if(!f[8]&&S+1){x>>8|y>>8||(X[S]=X[z],X[z]=T);S=-1;for(i=16;--i&&X[i]==i;);R&=i}}},C)
// X-Mas Puzzle for JS1K by @veubeke
// See http://games.23inch.de/puzzle/ for (ugly) converter script
// Optimizations used:
// * Content
// * Interlaced colors in one string
// * Procedurally generated tree, decoration, falling snow
// * Syntax
// * Used 'a ? b : c' instead of 'if(a) b; else c'
// * Used 'a && b' and 'a || b' instead of 'if(a) b' and 'if(!a) b'
// * Use '|' instead of '||' and '|' instead of '+' (for different operator priority)
// * Used 'a,b,c' instead of '{a;b;c}'
// * Used 'for(a=b;a--)' instead of 'for(a=0;a<b;a++)' (or '--a' for 'a=1')
// * Used calls w/o parameter for other statements
// * ...
// * Didn't use ~ to check for -1 because opera fails under circumstances
// beyond my control
// * Other
// * Cached often used function (names)
// * Used every function for multiple purpoises
// Global variables:
// a - context, b - body, c - canvas
// C - tile size
// anim - animation step
// data - tile image data
// map - index to tile map
// running - whether the puzzle is still incomplete
// source - dragged tile source
// tile - dragged tile
C = 64; // tile size
mmin = Math.min;
// ark - draw a circle segment or set a color
// 1 parameter - set color
// 3-5 parameters - draw circle
//
// Interlaced colors
// a00 decoration
// 000 cat, border
// 0b4 tree
// bbf sky
// fff snow
// ff5 eye
function ark(x,y,r,u,v) {
y ? a.beginPath() || a.fill(a.arc(x,y,r,u||0,v||7,0))
: a.fillStyle = '#'+'a000b4bbfff5'.substr(x,3)
}
// paint - paint image or rearrange tiles
function paint(e) {
// Reset canvas
c.height = innerHeight;
// Paint snow or tile background/borders
ark(e?1:8);
a[frect='fillRect'](0,0,size=C*4,size);
// Rearrange tiles
if(e) {
// Paste placed tiles
for(i=16; i--;)
i-source && a[pimd='putImageData'](data[map[i]], i%4*C, i/4<<6);
// Paste dragged tile if currently dragging
if(source+1)
// Shadow
a.globalAlpha = .5,
a[frect](x=mousep[0], y=mousep[1], C, C),
// Tile
a[pimd](data[tile], x-5, y-5)
}
// Paint image / animation
else {
// Sky
ark(6);
a[frect](0,0,size,99);
// Cat
ark(1);
// Head
ark(148,27,14);
// Ears
ark(x=153,15,10,1,4);
ark(135,26,10);
// Body and tail
for(ark(x,60,i=y=24); i+9;)
ark(183+i,74-i*i--/40,4);
// Eye
ark(9);
ark(x,y,4,4);
ark(x,y,4,8,5);
// Tree
ark(3);
for(i=205; i--; a[frect](128-j,i+10,j*2,1))
j = i%49*(i%21)/y+i/2.4;
// Globes
ark(0);
for(i=18; --i;)
ark((C+i%6*i-3*i)*2, y+i*11, 4);
// Snow (will fall if painted repeatedly)
ark(8);
for(i=C; --i; ark(k%size, (k*i)%size+1,2))
k = i*i+anim/3;
anim++
}
}
// Paint initial image
paint(anim=0);
// Create and 'randomize' tiles
map = [];
for(data=[x=16]; x--; map.splice(x*127%15,0,x))
data[x] = a.getImageData(x%4*C,x/4<<6,62,62);
// Draw tiles
paint(running=source=-1);
// Add event handlers and start animation loop
setInterval(
c.onmousedown = c.onmousemove = b.onmouseup = function(e) {
// Paint tiles / animation when not in an event handler.
// Check for e.type is necessary for firefox as it adds a latency parameter
if((f = e ? e.type : paint(running)) && running) {
// Handle dragging
for(i in mousep=[x=e.clientX, y=e.clientY])
mousep[i] = mmin(5-mmin(32-mousep[i], 0), 192);
// Get tile
z = x/C | y/C<<2;
// Handle grabbing
f[8]=='n' ? tile = map[source=z] : C;
// Handle drop
if(!f[8] && source+1) { // try ~source here in opera and have fun :D
// Check boundaries
x>>8 | y>>8 || (
// Switch tiles
map[source] = map[z],
map[z] = tile);
// Reset
source = -1;
// Check if finished. We can stop at i==0
// since 1 piece can't be in the wrong place.
for(i=16; --i && map[i]==i;);
running &= i
}
}
}, C)