A simple variation of the Pac-Man game (almost) made with 140byt.es snippets.
My setInnerHTML
uses tricks learned from @jed's cssSelect. Everything else is created from scratch. Tested with Opera and Firefox. Does not work in Internet Explorer 8.
function(l, k, w) //level, keyCode, level width | |
{ | |
l[l.x] = ' '; //remove player from level | |
if (l[l.x + l.d] == '#') //check if previous walking direction is possible | |
l.d = 0; //stop | |
if (k > 36 && k < 41 && //check if a cursor key was pressed | |
l[l.x + (k = k % 2 //calculate walking direction | |
? k - 38 //-1 is left, +1 is right | |
: (k - 39) * w //-width is up, +width is down | |
)] != '#') //check if not walking into a wall | |
l.d = k; //set new walking direction | |
l.p += l[l.x += l.d //walk | |
] == '.'; //and increase points | |
l[l.x] = 'P' //place player in level | |
} |
function(l,k,w){l[l.x]=' ';if(l[l.x+l.d]=='#')l.d=0;if(k>36&&k<41&&l[l.x+(k=k%2?k-38:(k-39)*w)]!='#')l.d=k;l.p+=l[l.x+=l.d]=='.';l[l.x]='P'} |
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
Version 2, December 2004 | |
Copyright (C) 2011 Thiemo Mättig <http://maettig.com/> | |
Everyone is permitted to copy and distribute verbatim or modified | |
copies of this license document, and changing it is allowed as long | |
as the name is changed. | |
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
0. You just DO WHAT THE FUCK YOU WANT TO. |
{ | |
"name": "pacMan", | |
"description": "Pac-Man made with 140byte.es snippets.", | |
"keywords": [ | |
"game", | |
"pacman" | |
] | |
} |
<!DOCTYPE HTML> | |
<pre>Loading...</pre> | |
<span id="points">0</span> points | |
<style type="text/css"> | |
body { background: #000; color: #FFF; font-family: Consolas, monospace; margin: 4em; text-shadow: 0 0 0.2em #FFF; } | |
pre { font-size: 4em; line-height: 0.55em; margin: 0; } | |
.air { color: #222; text-shadow: 0 0 0.2em #222; } | |
.wall { color: #30F; text-shadow: 0 0 0.2em #30F; } | |
.player { color: #FF0; text-shadow: 0 0 0.2em #FF0; } | |
#points { font-size: 2em; line-height: 2em; } | |
</style> | |
<script> | |
// 153 bytes, any idea how to save 13 bytes? | |
var setInnerHTML = function(a, h) | |
{ | |
a = /(\W?)(.*)/.exec(a); | |
(a[1] == '#' | |
? document.getElementById(a[2]) | |
: document['getElementsBy' + (a[1] ? 'Class' : 'Tag') + 'Name'](a[2])[0] | |
).innerHTML = h | |
} | |
// 97 bytes, maybe use the remaining 43 bytes to make this more flexible? | |
var parseLevel = function(l, w) | |
{ | |
l = l.join ? l.join('\n') : l; | |
w = l.indexOf('\n') + 1; | |
l = l.split(''); | |
l.w = w; | |
l.d = l.p = 0; | |
return l | |
} | |
// 99 bytes, should be enough to add a "you won" screen. | |
var renderLevel = function(l, t) | |
{ | |
for (var h = '', x = 0; x < l.length; x++) | |
{ | |
if (l[x] == 'P') l.x = x; | |
h += t[l[x]] ? t[l[x]] : l[x] | |
} | |
return h | |
} | |
// 140 bytes | |
var updateLevel = function(l, k, w) | |
{ | |
l[l.x] = ' '; | |
if (l[l.x + l.d] == '#') | |
l.d = 0; | |
if (k > 36 && k < 41 && l[l.x + (k = k % 2 ? k - 38 : (k - 39) * w)] != '#') | |
l.d = k; | |
l.p += l[l.x += l.d] == '.'; | |
l[l.x] = 'P' | |
} | |
var theme = { | |
' ': '<span class="air">\u2219</span>', | |
'.': '<span class="gold">\u2219</span>', | |
'#': '<span class="wall">\u25A1</span>', | |
'P': '<span class="player">\u263B</span>' | |
}; | |
var level = parseLevel( | |
'###################\n' + | |
'#....#.......#....#\n' + | |
'#.##.#.#####.#.##.#\n' + | |
'#.#.............#.#\n' + | |
'#.#.##.##.##.##.#.#\n' + | |
'#......#...#......#\n' + | |
'#.#.##.#####.##.#.#\n' + | |
'#.#......P......#.#\n' + | |
'#.##.#.#####.#.##.#\n' + | |
'#....#.......#....#\n' + | |
'###################'); | |
window.onkeydown = function(e) | |
{ | |
level.keyCode = (e || window.event).keyCode; | |
} | |
window.setInterval(function() | |
{ | |
setInnerHTML('PRE', renderLevel(level, theme)); | |
setInnerHTML('#points', level.p); | |
updateLevel(level, level.keyCode, level.w); | |
}, 250) | |
</script> |
getFirstElement in 134bytes:
function(a){a=/(\W?)(.*)/.exec(a);return(a=document['getElement'+(a[1]=='#'?'ById':a[1]?'sByClassName':'sByTagName')](a[2]))&&a[0]||a}
It's so easy. Thank you. Enough room to add the possibility to omit the parameter. In this case it returns the first element in the document (which is the HTMLHtmlElement
in most browsers but the DOCTYPE as an HTMLCommentElement
in IE). This is 139 bytes. I tried to allow the parameter '*'
instead or in addition to leaving out the parameter but if I do so it always becomes bigger than 140 bytes.
function(a){a=/(\W?)(.*)/.exec(a||' *');a=document['getElement'+(a[1]>'-'?'sByClassName':a[1]<1?'sByTagName':'ById')](a[2]);return a[0]||a}
Some basics...
if(x)y;
could be x&&y;
if(x)y;else z;
could be x?y:z;
More specific cases:
I think k>36&&k<41
could be k-36<5
So with the above, the ifs first become:
l[l.x+l.d]=='#' && l.d=0;
k-36<5&&l[l.x+(k=k%2?k-38:(k-39)*l.w)]!='#'&&l.d=k;
I think there are some more "easy" gains to be had but I don't really have the time right now to dive deeper into it...
@qfox, you are right, all if can be replaced with &&. Thank you. Edit: To bad, using &&
does not save a byte. if(a)b=c;
needs to be a&&(b=c);
. And k-36<5
= k-36+36<5+36
= k<41
so it's not the same.
@p01, I think I found an almost perfect solution for my getFirstElement
function (138 bytes). Now it's possible to use IDs (e.g. '#top'
), class names (e.g. '.item'
), tag names (e.g. 'pre'
), the special class name '*'
and no parameter (does the same as '*'
).
function(a){a=/([#.]?)(.*)/.exec(a||'*');a=document['getElement'+(a[1]>'-'?'sByClassName':a[1]?'ById':'sByTagName')](a[2]);return a[0]||a}
After some bit fiddling I found that k>36&&k<41?true:false
is identical to k-37>>2?false:true
. I saved some additional bytes by rearranging the code without changing the game logic. I did an update. Next step? Adding enemies? Graphical tiles? A bunch of 140 bytes GIFs? Edit: While looking at what I did I saw it's possible to move l.d=...
down to where it's used. 127 bytes.
New features: A ghost is walking around. Internet Explorer is supported. Graphics are supported. Unfortunately, I have no idea how to upload binary files at GitHub so you have to download this ZIP archive to see my bunch of tiny GIF images (each below 140 bytes, of course) in action. Next step: Multiple ghosts.
The bad thing is that the control is lagged.
You are right. I was always wondering since I started working on this. Now I found the reason. I was doing the main loop in the wrong order: It was waiting for a key → rendering the level → processing the key. Now it is waiting for a key → processing the key → rendering the level.
Major update to my "Pac-Man in @140bytes" adventure: It's a full game now, including multiple levels and ghosts. Click here to play it.
Great.
I introduced a bug while golfing my updateGhosts function down. Some ghosts in the last level got stuck. Now I'm back at 152 bytes for that function.
Sorry I haven't tried but why not replace the following code in updateGhosts
for(g=a.length;g--;)
l[a[g].x]=a[g].b;
for(g in a)
{
g=a[g];
//...
by
for(g in a)
{
g=a[g];
l[g.x]=g.b;
//...
When the paths of two ghosts intersect the first stores a backup of the ground (which is either air or gold) and the second stores a backup of the first ghost. I have to restore these backups in reverse order.
I think I can drop this backup stuff. Edit: I did it. I introduced this when my array was made of characters. But I switched to integers. Now a ghost (8) on gold (2) can be stored as 10. Setting a ghost is |8
, removing a ghost is &7
.
Nice touch with the bitmask. I replaced:
for(g=a.length;g--;)
l[a[g].x]&=7;
for(g in a)
{
g=a[g];
//...
by
for(g in a)
{
g=a[g];
l[g.x]&=7;
//...
and played a couple of levels without noticing any glitch.
Thanks. There is a tiny glitch unfortunately. If the paths of two ghosts intersect one of the ghosts disappears for one frame.
If your looking for a new challenge, my decodeMinecraftColors needs to be golfed down by 10 bytes.
Wow: that was more than three years ago!
Now we reanimate this awesome work and hacked the HTML+JS down to 460 bytes:
here's a
demo.