A 2048 clone with two boards played in parallel. Use arrow keys or swipe to move tiles and press space or tap to restart. The whole game ends if it ends on either board.
for(_=");FT(FE},``on_,y,C^b[m(LLx^KK,J5,W8,V,fUe=Q&&P)]OKON250-N*1;i--P!(,g,h,i)d('#),for(i=P(C=',touchif(J3O32Ji+1Oc.fill16.page0,|Math.random()e+J1OU+J2O+ --;)=e.es[0]_Li&1W2g<2Q3-eFg&1i=e,QfU=iFk=2;ky=4;yx=4;xLi^O=function(eU{b=[c.scale(Qa.width/40eO,s=-Wdc.textAlign='centerStylQeU+=58+k*14Vi?Rect(f-h/2:Text(hU,g)`T*!O,3O=,O=0+1.1)FF`mreturn e*4|f+k*|h*_keydowne.keyCode-37_movee.preventDefault(Frstartpendr?(x=rX-pX)*x>(y=rY-pY)*y?x>0?2:0:y>0?3:1:-Wr=0`setInterval(e=-5){128;ir=S=s=A=b[i]=0;EE}h=!(C>>2)P!s){A=2+C&3;xNPPN-||(N?N===-4,S+=1<<++N):N=,J1+C%2O=(C<2?:-)*(x++-i)))=h=1)FF}h){E{h=2;Cs|=N>1h|=!N||xPN==Lx-1^O;s|=!h}}{k)fed,ba911W4Vfed11,S44,s?'Game over':'║2048'Fba96V2V1,1F{g=A,QxU=y,cbaQe*+2f=f*+,2V28F2;i+=<0?4:-4F*=.7;N)d('rgb('+[25W8]+') ,i=28-*2,i4 1V1<<N)}}k`20)";g=/[-N-QU-WJ-L^-`EF]/.exec(_);)with(_.split(g))_=join(shift());eval(_)
Zm9yKF89Iik7RlQoRkV9LGBgb25fLHksQ15iW20oTEx4XktLLEo1LFc4LFYsZlVlPVEmJlApXU9LT04yNTAtTioxHztpLS1QISgeLGcsaCxpKR1kKCcjHCksHBtmb3IoGhppPRlQKBhDPRcnLBZ0b3VjaBVpZigUSjNPEzMyEkppKzFPEWMuZmlsbBAxNg8ucGFnZQ4wLAx8TWF0aC5yYW5kb20oKQsWZStKMU9VK0oyTysJLS07KQg9ZS4VZXNbMF1fFQdMDGkmMVcyBmc8MhhRMy1lRmcmMRhpPWUsUWZVPWlGBRprPTI7awgEGnk9NDt5CBp4PTQ7eAgDTGleTwI9ZnVuY3Rpb24oZVUdewFiPVtjLnNjYWxlKFFhLndpZHRoLzQwDGVPLBdzPS1XZAFjLnRleHRBbGlnbj0nY2VudGVyFhBTdHlsUWVVKz01OCtrKjE0Vmk/EFJlY3QoZi1oLzIdOhBUZXh0KGhVLGcpYFQBBBkPCyoPHiEGTxgGLDNPPQ8sBk89MAsrMS4xKUZGYG0BBXJldHVybiBlKjR8ZitrKg98aCoSX2tleWRvd24BF2Uua2V5Q29kZS0zN18VbW92ZQFlLnByZXZlbnREZWZhdWx0KEZyB3N0YXJ0AXAHZW5kARdyPyh4PXIOWC1wDlgpKng+KHk9cg5ZLXAOWSkqeT94PjA/MjowOnk+MD8zOjE6LVdyPTBgc2V0SW50ZXJ2YWwoZQEUFz0tNSl7GTEyODtpCHI9Uz1zPUE9YltpXT0wO0VFfRRoPQwhKEM+PjIpUCFzKXtBPTIrQyYzOwQDGXgeTlACUE4tAnx8KE4/Tj09AhgTPS00LFMrPTE8PCsrTik6AhhOPQIsSjErQyUyTz0oQzwyPxI6LRIpKih4KystaSkpKRgCPQxoPTEpRkZ9FGgpe0UEexpoPQwXMjtDCANzfD1OPjEMaHw9IU58fHhQTj09THgtMV5PO3N8PSFofX0EexRrKRxmZWQWDAwPDywPDxtiYTkWMTEMVzRWDxtmZWQWMTEMDyxTGzQSFjQMDyxzPydHYW1lIG92ZXInOifilZEyMDQ4J0YcYmE5FjZWMlYxEiwxEkYDe2c9F0EsUXhVPXksBRxjYmEWUWUqEisyDGY9ZioSKxIsMlYyOEYZMjtpCBErPREYETwwPzQ6LTRGEyo9Ljc7FE4pZCgncmdiKCcrWzI1DB9XHzhdKycpCRMsaT0yOC0TKjIsaRs0EgkxVjE8PE4pfX0Xa2AyMCkiO2c9L1sBLR9OLVFVLVdKLUxeLWBFRl0vLmV4ZWMoXyk7KXdpdGgoXy5zcGxpdChnKSlfPWpvaW4oc2hpZnQoKSk7ZXZhbChfKQ==
// _________________________________________________________________
// / __ __ __ __ __ __ __ __ /
// / /_/ /_/ /_/ /_/ / / /_ / __/ / / /_/ /_/ /
// / / / / / \ / / /_ /_ /_ /_ /_ /_/ / /_/ /
// /________________________________________________________________/
// | |
// | This is a clone of the popular game 2048 and your goal is to |
// | merge tiles with the same number to eventually create a 2048 |
// | tile. In contrast to the original you play on two boards |
// | simultaneously - the game ends if you create a 2048 tile or |
// | there are no more possible moves on either board. |
// |________________________________________________________________|
// | |
// | Controls |
// | |
// | * Use the arrow keys or swipe to move the tiles. |
// | * Press space or tap anywhere to start a new game. |
// |________________________________________________________________|
// | |
// | Some Programming Details |
// | |
// | Compression: |
// | |
// | Done with RegPack (http://siorki.github.io/regPack.html) |
// | by @Siorki. To make it more effective only characters that |
// | occur in strings and API calls have been used for variable |
// | names. |
// | |
// | Character Usage: |
// | |
// | in program: A C D G I M R S T |
// | a b c d e f g h i k l m n o p r s t u v w x y |
// | variables: A C M S T |
// | a b c d e f g h i k m r s x y |
// | |
// | Board Layout: |
// | |
// | layer | # | first | second | |
// | ------+---+--------+--------+ |
// | tiles | 0 | 0 | 16 | |
// | anim x| 1 | 32 | 48 | |
// | anim y| 2 | 64 | 80 | |
// | anim z| 3 | 96 | 112 | |
// | |
// | Some Optimizations Used: |
// | |
// | * Used same variables for similar task to improve compression |
// | * Used one algorithm for iterating over the board by mapping |
// | coordinates differently depending on the current view |
// | * Read touches in ontouchmove instead of changedTouches in |
// | ontouchend |
// | * Chose scale to avoid setting font size |
// | * All functions have the same signature for compression |
// |________________________________________________________________|
// setup board and scale graphics
board = [c.scale(e = a.width / 400, e)],
// make sure that game gets initialized
key = stop = -5,
// Draw a rectangle or text.
// Params:
// e - color
// f - x coordinate
// g - y coordinate
// h - width of rectangle or text
// i - height of rectangle or 0 to draw text
draw = function(e, f, g, h, i) {
c.textAlign = 'center',
c.fillStyle = e,
f += 58 + k * 148,
i ? c.fillRect(f - h / 2, g, h, i)
: c.fillText(h, f, g)
},
// Add up to two 2 or 4 tiles to random free spots.
// Params: none
addTile = function(e, f, g, h, i) {
for(k = 2; k--;)
for(i = 16 | Math.random() * 16; i-- && !(
!board[map(0, i & 15, 2)] && (
// start animation
board[map(0, i & 15, 2, 3)] = 16,
// set number
board[map(0, i & 15, 2)] = 0 | Math.random() + 1.1
)
););
},
// Map view coordinates to board and select board and layer.
// Precondition: set k to select the board (0/1)
// Params:
// e - x coordinate
// f - y coordinate
// g - direction (0..3)
// h - layer
map = function(e, f, g, h, i) {
// invert x coordinate
g < 2 && (e = 3 - e);
// swap x and y
g & 1 && (i = e, e = f, f = i);
// combine coordinates, board, and layer information
return e * 4 | f + k * 16 | h * 32
},
// Set up controls
onkeydown = function(e, f, g, h, i) { key = e.keyCode - 37 },
ontouchmove = function(e, f, g, h, i) { e.preventDefault(); r = e.touches[0] },
ontouchstart = function(e, f, g, h, i) { p = e.touches[0] },
ontouchend = function(e, f, g, h, i) {
// map swipe directions to key directions
key = r ? (x = r.pageX - p.pageX) * x > (y = r.pageY - p.pageY) * y
? x > 0 ? 2 : 0
: y > 0 ? 3 : 1
: -5,
r = 0
},
setInterval(e = function(e, f, g, h, i) {
// (re-)start
if(key == -5) {
for(i = 128; i--;)
r = score = stop = lastAction = board[i] = 0;
addTile();
addTile();
}
// movement
if(h = 0, !(key >> 2) && !stop) {
// remember last action for animations
lastAction = 2 + key & 3;
// for each board
for(k = 2; k--;)
// for each row
for(y = 4; y--;)
// for each destination column
for(x = 4; x--;)
// for each source column
for(i = x; i-- &&
// skip to next destination if source and destination have different numbers
!(
board[map(x, y, key)] && board[map(i, y, key)] &&
board[map(x, y, key)] - board[map(i, y, key)] || (
board[map(x, y, key)]
// merge tiles
? board[map(x, y, key)] == board[map(i, y, key)] && (
// start "bump" animation
board[map(x, y, key, 3)] = -4,
// increase destination tile (values are stored as x instead of 2^x)
// and score by value of merged tiles
score += 1 << ++board[map(x, y, key)]
)
// move tile to empty destination
: board[map(i, y, key)] && (
// move tile
board[map(x, y, key)] = board[map(i, y, key)],
// start movement animation and check destination again because
// the moved tile could be merged
board[map(x, y, key, 1 + key % 2)] = (key<2 ? 32 : -32) * (x++ - i)
)
) && (
// remove moved tile
board[map(i, y, key)] = 0,
// remember that we have moved a tile
h = 1
)
);
);
}
// check whether the game is over
if(h) {
// add random numbers
addTile();
// check if any move possible
for(k = 2; k--;) {
// for up/down and left/right orientation
for(h = 0, key = 2; key--;)
// for each row
for(y = 4; y--;)
// for each destination column
for(x = 4; x--;)
// we won when a 2048 is reached
stop |= board[map(x, y, key)] > 10,
// otherwise a move is possible if one square is empty
h |= !board[map(x, y, key)] ||
// ... or the tiles match
x && board[map(x, y, key)] == board[map(x - 1, y, key)];
// check for 2048 tile
stop |= !h
}
}
// draw each board
for(k = 2; k--;) {
// UI unrelated to a board
if(k)
// background
draw('#fed', 0, 0, 1616, 1616),
// score box
draw('#ba9', 110, 5, 48, 16),
draw('#fed', 110, 16, score),
draw('#432', 40, 16, stop ? 'Game over' : '║2048');
// board background
draw('#ba9', 68, 28, 132, 132);
// board
for(y = 4; y--;)
for(x = 4; x--;) {
// abuse key and g variables for better compression
g = key = lastAction,
// map view to avoid drawing still tiles over moving ones
e = x,
f = y,
g < 2 && (e = 3 - e);
g & 1 && (i = e, e = f, f = i);
// empty square background
draw('#cba', e = e * 32 + 20, f = f * 32 + 32, 28, 28);
// advance movement animation
for(i = 2; i--;)
board[map(x, y, key, i + 1)] += board[map(x, y, key, i + 1)] &&
(board[map(x, y, key, i + 1)] < 0 ? 4 : -4);
// advance "bump" and tile creation animations
board[map(x, y, key, 3)] *= .7;
if(board[map(x, y, key)])
// draw tile background
draw('rgb(' + [250, 250 - board[map(x, y, key)] * 15, 250 - board[map(x, y, key)] * 18] + ')',
e + board[map(x, y, key, 1)],
f + board[map(x, y, key, 2)] + board[map(x, y, key, 3)],
i = 28 - board[map(x, y, key, 3)] * 2, i),
// draw tile value
draw('#432',
e + board[map(x, y, key, 1)],
f + board[map(x, y, key, 2)] + 18,
1 << board[map(x, y, key)])
}
}
key = k
}, 20)