Created
November 20, 2012 09:22
-
-
Save bellbind/4116929 to your computer and use it in GitHub Desktop.
[javascript][svg]tetris model with svg ui (arrow key control)
This file contains 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> | |
<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> |
This file contains 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
"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); |
This file contains 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
"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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
demo