Skip to content

Instantly share code, notes, and snippets.

@veu
Last active August 29, 2015 13:57
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save veu/603535c10c8516de15cb to your computer and use it in GitHub Desktop.
Save veu/603535c10c8516de15cb to your computer and use it in GitHub Desktop.
2048 in 491 bytes of JavaScript and HTML.
<body onload="function M(c,d){for(i=H=16;i--;G|=p>>11)for(p=B[m=V(j=i%4+1)];--j?(q=B[n=V(j)])?p-q?0:h=c?0:B[p?S+=B[m]*=2:B[i++,m]=q,n]=d:L=localStorage:0;)R=Math.random}function V(x){if(x)return(D>1?4-x:x-1)<<D%2*2|i-i%4>>D%2*2;for(i=H|R()*H;B[--i%H];);B[i%H]=2<<R()+.1}(onkeyup=function(e){D=e?e.which-37:B=[];D>>2||M()|V(e?h:V());for(D=h=4;D--;)M(1);for(h=(G|h?S+' / '+(L.S>S?L.S:L.S=S):S)+'<table border>';H;P.innerHTML=h+='<th width=50 height=50>'+[B[H]])H--%4?0:h+='<tr>'})(S=G=0)"id=P>
@aemkei
Copy link

aemkei commented Apr 8, 2014

And somehow the Game-Over logic is broken. At least on Chrome / Mac here. But that's okay for me and in my opinion you could reduce the board even further by removing the score at all.

See: http://jsbin.com/MMXLVIII/5: That are 422 bytes - A beautiful 2k number!

@aemkei
Copy link

aemkei commented Apr 8, 2014

And remove the whitespace after the return to save another byte as @subzey suggested:

http://jsbin.com/MMXLVIII/6
https://twitter.com/subzey/status/453203401187553280

@xem
Copy link

xem commented Apr 8, 2014

Couldn't we replace Math.random with new Date%9 or new Date%H ?

@aemkei
Copy link

aemkei commented Apr 8, 2014

Had the same idea about Math.random but it is used twice here and will be shorter is stored in a var.

PS: This is a more "readable" version of the 421 version:

<body id=P onload="function M(c,d){for(i=H=16;i--
;)for(p=B[m=V(j=i%4+1)];--j?(q=B[n=V(j)])?!p|p==q
&&(h=c?0:B[p?B[m]*=2:B[i++,m]=q,n]=d):1:0;)R=Math
.random}function V(x){if(x)return(D>1?4-x:x-1)<<D
%2*2|i-i%4>>D%2*2;for(i=H|R(D=4)*H;B[--i%H];);for
(B[i%H]=h=2<<R()+.1;D--;)M(1)}(onkeyup=function(e
){D=e?e.keyCode-37:B=[];M()|V(e?h:V());t='<pre>';
for(i=h=H;i--;P.innerHTML=t)t+=('    '+[B[i]]).
slice(-4)+(i%4?'|':'|\n')})()">

@veu
Copy link
Author

veu commented Apr 8, 2014

@aemkei What about the game over is broken?

@xem It would work but the first two generated tiles would always have the same number and be next to each other because they're generated too fast for the date to change.

PS: Replaced keydown with keyup and removed the space after return.

@xem
Copy link

xem commented Apr 8, 2014

Alright. Good work then!
BTW, I made this 446b variation freturing a table instead of ASCII ART:

<body id=P onload="function M(c,d){for(i=H=16;i--;)for(p=B[m=V(j=i%4+1)];--j?(q=B[n=V(j)])?!p|p==q&&(h=c?0:B[p?B[m]*=2:B[i++,m]=q,n]=d):1:0;)R=Math.random}function V(x){if(x)return(D>1?4-x:x-1)<<D%2*2|i-i%4>>D%2*2;for(i=H|R(D=4)*H;B[--i%H];);for(B[i%H]=h=2<<R()+.1;D--;)M(1)}(onkeyup=function(e){D=e?e.keyCode-37:B=[];M()|V(e?h:V());t='<table border>';for(i=h=H;i--;P.innerHTML=t)t+=(!(i%4-3)?'<tr>':'')+'<th width=50 height=50>'+(B[i]||'')})()">

