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