8 ms·
Snake game in a data: URI
- jacobwg 15y agoNice! Just wish there was some kind of score counter...
- TeMPOraL 15y agoSnake length is a score counter. :).
- shaggyfrog 15y agoShouldn't the snake speed up as it gets longer? I played for a while but didn't notice.
- citricsquid 15y agoit grew for me.
- biot 15y agoHere you go. Starts out at a slightly quicker pace and gets gradually more challenging as the length increases. data:text/html,<script>function P(x,y){return{x:x,y:y}}function J(){return Math.floor(Math.random()*16)}function R(){z=s[0];while(T(z)){z={x:J(),y:J()};}return z;}function A(a,b){return{x:a.x+b.x,y:a.y+b.y}}function Q(a,b){return a.x==b.x&&a.y==b.y}function O(p){return p.x<0||p.x>15||p.y<0||p.y>15}function T(p){return s.filter(Q.bind(this,p)).length}function M(p){if(p.x!=-d.x&&p.y!=-d.y)m=p}function B(l){s.map(function(p){c.fillRect(p.x<<3,p.y<<3,8,8)})}function C(p){c.beginPath();c.arc((p.x<<3)+4,(p.y<<3)+4,4,0,7);c.fill()}function F(){c.fillStyle='rgb(200,0,0)';B(s);clearInterval(t);setTimeout(I,5000);}window.addEventListener('keydown',function(e){M({37:P(-1,0),38:P(0,-1),39:P(1,0),40:P(0,1)}[e.keyCode])},false);function I(){document.body.innerHTML='<canvas width=128 height=128 style="width:256px;height:256px;border:8px solid black"></canvas>';ww=0;c=document.body.firstChild.getContext('2d');c.fillStyle='rgb(0,0,0)';s=[P(8,8),P(7,8),P(6,8)];a=R();m=P(1,0);v=function(){clearInterval(t);t=setInterval(v,350-s.length);d=m;n=A(s[0],d);(O(n)||T(n))&&F();s.unshift(n);Q(s[0],a)&&(a=R())||s.pop();c.clearRect(0,0,128,128);B(s);C(a);};t=setInterval(v,250);}setTimeout(I,0)</script> I also removed the annoying alert if you hit something. Edit: also updated to prevent new dot spawning under tail and turn the snake red when you die Clickable: http://preview.tinyurl.com/biotsnake http://preview.tinyurl.com/biotsnake
- bytex64 15y agoVery nice. I also see you got a few other tricks that I missed, like factoring out the random number generator. I am impressed.
- biot 15y agoThanks. I really like your use of map and filter to do the drawing and hit testing! Edit: oh, and please consider it a pull request. :)
- bytex64 15y agoSure thing. What was ww=0; trying to do? It seems to be the only place ww is referenced.
- biot 15y agoPrior to implementing the s.length-based speed increase, I tried implementing a funkier frame-based gradual increase using a log scale. Ultimately, that proved to be not worth the effort as speeding it up in proportion to how many pellets were eaten was a more natural fit. So that variable is a leftover and should have been deleted.
- bytex64 15y agoI've merged in your changes and made a few improvements, too. I streamlined the timeout handling so that it uses setTimeout instead of clearing and resetting setInterval. I also used color literals instead of CSS rgb(). I removed the reset to black in I() since recreating the canvas context does that automatically. Thanks, biot, you are officially awesome!
- biot 15y agoOf course! It's been a while since I've done much javascript and I had forgotten about setTimeout. And your do/while loop in the R() function is much cleaner than my hack. :) One thing I noticed is that you're setting the timeout value to 350-s.length*5. As the grid is 16x16, the maximum snake length will be 256. Multiplying the length by 5 will soon make the game humanly unplayable somewhere around a length of 60.
- decklin 15y agoTinyURL allows one to create http://tinyurl.com/7q3en34 http://tinyurl.com/7q3en34 -- but, alas, Chrome refuses to follow the redirect. Safari and Firefox don't seem to mind, though.
- nyrb 15y agoit works in Chrome after refresh.
- mkilling 15y agoThis is awesome. Please HN, give us more hacks like this instead of yet another "X in Pure CSS3/WebGL".
- gazrogers 15y agoFrom Codegolf.SE: http://codegolf.stackexchange.com/questions/1696/what-can-you-do-in-a-4k-data-uri http://codegolf.stackexchange.com/questions/1696/what-can-yo...
- AdamTReineke 15y agoNice job. One note: dots can spawn underneath the snake.
- badhairday 15y agoI was so confused thinking to myself "Where's the next dot?!?!"
- shubhamgoel 15y agoThis is insane
- daniel_iversen 15y agoIt would have been awesome seeing this being created.. Did he start out with a large version? Did he refactor the whole thing a lot? What functional compromises did he have to make and which were the most creative ways of drastically shortening it? No matter how useful or not it is for others, its an amazing feat, I'm sure his brain got working.., well done!
- bytex64 15y agoIf you're curious, the "readable" version is here: http://bytex64.net/code/datasnake/snake.html http://bytex64.net/code/datasnake/snake.html I started out writing it as straightforward as possible, then applied functional tricks to slim it down. That was a lot of fun. I'm not sure how to post a code snippet here, but I'll try to illustrate one of my favorite portions. In the original, I did key input with a switch statement. That got replaced by: { 37: P(-1,0), 38: P(0,-1), 39: P(1,0), 40: P(0,1) }[e.keyCode] "But wait!" you're thinking, "What happens when e.keyCode is less than 37 or more than 40?" What happens is it evaluates to undefined, and it errors out in the M() function, effectively short-circuiting it. If you look in your browser's error log, it'll spew an error every time you hit another key. It's lazy, but it works. :]
- VBprogrammer 15y agoLittle bug fix, apples can appear under the snake.. --- snake.html 2011-12-09 08:03:47.000000000 +0000 +++ snake.html.2 2011-12-09 11:40:54.414774460 +0000 @@ -59,7 +59,7 @@ var n = AP(s[0],d); (OB(n) || TC(n)) && fail(); s.unshift(n); - EQ(s[0],a) && (a = RP()) || s.pop(); + EQ(s[0],a) && (function() {do {a = RP()} while (TC(a))}()) || s.pop(); c.clearRect(0,0,128,128); blocks(s);
- VBprogrammer 15y agoWhoops, I see someone below beat me to it.
- InnocentB 15y agoI think you have an extra two bytes (or a bug) here: function blocks(l) { s.map(function(p) { c.fillRect(p.x << 3, p.y << 3, 8, 8); }); } l is ignored, so you can remove it (and the part where you pass it in from the engine). This code still works because s has global scope, but that looks like an accident. I guess the "right" thing to do would be to rename s to l here, but when you're trying to fit stuff into 4K that's not the main concern :) Cool program by the way, I'm impressed with how clear the code remained.
- SpikeGronim 15y agoI'm surprised that the first nine comments didn't mention the easter egg in the lower right hand corner.
- vog 15y agoWhat easter egg exactly are you talking about? I wasn't able to find any, even with your hint.
- webspiderus 15y agoI wonder if it's the link in "All content printed on 100% recycled internet memes"?
- vog 15y agoAh, the easter egg is in the announcement page, not in the game. Thanks for clarifying!
- carldall 15y agoAw, doesn't work in Opera. But a great idea. This reminds me of the time when I was young and worked in a small IT department in a local company. Most of the time it was boring, but installing games on the machines was forbidden. So I coded a set of Excel Macros to play Snake on an Excel cheat. Good times, we even had an internal high score list right in there; half the department joined :)
- naz 15y agoYou should queue inputs. If you press left-down the snake should turn left and then go down on the next frame. That is, if you are attempting to recreate the classic Nokia experience, anyway.
- bytex64 15y agoI thought about it. It's been so long since I've played Nokia snake that I didn't remember whether it queued inputs or not. I decided not to bother since it would make input handling more complicated.
- igravious 15y agoPlease do it! This is how (I'm showing my age now) it worked in DOS all those years ago.
- xtracto 15y ago>That is, if you are attempting to recreate the classic Nokia experience, anyway. Wow, it is interesting that "the reference" for the snake game is the one in a Nokia mobile phone. The reference for me is the BASIC implementation that (I think) came with a version of Microsoft DOS some time ago.
- stuaxo 15y agoHeh, yeah the Qbasic one was good, I remember extending it to have powerups, 23 levels and 4 player ...
- burgerbrain 15y agoIt's kind of like how the reference for tetris for many people is the gameboy version.
- saddino 15y agoAnd the reference for me is the version that was bundled with 4.0 BSD in 1980 (also the distribution that introduced Zork)
- 15y ago
- thomasfl 15y agoHack of the day!
- mathias 15y ago> "CHIP. CHIP, WAKE UP. YOU KNOW WHAT WOULD BE TOTALLY BITCHIN'? A VERSION OF SNAKE THAT COULD FIT IN A URI!" Sorry to Melvin all over this comment, but technically, anything can fit in a data: URL. On its own, it’s not much of an accomplishment. OTOH, a fully functional snake game that fits in ~1KB is pretty impressive. Kudos to you, sir!
- bytex64 15y agoI read somewhere that most browsers have a practical limit to what you can paste in. What I should have written was that it would fit in an address bar. shrug
- DanBC 15y agoI agree, RFC2397 shows a bunch of things that can limit the length of a data URL. (http://www.ietf.org/rfc/rfc2397 http://www.ietf.org/rfc/rfc2397)
- mathias 15y agoThe "data:" URL scheme is only useful for short values. Note that some applications that use URLs may impose a length limit; for example, URLs embedded within <A> anchors in HTML have a length limit determined by the SGML declaration for HTML [RFC1866]. The LITLEN (1024) limits the number of characters which can appear in a single attribute value literal, the ATTSPLEN (2100) limits the sum of all lengths of all attribute value specifications which appear in a tag, and the TAGLEN (2100) limits the overall length of a tag. These are references to (theoretical) limitations in HTML/SGML. They are not limitations of the data URL scheme. Also note that bytex64’s page doesn’t use a link; it just shows the data URL so you can copy and paste it. Either way, none of the above limitations apply here. </nitpick>
- maximusprime 15y agoafaik it's usually about 4k but more in modern browsers.
- 15y ago
- tlrobinson 15y agoThe most interesting thing about this is you can use plain text in data URIs. For some reason I assumed it had to be base64 encoded, but apparently only for binary data.
- bytex64 15y agoI thought the same thing until I watched a talk a few weeks ago about HTML5, and the guy demonstrated the world's smallest text editor: data:text/html,<pre contenteditable>
- moontear 15y agoDoesn't work for me, but data:text/html,<pre contenteditable>Hello</pre> works for me.
- rufibarbatus 15y agoIf the Hello version works, the smaller version should work too. The <pre> will be 1-line high on HTML5 browsers, not window high, so you have to click the very top of the page body for it to focus. (If that still doesn't work, out of curiosity, what browser are you using?)
- grayrest 15y agodata:text/html,<pre contenteditable/> Works in Chrome 15 without content but Firefox requires content.
- rufibarbatus 15y agoOh! Looks like we found a most unexciting difference in Fx's and Chrome's HTML5 rendering engines. Someone alert the authorities! What's happening is in fact a little bit more complex than that. Both browsers create the pre element (you can verify that by typing that URI and hitting the tab key to switch focus from the address bar into it). Now, where Chrome renders a line-sized pre element even when it's empty, Firefox renders it as a 0px element until you feed text into it (that's probably its standard behaviour for pre elements without a child text node, but I can't be bothered verifying that). Boy, I love getting a rush out of figuring out something completely irrelevant! :-)
- joelhaasnoot 15y agoI tried putting this in a QR code and reading it with my phone. Neadless to say, an XL QR code can't be scanned by my old Android 2 MP phone camera.
- rufibarbatus 15y agoThat's interesting. My phone kept (mis)reading UPC-E values from the XL QR code!
- joelhaasnoot 15y agoYeah looks like the "Barcode Scanner" app doesn't like a QR code with multiple square nodes in it, but needs three good corners or so.
- rufibarbatus 15y agoJust tested it on an iPhone 3 (using an app merely called "Scan") — no reaction whatsoever.
- laumars 15y agoWeird you guys are having problems because it scans perfectly using the standard barcode scanner app on my Desire S (running MIUI): http://www.armyofcrabs.com/qrsnake.png http://www.armyofcrabs.com/qrsnake.png
- etrain 15y agoRL Classic on iPhone 4S correctly scanned the code, but did not launch a browser. Tried on iPad 2 last night and safari didn't render it properly, anyway.
- chrislomax 15y agoFantastic work, very impressed! Far more interesting start to the day than when I woke up the other day dreaming of how to get culture codes into my .net application for a foreign website we are building...
- theprestig3 15y agoNice work!
- its_so_on 15y agoI don't know if I should have just been more patient waiting for levels, but this is way too slow. If you guys want a playable experience, replace the literal 500 with 100 in the URL (careful using search and replace, there's also a 5000 literal, that's not the one I'm talking about).
- k_bx 15y agosleep() in javascript. just feel it's absence.
- philh 15y agoBased on this, I made Shit Snake[1] in a data: URI: http://philh.net/datashitsnake/ http://philh.net/datashitsnake/ [1] http://www.draknek.org/games/shitsnake/ http://www.draknek.org/games/shitsnake/
- bkrausz 15y agoNice code! There are 4 functions that can be collapsed to save some space, since they're only called once. Also assigned document.body & setTimeout to vars. Brings the size from 1090 to 966...nice to be less than 1kb! data:text/html,<script>S=setTimeout;function f(){do z={x:Math.floor(16*Math.random()),y:Math.floor(16*Math.random())};while(g(z));return z}function h(b,e){return b.x==e.x&&b.y==e.y}function g(b){return s.filter(h.bind(this,b)).length}function i(){s.map(function(b){c.fillRect(b.x<<3,b.y<<3,8,8)})}function j(){d=m;var b={x:s[0].x+d.x,y:s[0].y+d.y},e;if(e=0>b.x||15<b.x||0>b.y||15<b.y||g(b))c.fillStyle="red",i(),S(k,5E3),e=1;e||S(j,350-5*s.length);s.unshift(b);h(s[0],a)&&(a=f())||s.pop();c.clearRect(0,0,128,128);i();c.beginPath();c.arc((a.x<<3)+4,(a.y<<3)+4,4,0,7);c.fill()}window.addEventListener("keydown",function(b){b={37:{x:-1,y:0},38:{x:0,y:-1},39:{x:1,y:0},40:{x:0,y:1}}[b.keyCode];b.x!=-d.x&&b.y!=-d.y&&(m=b)},!1);function k(){d=document.body;d.innerHTML='<canvas width=128 height=128 style="width:256px;height:256px;border:8px solid black"></canvas>';c=d.firstChild.getContext("2d");s=[{x:8,y:8},{x:7,y:8},{x:6,y:8}];a=f();m={x:1,y:0};j()}S(k,0);</script>
- SoldierB 15y agoYou can save 6 bytes by switching up the bounds checking. From "0>b.x||15<b.x||0>b.y||15<b.y" to "(b.x^16)<1||(b.y^16)<1". data:text/html,<script>S=setTimeout;function f(){do z={x:Math.floor(16*Math.random()),y:Math.floor(16*Math.random())};while(g(z));return z}function h(b,e){return b.x==e.x&&b.y==e.y}function g(b){return s.filter(h.bind(this,b)).length}function i(){s.map(function(b){c.fillRect(b.x<<3,b.y<<3,8,8)})}function j(){d=m;var b={x:s[0].x+d.x,y:s[0].y+d.y},e;if(e=(b.x^16)<1||(b.y^16)<1||g(b))c.fillStyle="red",i(),S(k,5E3),e=1;e||S(j,350-5*s.length);s.unshift(b);h(s[0],a)&&(a=f())||s.pop();c.clearRect(0,0,128,128);i();c.beginPath();c.arc((a.x<<3)+4,(a.y<<3)+4,4,0,7);c.fill()}window.addEventListener("keydown",function(b){b={37:{x:-1,y:0},38:{x:0,y:-1},39:{x:1,y:0},40:{x:0,y:1}}[b.keyCode];b.x!=-d.x&&b.y!=-d.y&&(m=b)},!1);function k(){d=document.body;d.innerHTML='<canvas width=128 height=128 style="width:256px;height:256px;border:8px solid black"></canvas>';c=d.firstChild.getContext("2d");s=[{x:8,y:8},{x:7,y:8},{x:6,y:8}];a=f();m={x:1,y:0};j()}S(k,0);</script>