Created
November 28, 2012 01:29
-
-
Save drm/4158455 to your computer and use it in GitHub Desktop.
tetris.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<body> | |
<pre id="canvas" style="font-size:20px; font-weight:bold;"></pre> | |
</body> | |
</html> | |
<script type="text/javascript"> | |
var Keys = { | |
LEFT: 37, | |
UP: 38, | |
RIGHT: 39, | |
DOWN: 40, | |
A: 'A'.charCodeAt(0), | |
a: 'a'.charCodeAt(0), | |
Z: 'Z'.charCodeAt(0), | |
z: 'z'.charCodeAt(0), | |
DOT: '.'.charCodeAt(0), | |
COMMA: ','.charCodeAt(0), | |
}; | |
var Rotation = { | |
RIGHT: [[0, 1], [-1, 0]], | |
LEFT: [[0, -1], [1, 0]], | |
apply: function(point, rotation) { | |
// multiply vector (x, y) by rotation matrix. | |
return [ | |
point[0] * rotation[0][0] + point[1] * rotation[0][1], | |
point[0] * rotation[1][0] + point[1] * rotation[1][1] | |
]; | |
} | |
}; | |
var tetrominoes = [ | |
[[0, 1], [1, 1], [1, 0]], | |
[[1, 0], [1, 1], [0, 1]], | |
[[1], [1], [1], [1]], | |
[[1, 0], [1, 0], [1, 1]], | |
[[0, 1], [0, 1], [1, 1]], | |
[[1, 1], [1, 1]], | |
[[0, 1], [1, 1], [0, 1]], | |
/** proof of concept that the world works; | |
[[1, 1], [1, 1], [0, 1]], | |
[[1, 1, 0], [0, 1, 1], [0, 0, 1]] | |
*/ | |
]; | |
var kill = false; | |
var gridSize = [10, 15], startX = 0; | |
var grid = []; | |
for (var x = 0; x < gridSize[0]; x ++) { | |
grid[x] = []; | |
for (var y = 0; y < gridSize[1]; y ++) { | |
grid[x][y]= 0; | |
} | |
} | |
function randomPiece() { | |
return tetrominoes[Math.floor(Math.random() * tetrominoes.length)]; | |
} | |
var piece = {}, queue = [randomPiece()]; | |
function clonePiece(piece) { | |
var ret = {x: piece.x, y: piece.y, type: []}; | |
iterateGrid(piece.type, function(x, y, v) { | |
if (typeof ret.type[x] === 'undefined') { | |
ret.type[x] = []; | |
} | |
ret.type[x][y] = v; | |
}); | |
return ret; | |
} | |
function spawnPiece() { | |
piece.x = startX; | |
piece.y = -1; | |
piece.type = queue.shift(); | |
queue.push(randomPiece()); | |
} | |
function gameover() { | |
kill = true; | |
alert("Game over :("); | |
} | |
function isCollision(piece, gridB) { | |
var ret = false; | |
iterateGrid(piece.type, function(x, y, v) { | |
if (v) { | |
var realX = x + piece.x; | |
var realY = y + piece.y; | |
if (realX >= gridSize[0] | |
|| realX < 0 | |
|| realY >= gridSize[1] | |
// || realY < 0 | |
|| gridB[x+piece.x][y + piece.y]) { | |
ret = true; | |
} | |
} | |
}); | |
return ret; | |
} | |
function deleteRow(grid, row) { | |
var newGrid = []; | |
for (var x = 0; x < gridSize[0]; x++) { | |
newGrid[x] = [0]; | |
} | |
iterateGrid(grid, function(x, y, v) { | |
if (y < row) { | |
newGrid[x][y+1] = v; | |
} else if (y > row) { | |
newGrid[x][y] = v; | |
} | |
}); | |
return newGrid; | |
} | |
function checkRows(grid) { | |
for (var y = 0; y < gridSize[1]; y++) { | |
var isFull = true; | |
for (var x = 0; x < gridSize[0]; x ++) { | |
isFull &= grid[x][y]; | |
} | |
if (isFull) { | |
grid = deleteRow(grid, y); | |
} | |
} | |
return grid; | |
} | |
function fallDown() { | |
var tmp = clonePiece(piece); | |
if (isCollision(piece, grid)) { | |
gameover(); | |
} | |
tmp.y ++; | |
if(isCollision(tmp, grid)) { | |
mergeToGrid(grid, piece); | |
grid = checkRows(grid); | |
spawnPiece(); | |
} else { | |
piece = tmp; | |
} | |
} | |
function rotatePiece(piece, rotation) { | |
var origin = [piece.x, piece.y]; | |
var points = [] | |
iterateGrid(piece.type, function(x, y, v) { | |
points.push( | |
[Rotation.apply([x, y], rotation), v] | |
); | |
}); | |
var minX = points[0][0][0]; | |
var minY = points[0][0][1]; | |
for (var i = 1; i < points.length; i ++) { | |
minX = Math.min(points[i][0][0], minX); | |
minY = Math.min(points[i][0][1], minY); | |
} | |
// rebase the coordinates in the origin of the piece. | |
piece.type = []; | |
for (var i = 0; i < points.length; i ++) { | |
points[i][0][0] -= minX; | |
points[i][0][1] -= minY; | |
if (typeof piece.type[points[i][0][0]] === 'undefined') { | |
piece.type[points[i][0][0]] = []; | |
} | |
piece.type[points[i][0][0]][points[i][0][1]] = points[i][1]; | |
} | |
} | |
function heartbeat() { | |
if (kill) { | |
return; | |
} | |
fallDown(); | |
setTimeout(arguments.callee, 300); | |
} | |
document.onkeypress = function (e) { | |
var handled = false; | |
var ghost = clonePiece(piece); | |
switch (e.keyCode || e.charCode) { | |
case Keys.LEFT: | |
case Keys.COMMA: | |
handled = true; | |
ghost.x --; | |
break; | |
case Keys.DOT: | |
case Keys.RIGHT: | |
handled = true; | |
ghost.x ++; | |
break; | |
case Keys.UP: | |
case Keys.a: case Keys.A: | |
handled = true; | |
rotatePiece(ghost, Rotation.RIGHT); | |
break; | |
case Keys.z: case Keys.Z: | |
handled = true; | |
rotatePiece(ghost, Rotation.LEFT); | |
break; | |
case Keys.DOWN: | |
handled = true; | |
ghost.y ++; | |
break; | |
} | |
if (!isCollision(ghost, grid)) { | |
piece = ghost; | |
} | |
if (handled) { | |
e.preventDefault(); | |
} | |
} | |
function iterateGrid(grid, fn) { | |
for (var dx = 0; dx < grid.length; dx ++) { | |
for (var dy = 0; dy < grid[dx].length; dy++) { | |
fn(dx, dy, grid[dx][dy]); | |
} | |
} | |
} | |
function mergeToGrid(grid, piece) { | |
iterateGrid(piece.type, function(x, y, v) { | |
if (v) { | |
grid[piece.x + x][piece.y + y] = 1; | |
} | |
}); | |
} | |
function paint() { | |
if (kill) { | |
return; | |
} | |
var t = ''; | |
var viewPort = [40, 40]; | |
for (var y = 0; y < viewPort[1]; y ++) { | |
for (var x = 0; x < viewPort[0]; x ++) { | |
t += " " | |
} | |
t += "\n"; | |
} | |
function drawXY(x, y, ch) { | |
if (y >= 0 && x >= 0 && x < viewPort[0] && y < viewPort[1]) { | |
var offset = y * (viewPort[0] + 1) + x; | |
t = t.substr(0, offset) + ch + t.substr(offset +1); | |
} | |
} | |
for (var y = 0; y < gridSize[1]; y++) { | |
for (var x = 0; x < gridSize[0]; x++) { | |
if (grid[x][y]) { | |
drawXY(x, y, '#'); | |
} else { | |
drawXY(x, y, '/'); | |
} | |
} | |
} | |
if (piece.type) { | |
iterateGrid( | |
piece.type, | |
function(x, y, v) { | |
if (v) { | |
drawXY(x + piece.x, y + piece.y, 'O'); | |
} | |
} | |
); | |
} | |
for (var px = 0; px < queue[0].length; px ++) { | |
for (var py = 0; py < queue[0][px].length; py ++) { | |
if (queue[0][px][py]) { | |
drawXY(gridSize[0] + 2 + px, py + 2, 'O'); | |
} | |
} | |
} | |
document.getElementById('canvas').innerHTML = t; | |
setTimeout(arguments.callee, 20); | |
} | |
paint(); | |
spawnPiece(); | |
heartbeat(); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment