|
<!DOCTYPE html> |
|
<title>Pac-Man in 140byt.es by @maettig</title> |
|
<pre>LOADING...</pre> |
|
<h2></h2> |
|
<span id="points">0</span> POINTS |
|
<style type="text/css"> |
|
* { color: #FFF; font-family: Consolas, monospace; text-shadow: 0 0 .2em #FFF; } |
|
body { background: #000; margin: 4em; } |
|
h2 { font-size: 3em; margin: .2em 0; } |
|
img { border: 0; font-size: 32px; height: 32px; padding: 1px; vertical-align: top; width: 32px; } |
|
pre { font-size: 58px; line-height: 32px; margin: 0; } |
|
#points { font-size: 2em; line-height: 2em; } |
|
.air { color: #222; text-shadow: 0 0 .2em #222; } |
|
.player { color: #FD0; text-shadow: 0 0 .2em #FD0; } |
|
.wall { color: #30F; text-shadow: 0 0 .2em #30F; } |
|
.ghost { color: #A0F; text-shadow: 0 0 .2em #A0F; } |
|
</style> |
|
<script type="text/javascript"> |
|
// 138 bytes |
|
var getFirstElement = function(a) |
|
{ |
|
a = /([#.]?)(.*)/.exec(a || '*'); |
|
a = document['getElement' + (a[1] > '-' ? 'sByClassName' : a[1] ? 'ById' : 'sByTagName')](a[2]); |
|
return a[0] || a |
|
} |
|
// 69 bytes |
|
var getOuterHTML = function(a) |
|
{ |
|
return a.outerHTML || XMLSerializer().serializeToString(a) |
|
} |
|
// 126 bytes |
|
var parseLevel = function(l, w) |
|
{ |
|
l = l.join ? l[0].length > 1 //if it's an array of strings per row |
|
? l.join('\n') : l.join('') : l; //then join rows else join characters |
|
w = l.indexOf('\n') + 1; //calculate row length |
|
// It would be possible to calculate player position with `l.indexOf(1)` but that's 7 bytes longer. |
|
// It would be possible to count gold with `l.split(2).length - 1` but that's 11 bytes longer. |
|
l = l.split(''); //convert to character array |
|
l.w = w; //store width |
|
l.c = l.d = l.p = 0; //initialize gold count, player walking direction, points |
|
return l |
|
} |
|
// 110 bytes |
|
var initLevel = function(l) |
|
{ |
|
for (var i = g = []; i < l.length; ) |
|
{ |
|
l[i] & 8 && //if found a ghost then |
|
g.push({ //append to the array of ghosts |
|
x: i, //ghost position |
|
d: 0 //ghost walking direction (0 = north) |
|
}); |
|
if (l[i] & 1) l.x = i; //store player position |
|
l.c += l[i++] == 2 //count gold |
|
} |
|
return g |
|
} |
|
// 136 bytes |
|
var loadSprites = function(t, f) //theme, filename with $1 placeholder |
|
{ |
|
for (var i in t) |
|
with (new Image()) //new is required in IE |
|
{ |
|
alt = i; |
|
onload = function() |
|
{ |
|
t[this.alt] = getOuterHTML(this) |
|
} |
|
src = t[i].replace(/.*"(.*)".*/, f) |
|
} |
|
} |
|
// 88 bytes |
|
var renderLevel = function(l, t) //level object, optional theme object |
|
{ |
|
for (var c, h = '', x = 0; x < l.length; x++) |
|
h += t && t[c = l[x] & 8 || l[x]] //ghosts can sit on gold, render the ghost only |
|
? t[c] : c; //use theme or original character (e.g. the newline) |
|
return h |
|
} |
|
// 129 bytes |
|
var updatePlayer = function(l, k) |
|
{ |
|
l[l.x] = 0; |
|
l.p += l[l.x += l.d = k - 37 >> 2 || l[l.x + (k = k % 2 ? k - 38 : (k - 39) * l.w)] & 4 ? l[l.x + l.d] & 4 ? 0 : l.d : k] == 2; |
|
l.e |= l[l.x] & 8; |
|
l[l.x] = 1 |
|
} |
|
// 134 bytes |
|
var updateGhosts = function(a, l, g, d) |
|
{ |
|
for (g in a) |
|
l[a[g].x] &= 7; //remove all ghosts first |
|
for (g in a) |
|
{ |
|
g = a[g]; |
|
d = g.d % 4; |
|
d = d % 2 ? d - 2 : (d - 1) * l.w; //calculate walking direction |
|
l[g.x + d] & 4 //if walking into a wall |
|
? g.d++ //rotate ghost by 90 degree |
|
: l.e |= 1 & l[ //else check if hitting the player |
|
g.x += d]; //and walk |
|
l[g.x] |= 8 //place ghost |
|
} |
|
} |
|
var levels = [parseLevel([ |
|
'44444444444', //no ghosts |
|
'42222222224', |
|
'42444242424', |
|
'42412242424', |
|
'42444242424', |
|
'42222242224', |
|
'44444444444']), |
|
parseLevel([ |
|
'4444444444444', //1 ghost |
|
'4222224222224', |
|
'4244424244424', |
|
'4242228222424', |
|
'4242424442424', |
|
'4242421242424', |
|
'4242444242424', |
|
'4242222222424', |
|
'4244424244424', |
|
'4222224222224', |
|
'4444444444444']), |
|
parseLevel([ |
|
'444444444444444', //2 ghosts |
|
'424222222222224', |
|
'424244444242444', |
|
'422222222222224', |
|
'424244444242424', |
|
'424242212242424', |
|
'424242444442424', |
|
'422222222222224', |
|
'444242444442424', |
|
'482222222222484', |
|
'444444444444444']), |
|
parseLevel([ |
|
'4444444444444444444', //classic with 2 ghosts |
|
'4222242228222422224', |
|
'4244242444442424424', |
|
'4242222222222222424', |
|
'4242442440442442424', |
|
'4222222408042222224', |
|
'4242442444442442424', |
|
'4242222221222222424', |
|
'4244242444442424424', |
|
'4222242222222422224', |
|
'4444444444444444444']), |
|
parseLevel([ |
|
'444444444444444', //3 ghosts |
|
'422222282222224', |
|
'424444424444424', |
|
'424222424822424', |
|
'424242424242424', |
|
'422242212242224', |
|
'424242424242424', |
|
'424228424222424', |
|
'424444424444424', |
|
'422222222222224', |
|
'444444444444444']), |
|
parseLevel([ |
|
'4444444444444444444', //5 ghosts |
|
'4222222248222222224', |
|
'4244444242444442444', |
|
'4242222282222222224', |
|
'4242444442444442424', |
|
'4242422242482222424', |
|
'4242424242424244424', |
|
'4222222222222228224', |
|
'4244424242424242424', |
|
'4242222842422242424', |
|
'4242444442444442424', |
|
'4222222222222222424', |
|
'4442444442424444424', |
|
'4222222122422222224', |
|
'4444444444444444444'])]; |
|
var theme = { |
|
0: '<span class="air">\u2219</span>', //doesn't need to be checked |
|
1: '<span class="player">\u263B</span>', //can be checked with &1 |
|
2: '<span class="gold">\u2219</span>', //can be checked with &2 |
|
4: '<span class="wall">\u25A1</span>', //can be checked with &4 |
|
8: '<span class="ghost">\u00A4</span>', //can be checked with &8 or >7 |
|
'\n': '<br>' //required for IE |
|
} |
|
loadSprites(theme, 'sprite-$1.gif'); //load optional GIF files from the same directory |
|
document.onkeydown = function(e) |
|
{ |
|
level.keyCode = (e || window.event).keyCode |
|
} |
|
var ghosts, level, number = 0; |
|
window.setInterval(function() |
|
{ |
|
if (!level && levels[number]) |
|
ghosts = initLevel(level = levels[number++]); |
|
if (level.p >= level.c) |
|
{ |
|
if (level.p < level.c-- + 4) //wait a few moments before loading next level |
|
return; |
|
level = 0 //unload current level when won |
|
} |
|
if (!level || level.e) //stop updating when lost or won |
|
return; |
|
updatePlayer(level, level.keyCode || 0); |
|
updateGhosts(ghosts, level); |
|
getFirstElement('PRE').innerHTML = renderLevel(level, theme); |
|
getFirstElement('#points').innerHTML = level.p; |
|
getFirstElement('H2').innerHTML = level.e ? 'GAME OVER!' : level.p < level.c ? '' : 'YOU WIN!' |
|
}, 250); |
|
</script> |
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.