The mine is collapsing! Left/right keys and space. Get the coins. Avoid the boulders.
for(_='0;X=XWx2Vx+U2080:k[3e.y0,++onkey,x,y,3bc=242x&&){);"Vd3x3+=160*l4bb7]=(e-t)/1 1,3.push(c.filla2",;if(dbx4b3xx0|Math.random()*a.width=a.height=64Xc.scale(2,2k=n=[];s=t=u=F=w=x=MWo=1;yXS=((aw,s,f)=>{q=rWfor(iWi<a.length;ih="000fed00ff75fd5533976".match(/.../g)[a[i].charCodeAt(0)-97];A=+a[i+1];A=A?(i,A):1;for(CWC<A;CStyle="#"+h;hRect(~~(U(f?w-q-1:q)*s),~~(y+r*s),s,s)q==wqWr}}}}(function T(el e3;u+=l;M+ 0Xt=e;S("f",S("d",28Text(s,1)u>=1n{x:(8)*4y:t:2,d:[-1,1][2]}u=0}f=[];n.map(e=>{>=260=26XU=60*l*d}x<UU30>xy<+30+y>o?t?(fe),o=0):s:feS(t?"g5eg4eg5":"xeeb2e2b2exe2",x,,4,5)}n=fo2]y=0w=-1}F=o(||9])??-Xw;y+=w*l;U=F*l;x>280?x=2x<0?x=0:xy>0yXw=0}S([4bcx4c3ac4cbx4caxc3aVc2ac4xaV"x3dx4d4bcx3c4xa22bxbx3cc"V2b9b3V2"][o?w?3:F?~~M%3:4:5]6,5,requestAnimationFrame(T)})(0up=down=(e=>k[which]=type[5]';G=/[-U-X]/.exec(_);)with(_.split(G))_=join(shift());eval(_)
Zm9yKF89JzA7WD1YV3gyVngrVTIwHzgwOh5rWzMdZS4cHHkbMCwaKysZb25rZXkYLHgseSwXM2JjFj0yNBUyeBQmJhMpexIpOxEiVmQzeDMQKz0xNjAqbA8UNA5iDmIMHTddCz0oZS10KS8xCRoxLDMfEQgucHVzaCgHYy5maWxsBmEyIiwFO2lmKARkYng0YjN4FngDMHxNYXRoLnJhbmRvbSgpKgIQAwFhLndpZHRoPWEuaGVpZ2h0PTY0WGMuc2NhbGUoMiwyEWs9bj1bXTtzPXQ9dT1GPXc9eD1NV289MTt5FVhTPSgoYRd3LHMsZik9PntxPXJXZm9yKGlXaTxhLmxlbmd0aDtpGRJoPSIwMDBmZWQwMGZmNzVmZDU1MzM5NzYiLm1hdGNoKC8uLi4vZylbYVtpXS5jaGFyQ29kZUF0KDApLTk3XTtBPSthW2krMV07QT1BPyhpGSxBKToxO2ZvcihDV0M8QTtDGRIGU3R5bGU9IiMiK2g7aBMGUmVjdCh+fihVKGY/dy1xLTE6cSkqcyksfn4oeStyKnMpLHMscykEGXE9PXcScVdyGX19fX0RKGZ1bmN0aW9uIFQoZRJsCWUzO3UrPWw7TSsJMFh0PWU7UygiZiIsGghTKCJkIiwaMjgIBlRleHQocywxGh8pBHU+PTESbgd7eDooAjgpKjQaeToadDoCMixkOlstMSwxXVsCMl19EXU9MH1mPVtdO24ubWFwKGU9PnsbDwQbPj0yNjASGz0yNlgcVT02MCpsKhxkfXg8HFUfE1UzMD4ceBN5PBsrHxMzMCt5PhsTbz8cdD8oZgdlKSxvPTApOnMZOmYHZRFTKBx0PyJnNWVnNGVnNSI6InhlFGViMmUyYjJleGUyIiwceCwbLDQsNSl9EW49ZgRvEx0yXRN5PRUwEnc9LTEffUY9bxMoC3x8HTldKT8LPy0eHlh3Dzt5Kz13Kmw7VT1GKmw7eD4yODA/eD0yHng8MD94PTA6eAR5PhUwEnkVWHc9MH1TKFsBNGJjeDRjFDNhYw4FATRjYng0YxRheGMzYVYFARYOYxQyYWM0eGFWBSJ4M2R4NGQOAzRiY3gzYzR4YRQyBRAMFBYyYngWYngzYw5jDgUiVgwUMmI5YjNWDA4MMiJdW28/dz8zOkY/fn5NJTM6NDo1XRc2LDUsCxFyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoVCl9KSgwERh1cD0YZG93bj0oZT0+a1scd2hpY2hdPRx0eXBlWzVdESc7Rz0vWwEtH1UtWF0vLmV4ZWMoXyk7KXdpdGgoXy5zcGxpdChHKSlfPWpvaW4oc2hpZnQoKSk7ZXZhbChfKQ==
// TOKEN_MAX_WIDTH and TOKEN_MAX_HEIGHT = 320
a.width = a.height = 640
// Scale for retina
c.scale(2, 2)
// k = keys
// Entities (n)
k = n = []
// s = score
// t = time
// u = time since spawn
// v = player x velocity
// w = player y velocity
// x = player x
// z = player frame
s = t = u = v = w = x = z = 0
// o = Is player alive?
o = 1
// y = player y
y = 240
// (S)prite
// a = run length encoded string
// x = sprite x
// y = sprite y
// w = width, i.e. rects in row
// s = size of rect
// f = flip?
S = (a, x, y, w, s, f) => {
// q = column, r = row
q = r = 0
for (i = 0; i < a.length; i++) {
// Get color
// 'a' = feet
// 'b' = skin/cross
// 'c' = body
// 'd' = hair/ground
// 'e' = coin
// 'f' = background
// 'g' = boulder
h = '000fed00ff75fd5533976'.match(/.../g)[a[i].charCodeAt(0) - 97]
// Use '+' instead of parseInt
// Non-numeric chars will be NaN
// Number of repeats
$ = +a[i + 1]
$ = $ ? (i++, $) : 1
for (j = 0; j < $; j++) {
c.fillStyle = '#' + h
h && c.fillRect(
// Get x coord for 'pixel'. If f is truthy, flip by rendering
// right to left
~~(x + (f ? w - q - 1 : q) * s),
~~(y + r * s),
s,
s
)
// Assign and test in one go...
if (++q == w) {
q = 0
r++
}
}
}
}
// (T)ick
// e = timestamp
(function T(e) {
// Calculate delta
// 1e3 is shorter than 1000
l = (e - t) / 1e3
// Update time since last spawn
u += l
// Animate sprite at 10fps
// 10 * (e - t) / 1e3 becomes (e - t) / 100
z += (e - t) / 100
t = e
// Draw background
S('f', 0, 0, 1, 320)
// Draw floor
S('d', 0, 280, 1, 320)
// Text will have the same fill as floor
c.fillText(s, 10, 20)
// Spawn every nth seconds
if (u >= 1) {
n.push({
x: (0 | Math.random() * 8) * 40,
y: 0,
// Set type (0 = coin, 1 = boulder)
t: 0 | Math.random() * 2,
// Set direction (left or right)
d: [-1, 1][0 | Math.random() * 2]
})
// Reset time since last spawn
u = 0
}
// Render and update entities
// IRL reduce would be a better choice, but map and pushing to a temp array
// is less bytes
f = []
n.map(e => {
// Gravity is 160
e.y += 160 * l
if (e.y >= 260) {
e.y = 260
e.x += 60 * l * e.d
}
// IRL we'd want to remove out of bound entities, but that uses up bytes
// e.g. if (e.x < 320 && e.x > -20)
// Simple hit detection (also checks if game if player is alive)
x < e.x + 20 && x + 30 > e.x && y < e.y + 20 && 30 + y > e.y && o
// Hit, game over if boulder, increment score if coin
? e.t ? (f.push(e), o = 0) : s++
: f.push(e)
// Draw
S(e.t ? 'g5eg4eg5' : 'xe2xeb2e2b2exe2', e.x, e.y, 4, 5)
})
// Re-assign entities
n = f
// Update player
// Jumping?
if (o && k[32] && y == 240) {
w = -120
}
// Moving?
v = o && (k[37] || k[39]) ? k[37] ? -80 : 80 : 0
// Update y velocity
// Gravity is 160
w += 160 * l
y += w * l
x += v * l
// Clamp to bounds (x)
// if (x > 280) x = 280
// if (x < 0) x = 0
x > 280 ? x = 280 : x < 0 ? x = 0 : x
// Keep player above ground plane (y)
if (y >= 240) {
y = 240
w = 0
}
// Draw player
S(
[
'x2d3x3dbx4b3x3bcx4bcx4c2x3ac2x4a2',
'x2d3x3dbx4b3x3bcx4cbx4c2xaxc3ax2a2',
'x2d3x3dbx4b3x3bcx3bc2x4c2x2ac4xax2a2',
// Jumping
'x3dx4d2x4dbx4b3x3bcx4bcx3c4xa2x2a2',
// Standing
'x2d3x3b2x4b2x3bc2bx3bcbx3c2x4c2x4a2',
// Dead
'x2b2x4b2x2b9b3x2b2x4b2x4b2x4b2'
][
// Ternary maddness
// Equivalent to
// if (o) {
// if (w) {
// j = 3 // Jumping
// }
// else if (v) {
// j = ~~z % 3 // Walking
// }
// if (o) {
// j = 4 // Standing
// }
// } else {
// j = 5
// }
o ? w ? 3 : v ? ~~z % 3 : 4 : 5
],
x,
y,
6,
5,
// v < 0 makes more sense, but k[37] packs better
k[37]
)
requestAnimationFrame(T)
})(0)
// x.type is 'keyup' or 'keydown'
// x.type[5] is undefined on 'keyup'
onkeyup = onkeydown = e => k[e.which] = e.type[5]