It's worth 25b, isn't it?

http://jsfiddle.net/Hag22/

@veu
Copy link
Author

veu commented Apr 8, 2014

You mean 16b, right? ;)

<body onload="function M(c,d){for(i=H=16;i--;)for(p=B[m=V(j=i%4+1)];--j?(q=B[n=V(j)])?!p|p==q&&(h=c?0:B[p?B[m]*=2:B[i++,m]=q,n]=d):1:0;)R=Math.random}function V(x){if(x)return(D>1?4-x:x-1)<<D%2*2|i-i%4>>D%2*2;for(i=H|R(D=4)*H;B[--i%H];);for(B[i%H]=h=2<<R()+.1;D--;)M(1)}(onkeyup=function(e,t){D=e?e.keyCode-37:B=[];M()|V(e?h:V());for(i=h=H;i--;)P.innerHTML=t=(i%4-3?t:[t]+'<tr>')+'<th width=50 height=50>'+[B[i]]})()"><table border id=P>

@subzey
Copy link

subzey commented Apr 8, 2014

I'm not too sure, but it seems,

p==q&&(h=c?0:/*…*/)

might be replaced with

p==q?h=c?0:/*…*/:0

That's 1 byte less

@subzey
Copy link

subzey commented Apr 8, 2014

<body id=P onload="…"> to <body onload="…"id=P> yields 1 more byte
Also, could we possibly rely on tabstops (in ASCII version)? t+=[B[i]]+(i%4?'\t|':'\n') is much shorter.

@veu
Copy link
Author

veu commented Apr 8, 2014

@subzey The first two optimizations are great but tabs look weird because they're 8 spaces and the numbers would be left-aligned.

@veu
Copy link
Author

veu commented Apr 8, 2014

Updated with xem's table version, subzey's optimizations and a few more optimizations of my own. It's now 507 bytes.

@veu
Copy link
Author

veu commented Apr 8, 2014

@aemkei

If you're really only interested in raw mechanics and don't care how ugly it looks you can get it down to 357b.

<body onload="function V(x){if(x)return(D>1?4-x:x-1)<<D%2*2|i-i%4>>D%2*2;for(i=H|R()*H;B[--i%H];);B[i%H]=2<<R()+.1}(onkeyup=function(e,d){D=e?e.keyCode-37:B=[];for(i=k=H=16;i--;)for(p=B[m=V(j=i%4+1)];--j?(q=B[n=V(j)])?p&&p-q?0:h=B[p?B[m]*=2:B[i++,m]=q,n]=d:1:0;)R=Math.random;V(e?h:V());h='<pre>';for(;k--;)P.innerHTML=h+=[B[k]]+'\t|'+[k%4?d:'\n']})()"id=P>

Obviously this doesn't keep the score or detect game over either.

@aemkei
Copy link

aemkei commented Apr 8, 2014

@veu: That's what I'm looking for! Great golfing by using tabs and reordering the attributes!

You can save a ; by moving h='<pre>' into the for initialization: http://jsbin.com/MMXLVIII/8

@veu
Copy link
Author

veu commented Apr 8, 2014

The tabs were subzey's idea. :) I moved a couple more variables around to get it down to 353. http://jsbin.com/lugidoyo

@subzey
Copy link

subzey commented Apr 9, 2014

/*...*/:1:0;L=localStorage)R=Math.random}
/*...*/:L=localStorage:0;)R=Math.random} // -1 byte

As far as I understand, 1 here stands for something truthy, and LocalStorage is a good pick if supported by browser. If it's not supported, ReferenceError, just like it was before. I'm pretty sure this "if branch" would be activated at least once before gameover when L is needed.

