Skip to content

Instantly share code, notes, and snippets.

@bellbind
Created November 20, 2012 09:22
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellbind/4116929 to your computer and use it in GitHub Desktop.
Save bellbind/4116929 to your computer and use it in GitHub Desktop.
[javascript][svg]tetris model with svg ui (arrow key control)
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge;chrome=1" />
<script src="tetris.js"></script>
<script src="tetris-svg.js"></script>
</head>
<body>
<div><svg id="stage"></svg></div>
</body>
</html>
"use strict";
window.addEventListener("load", function (ev) {
// SVG UI for Tetris
var opt = {
svgid: "stage",
svgns: "http://www.w3.org/2000/svg",
scale: 24,
width: 10,
height: 20,
};
var newBlock = function () {
return Tetris.Block(
0|(opt.width / 2) - 2, 0, 0,
Tetris.shapes[0|(Math.random() * Tetris.shapes.length)]);
};
var render = function () {
while (view.hasChildNodes()) view.removeChild(view.lastChild);
stage.eachStone(function (x, y, color) {
view.appendChild(stone(x, y, color));
});
block.eachStone(function (x, y, color) {
view.appendChild(stone(x, y, color));
});
};
var stone = function (x, y, color) {
var rect = document.createElementNS(opt.svgns, "rect");
rect.setAttribute("x", opt.scale * x);
rect.setAttribute("y", opt.scale * y);
rect.setAttribute("width", opt.scale);
rect.setAttribute("height", opt.scale);
rect.setAttribute("fill", color);
rect.setAttribute("stroke", "black");
return rect;
};
var tryLeft = function () {
var next = block.left();
if (!next.ok(stage)) return;
block = next;
render();
};
var tryRight = function () {
var next = block.right();
if (!next.ok(stage)) return;
block = next;
render();
};
var tryRotate = function () {
var next = block.rotate();
if (!next.ok(stage)) return;
block = next;
render();
};
var tryFall = function () {
var next = block.fall();
if (next.ok(stage)) {
block = next;
return render();
} else {
block.put(stage);
render();
setTimeout(function () {
stage.shrink();
block = newBlock();
if (!block.ok(stage)) {
// gameover
stage.reset();
}
render();
}, 10);
}
};
// init
var view = document.getElementById(opt.svgid);
view.setAttribute("width", opt.scale * opt.width);
view.setAttribute("height", opt.scale * opt.height);
view.style.backgroundColor = "grey";;
var stage = Tetris.Stage(opt.width, opt.height);
var block = newBlock();
// not work keypress event on chrome svg
var keyHandler = function (ev) {
//console.log(ev.target);
switch (ev.keyCode) {
case 37: return tryLeft(); //left
case 38: return tryRotate(); //up
case 39: return tryRight(); //right
case 40: return tryFall(); //down
}
};
//view.addEventListener("keydown", keyHandler, false);
document.body.addEventListener("keydown", keyHandler, false);
//window.addEventListener("keydown", keyHandler, false);
render();
}, false);
"use strict";
(function (exports) {
// models of Tetris game
// shape of block
var Shape = function Shape(name, color, form) {
var max = form.length - 1;
var angle1 = form.map(function (line, y) {
return line.map(function (e, x) {
return form[max - x][y];});});
var angle2 = form.map(function (line, y) {
return line.map(function (e, x) {
return form[max - y][max - x];});});
var angle3 = form.map(function (line, y) {
return line.map(function (e, x) {
return form[x][max - y];});});
return Object.create(Shape.prototype, {
name: {value: name, enumerable: true},
color: {value: color, enumerable: true},
angle0: {value: form, enumerable: true},
angle1: {value: angle1, enumerable: true},
angle2: {value: angle2, enumerable: true},
angle3: {value: angle3, enumerable: true},
});
};
Shape.prototype.rotated = function (angle) {
switch (((angle % 4) + 4) % 4) {
case 0: return this.angle0;
case 1: return this.angle1;
case 2: return this.angle2;
case 3: return this.angle3;
}
};
// moving block
var Block = function Block(x, y, angle, shape) {
return Object.create(Block.prototype, {
x: {value: x, enumerable: true},
y: {value: y, enumerable: true},
angle: {value: angle, enumerable: true},
shape: {value: shape, enumerable: true},
});
};
Block.prototype.left = function () {
return Block(this.x - 1, this.y, this.angle, this.shape);
};
Block.prototype.right = function () {
return Block(this.x + 1, this.y, this.angle, this.shape);
};
Block.prototype.fall = function () {
return Block(this.x, this.y + 1, this.angle, this.shape);
};
Block.prototype.rotate = function () {
return Block(this.x, this.y, this.angle + 1, this.shape);
};
Block.prototype.ok = function (stage) {
var form = this.shape.rotated(this.angle);
for (var fy = 0; fy < form.length; fy++) {
for (var fx = 0; fx < form[fy].length; fx++) {
if (form[fy][fx] === 0) continue;
var x = this.x + fx;
var y = this.y + fy;
if (x < 0 || stage.width <= x || stage.height <= y ||
(y >= 0 && stage.stones[y][x])) return false;
}
}
return true;
};
Block.prototype.overflow = function () {
var form = this.shape.rotated(this.angle);
for (var fy = 0; fy < form.length; fy++) {
for (var fx = 0; fx < form[fy].length; fx++) {
if (form[fy][fx] === 0) continue;
var y = this.y + fy;
if (y < 0) return true;
}
}
return false;
};
Block.prototype.put = function (stage) {
// assert(this.ok(stage) && !this.overflow());
var form = this.shape.rotated(this.angle);
for (var fy = 0; fy < form.length; fy++) {
for (var fx = 0; fx < form[fy].length; fx++) {
if (form[fy][fx] === 0) continue;
var x = this.x + fx;
var y = this.y + fy;
stage.stones[y][x] = this.shape.color;
}
}
};
Block.prototype.eachStone = function (callback) {
var form = this.shape.rotated(this.angle);
for (var fy = 0; fy < form.length; fy++) {
for (var fx = 0; fx < form[fy].length; fx++) {
if (form[fy][fx] === 0) continue;
var x = this.x + fx;
var y = this.y + fy;
if (x >= 0 && y >= 0) callback(x, y, this.shape.color);
}
}
};
// game stage
var Stage = function Stage(width, height) {
var stones = [];
for (var y = 0; y < height; y++) {
var line = [];
for (var x = 0; x < width; x++) {
line.push(null);
}
stones.push(line);
}
return Object.create(Stage.prototype, {
stones: {value: stones, enumerable: true},
width: {value: width, enumerable: true},
height: {value: height, enumerable: true},
});
};
Stage.prototype.filledLines = function () {
var lines = [];
for (var y = 0; y < this.stones.length; y++) {
if (this.stones[y].reduce(function (r, s) {
return r && s !== null;}, true)) lines.push(y);
}
return lines;
};
Stage.prototype.shrink = function () {
var shrinked = 0;
for (var y = this.stones.length - 1; y >= 0; y--) {
if (this.stones[y].reduce(function (r, s) {
return r && s !== null;}, true)) {
this.stones.splice(y, 1);
shrinked++;
}
}
for (var i = 0; i < shrinked; i++) {
var line = [];
for (var x = 0; x < this.width; x++) {
line.push(null);
}
this.stones.unshift(line);
}
};
Stage.prototype.reset = function () {
for (var y = 0; y < this.stones.length; y++) {
for (var x = 0; x < this.stones[y].length; x++) {
this.stones[y][x] = null;
}
}
};
Stage.prototype.eachStone = function (callback) {
for (var y = 0; y < this.stones.length; y++) {
var line = this.stones[y];
for (var x = 0; x <line.length; x++) {
var color = line[x];
if (color) callback(x, y, color);
}
}
};
// standard shapes
var shapes = [
Shape("I", "red",
[[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0]]),
Shape("J", "magenta",
[[0, 1, 0],
[0, 1, 0],
[1, 1, 0]]),
Shape("L", "yellow",
[[0, 1, 0],
[0, 1, 0],
[0, 1, 1]]),
Shape("T", "lightgrey",
[[1, 1, 1],
[0, 1, 0],
[0, 0, 0]]),
Shape("S", "blue",
[[0, 1, 1],
[1, 1, 0],
[0, 0, 0]]),
Shape("Z", "green",
[[1, 1, 0],
[0, 1, 1],
[0, 0, 0]]),
Shape("O", "cyan",
[[1, 1],
[1, 1]]),
];
exports.Tetris = {
shapes: shapes,
Shape: Shape,
Block: Block,
Stage: Stage,
};
})(typeof module === "undefined" ? this : module.exports);
@bellbind
Copy link
Author

bellbind commented Nov 22, 2012

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