Skip to content

Instantly share code, notes, and snippets.

@maettig
Forked from 140bytes/LICENSE.txt
Created November 21, 2011 23:12
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 maettig/1384306 to your computer and use it in GitHub Desktop.
Save maettig/1384306 to your computer and use it in GitHub Desktop.
Pac-Man in 140byt.es

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>
@jed
Copy link

jed commented Nov 22, 2011

here's a demo.

@p01
Copy link

p01 commented Nov 22, 2011

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}

@maettig
Copy link
Author

maettig commented Nov 22, 2011

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}

@pvdz
Copy link

pvdz commented Nov 22, 2011

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...

@maettig
Copy link
Author

maettig commented Nov 22, 2011

@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}

@maettig
Copy link
Author

maettig commented Nov 23, 2011

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.

@maettig
Copy link
Author

maettig commented Nov 30, 2011

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.

@tsaniel
Copy link

tsaniel commented Dec 1, 2011

The bad thing is that the control is lagged.

@maettig
Copy link
Author

maettig commented Dec 1, 2011

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.

@maettig
Copy link
Author

maettig commented Jan 2, 2012

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.

@tsaniel
Copy link

tsaniel commented Jan 2, 2012

Great.

@maettig
Copy link
Author

maettig commented Jan 16, 2012

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.

@p01
Copy link

p01 commented Apr 13, 2012

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;
  //...

@maettig
Copy link
Author

maettig commented Apr 14, 2012

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.

@p01
Copy link

p01 commented Apr 17, 2012

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.

@maettig
Copy link
Author

maettig commented Apr 17, 2012

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.

@aemkei
Copy link

aemkei commented Jun 14, 2015

Wow: that was more than three years ago!

Now we reanimate this awesome work and hacked the HTML+JS down to 460 bytes:

https://github.com/codegolf/pac-man/

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