@xem
Copy link

xem commented Apr 9, 2014

353...
boy, that escalated quickly. great golf guys!

@aemkei
Copy link

aemkei commented Apr 9, 2014

Great work guys!

But why not save two bytes by using .which instead of .keycode? That should work in all modern browsers: http://jsbin.com/MMXLVIII/12/ – 351 …

@aemkei
Copy link

aemkei commented Apr 9, 2014

Some ideas, but no time to try them:

  • e.which-37e.which%4
  • join the two functions
  • get rid of " by avoiding > like here

@subzey
Copy link

subzey commented Apr 9, 2014

I think, p&& check in p&&p-q is redundant.

If I understand right, p is taken from B array, whose values may be only positive integers or undefineds. Thus, the only "falsy" value is undefined that is coerced to NaN in p-q and whole condition becomes falsy.

@veu
Copy link
Author

veu commented Apr 9, 2014

@aemkei

  • .which may work, have to try later
  • Changing it to e.which%4 would require changing the mapping and also means that all keys can be used which is kind of ugly (at least for the fully featured version).
  • Joining the two functions is likely not worth it because of longer calls and bytes needed for if/else or if and return.
  • The quotes are only two bytes whereas replacing >> everywhere would likely generate many more bytes.

@subzey You're right about p&&p-q but I'll have to check whether that breaks assumptions made in the code that follows. Not sure whether I can use it.

@subzey
Copy link

subzey commented Apr 9, 2014

for(i=H;i--;/*...*/)~i%4?0:h+='<tr>'
for(i=H;i;/*...*/)i--%4?0:h+='<tr>' // -1 byte

Upd: There's more maniacal version:

for(;H;/*...*/)H--%4?0:h+='<tr>' // -4 bytes

Although global "constant" H is changed, it doesn't affect M() and V() as in M() its value is reverted to 16 and V() is always called after M().

@veu
Copy link
Author

veu commented Apr 9, 2014

Haha, that's crazy. It's now 499 bytes with everything.

EDIT: And here's the bare-bones version with all suggested optimizations at 346 bytes.

<body onload="function V(x){if(x)return(D%3?x-1:4-x)<<2-D%2*2|i/4<<D%2*2;for(h=H|R()*H;B[--h%H];);B[h%H]=2<<R()+.1}(onkeyup=function(e,d){D=e?e.which%4:B=[];for(i=H=16;i--;)for(p=B[m=V(j=i%4+1)];--j?(q=B[n=V(j)])?p-q?0:h=B[p?B[m]*=2:B[i++,m]=q,n]=d:R=Math.random:0;);for(V(e?h:V());H--;H%4?0:d+='\n')P.innerHTML=d=[d]+[B[H]]+'\t|'})()"><pre id=P>

@veu
Copy link
Author

veu commented Apr 9, 2014

@subzey I tried that before and it doesn't work. M doesn't get called after the game's over leading to an infinite loop over the now always negative H.

@subzey
Copy link

subzey commented Apr 9, 2014

@veu, I've removed D>>2|G|| part and the code seems to be working correctly: http://jsbin.com/vigohuso/1/

@veu
Copy link
Author

veu commented Apr 9, 2014

@subzey That works if you don't care that any pressed key moves tiles and the game continues after reaching 2048. Btw, I found a way to use your output loop optimization and it's now 496 bytes.

@subzey
Copy link

subzey commented Apr 9, 2014

Oh… Right, sorry.
Btw, original game doesn't end with 2048 tile, you may pick "continue playing" and try to get 4096 or 8192

@veu
Copy link
Author

veu commented Apr 9, 2014

In that case removing |G might actually be closer to the original as it would update the highscore when you've reached the 2048 tile but would let you keep playing.

@mathiasbynens
Copy link

Use oninput instead of onkeyup. It’s not shorter but it feels much faster.

@xem
Copy link

xem commented Jul 7, 2014

Let's golf this: http://243game.com/ :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment