Yeni23
JsTetris 1.1.0Game Over
Help x
Controllers:
up - rotate down - move down left - move left right - move right space - fall to the bottom n - new game r - reset Rules: 1) Puzzle speed = 80+700/level miliseconds, the smaller value the faster puzzle falls 2) If puzzles created in current level >= 10+level*2 then increase level 3) After puzzle falling score is increased by 1000*level*linesRemoved 4) Each "down" action increases score by 5+level (pressing space counts as multiple down actions) Highscores x
Note: these scores are kept in cookies, they are only visible to your computer, other players that visit this page see their own scores. |
<script type="text/javascript">
/* This script and many more are available free online at
The JavaScript Source!! http://kanalbizim.tr.gg
Created by: Cezary Tomczak | http://gosu.pl/Licensed under: BSD
*/
/*
* PROJECT: JsTetris
* VERSION: 1.1.0
* LICENSE: BSD (revised)
* AUTHOR: (c) 2004 Cezary Tomczak
* LINK: http://gosu.pl/dhtml/JsTetris.html
*
* This script can be used freely as long as all
* copyright messages are intact.
*/
/**
* Tetris Game
* Initializes the buttons automatically, no additional actions required
*
* Score:
* 1) puzzle speed = 80+700/level
* 2) if puzzles created in current level >= 10+level*2 then increase level
* 3) after puzzle falling score is increased by 1000*level*linesRemoved
* 4) each down action increases score by 5+level
*
* API:
*
* public - method can be called outside of the object
* event - method is used as event, "this" refers to html object, "self" refers to javascript object
*
* class Tetris
* ------------
* public event void start()
* public event void reset()
* public event void gameOver()
* public event void up()
* public event void down()
* public event void left()
* public event void right()
* public event void space()
*
* class Window
* ------------
* event void activate()
* event void close()
* public bool isActive()
*
* class Keyboard
* --------------
* public void set(int key, function func)
* event void event(object e)
*
* class Stats
* -----------
* public void start()
* public void stop()
* public void reset()
* public event void incTime()
* public void setScore(int i)
* public void setLevel(int i)
* public void setLines(int i)
* public void setPuzzles(int i)
* public void setActions(int i)
* public int getScore()
* public int getLevel()
* public int getLines()
* public int getPuzzles()
* public int getActions()
*
* class Area
* ----------
* public Constructor(int unit, int x, int y, string id)
* public void destroy()
* public int removeFullLines()
* public bool isLineFull(int y)
* public void removeLine(int y)
* public mixed getBlock(int y, int x)
* public void addElement(htmlObject el)
*
* class Puzzle
* ------------
* public Constructor(object area)
* public void reset()
* public bool isRunning()
* public bool isStopped()
* public int getX()
* public int getY()
* public bool mayPlace()
* public void place()
* public void destroy()
* private array createEmptyPuzzle(int y, int x)
* event void fallDown()
* public event void forceMoveDown()
* public void stop()
* public bool mayRotate()
* public void rotate()
* public bool mayMoveDown()
* public void moveDown()
* public bool mayMoveLeft()
* public void moveLeft()
* public bool mayMoveRight()
* public void moveRight()
*
* class Highscores
* ----------------
* public Constructor(maxscores)
* public void load()
* public void save()
* public bool mayAdd(int score)
* public void add(string name, int score)
* public array getScores()
* public string toHtml()
* private void sort()
*
* class Cookie
* ------------
* public string get(string name)
* public void set(string name, string value, int seconds, string path, string domain, bool secure)
* public void del(string name)
*
* TODO:
* document.getElementById("tetris-nextpuzzle") cache ?
*
*/
function Tetris() {
var self = this;
this.stats = new Stats();
this.puzzle = null;
this.area = null;
this.unit = 20; // unit = x pixels
this.areaX = 20; // area width = x units
this.areaY = 20; // area height = y units
this.highscores = new Highscores(10);
/**
* @return void
* @access public event
*/
this.start = function() {
self.reset();
self.stats.start();
document.getElementById("tetris-nextpuzzle").style.display = "block";
self.area = new Area(self.unit, self.areaX, self.areaY, "tetris-area");
self.puzzle = new Puzzle(self, self.area);
if (self.puzzle.mayPlace()) {
self.puzzle.place();
} else {
self.gameOver();
}
}
/**
* @return void
* @access public event
*/
this.reset = function() {
if (self.puzzle) {
self.puzzle.destroy();
self.puzzle = null;
}
if (self.area) {
self.area.destroy();
self.area = null;
}
document.getElementById("tetris-gameover").style.display = "none";
document.getElementById("tetris-nextpuzzle").style.display = "none";
self.stats.reset();
}
/**
* End game.
* Stop stats, ...
* @return void
* @access public event
*/
this.gameOver = function() {
self.stats.stop();
self.puzzle.stop();
document.getElementById("tetris-nextpuzzle").style.display = "none";
document.getElementById("tetris-gameover").style.display = "block";
if (this.highscores.mayAdd(this.stats.getScore())) {
var name = prompt("Game Over !nEnter your name:", "");
if (name && name.trim().length) {
this.highscores.add(name, this.stats.getScore());
}
}
}
/**
* @return void
* @access public event
*/
this.up = function() {
if (self.puzzle && self.puzzle.isRunning() && !self.puzzle.isStopped()) {
if (self.puzzle.mayRotate()) {
self.puzzle.rotate();
self.stats.setActions(self.stats.getActions() + 1);
}
}
}
/**
* @return void
* @access public event
*/
this.down = function() {
if (self.puzzle && self.puzzle.isRunning() && !self.puzzle.isStopped()) {
if (self.puzzle.mayMoveDown()) {
self.stats.setScore(self.stats.getScore() + 5 + self.stats.getLevel());
self.puzzle.moveDown();
self.stats.setActions(self.stats.getActions() + 1);
}
}
}
/**
* @return void
* @access public event
*/
this.left = function() {
if (self.puzzle && self.puzzle.isRunning() && !self.puzzle.isStopped()) {
if (self.puzzle.mayMoveLeft()) {
self.puzzle.moveLeft();
self.stats.setActions(self.stats.getActions() + 1);
}
}
}
/**
* @return void
* @access public event
*/
this.right = function() {
if (self.puzzle && self.puzzle.isRunning() && !self.puzzle.isStopped()) {
if (self.puzzle.mayMoveRight()) {
self.puzzle.moveRight();
self.stats.setActions(self.stats.getActions() + 1);
}
}
}
/**
* @return void
* @access public event
*/
this.space = function() {
if (self.puzzle && self.puzzle.isRunning() && !self.puzzle.isStopped()) {
self.puzzle.stop();
self.puzzle.forceMoveDown();
}
}
// windows
var helpwindow = new Window("tetris-help");
var highscores = new Window("tetris-highscores");
// game menu
document.getElementById("tetris-menu-start").onclick = function() { helpwindow.close(); highscores.close(); self.start(); this.blur(); }
document.getElementById("tetris-menu-reset").onclick = function() { helpwindow.close(); highscores.close(); self.reset(); this.blur(); }
// help
document.getElementById("tetris-menu-help").onclick = function() { highscores.close(); helpwindow.activate(); this.blur(); }
document.getElementById("tetris-help-close").onclick = helpwindow.close;
// highscores
document.getElementById("tetris-menu-highscores").onclick = function() {
helpwindow.close();
document.getElementById("tetris-highscores-content").innerHTML = self.highscores.toHtml();
highscores.activate();
this.blur();
}
document.getElementById("tetris-highscores-close").onclick = highscores.close;
// keyboard - buttons
document.getElementById("tetris-keyboard-up").onclick = function() { self.up(); this.blur(); }
document.getElementById("tetris-keyboard-down").onclick = function() { self.down(); this.blur(); }
document.getElementById("tetris-keyboard-left").onclick = function () { self.left(); this.blur(); }
document.getElementById("tetris-keyboard-right").onclick = function() { self.right(); this.blur(); }
// keyboard
var keyboard = new Keyboard();
keyboard.set(keyboard.n, this.start);
keyboard.set(keyboard.r, this.reset);
keyboard.set(keyboard.up, this.up);
keyboard.set(keyboard.down, this.down);
keyboard.set(keyboard.left, this.left);
keyboard.set(keyboard.right, this.right);
keyboard.set(keyboard.space, this.space);
document.onkeydown = keyboard.event;
/**
* Window replaces game area, for example help window
* @param string id
*/
function Window(id) {
this.id = id;
this.el = document.getElementById(this.id);
var self = this;
/**
* Activate or deactivate a window - update html
* @return void
* @access event
*/
this.activate = function() {
self.el.style.display = (self.el.style.display == "block" ? "none" : "block");
}
/**
* Close window - update html
* @return void
* @access event
*/
this.close = function() {
self.el.style.display = "none";
}
/**
* @return bool
* @access public
*/
this.isActive = function() {
return (self.el.style.display == "block");
}
}
/**
* Assigning functions to keyboard events
* When key is pressed, searching in a table if any function has been assigned to this key, execute the function.
*/
function Keyboard() {
this.up = 38;
this.down = 40;
this.left = 37;
this.right = 39;
this.n = 78;
this.r = 82;
this.space = 32;
this.f12 = 123;
this.escape = 27;
this.keys = [];
this.funcs = [];
var self = this;
/**
* @param int key
* @param function func
* @return void
* @access public
*/
this.set = function(key, func) {
this.keys.push(key);
this.funcs.push(func);
}
/**
* @param object e
* @return void
* @access event
*/
this.event = function(e) {
if (!e) { e = window.event; }
for (var i = 0; i < self.keys.length; i++) {
if (e.keyCode == self.keys[i]) {
self.funcs[i]();
}
}
}
}
/**
* Live game statistics
* Updating html
*/
function Stats() {
this.level;
this.time;
this.apm;
this.lines;
this.score;
this.puzzles; // number of puzzles created on current level
this.actions;
this.el = {
"level": document.getElementById("tetris-stats-level"),
"time": document.getElementById("tetris-stats-time"),
"apm": document.getElementById("tetris-stats-apm"),
"lines": document.getElementById("tetris-stats-lines"),
"score": document.getElementById("tetris-stats-score")
}
this.timerId = null;
var self = this;
/**
* Start counting statistics, reset stats, turn on the timer
* @return void
* @access public
*/
this.start = function() {
this.reset();
this.timerId = setInterval(this.incTime, 1000);
}
/**
* Stop counting statistics, turn off the timer
* @return void
* @access public
*/
this.stop = function() {
if (this.timerId) {
clearInterval(this.timerId);
}
}
/**
* Reset statistics - update html
* @return void
* @access public
*/
this.reset = function() {
this.stop();
this.level = 1;
this.time = 0;
this.apm = 0;
this.lines = 0;
this.score = 0;
this.puzzles = 0;
this.actions = 0;
this.el.level.innerHTML = this.level;
this.el.time.innerHTML = this.time;
this.el.apm.innerHTML = this.apm;
this.el.lines.innerHTML = this.lines;
this.el.score.innerHTML = this.score;
}
/**
* Increase time, update apm - update html
* This func is called by setInterval()
* @return void
* @access public event
*/
this.incTime = function() {
self.time++;
self.el.time.innerHTML = self.time;
self.apm = parseInt((self.actions / self.time) * 60);
self.el.apm.innerHTML = self.apm;
}
/**
* Set score - update html
* @param int i
* @return void
* @access public
*/
this.setScore = function(i) {
this.score = i;
this.el.score.innerHTML = this.score;
}
/**
* Set level - update html
* @param int i
* @return void
* @access public
*/
this.setLevel = function(i) {
this.level = i;
this.el.level.innerHTML = this.level;
}
/**
* Set lines - update html
* @param int i
* @return void
* @access public
*/
this.setLines = function(i) {
this.lines = i;
this.el.lines.innerHTML = this.lines;
}
/**
* Number of puzzles created on current level
* @param int i
* @return void
* @access public
*/
this.setPuzzles = function(i) {
this.puzzles = i;
}
/**
* @param int i
* @return void
* @access public
*/
this.setActions = function(i) {
this.actions = i;
}
/**
* @return int
* @access public
*/
this.getScore = function() {
return this.score;
}
/**
* @return int
* @access public
*/
this.getLevel = function() {
return this.level;
}
/**
* @return int
* @access public
*/
this.getLines = function() {
return this.lines;
}
/**
* Number of puzzles created on current level
* @return int
* @access public
*/
this.getPuzzles = function() {
return this.puzzles;
}
/**
* @return int
* @access public
*/
this.getActions = function() {
return this.actions;
}
}
/**
* Area consists of blocks (2 dimensional board).
* Block contains "0" (if empty) or Html Object.
* @param int x
* @param int y
* @param string id
*/
function Area(unit, x, y, id) {
this.unit = unit;
this.x = x;
this.y = y;
this.el = document.getElementById(id);
this.board = [];
// create 2-dimensional board
for (var y = 0; y < this.y; y++) {
this.board.push(new Array());
for (var x = 0; x < this.x; x++) {
this.board[y].push(0);
}
}
/**
* Removing html elements from area.
* @return void
* @access public
*/
this.destroy = function() {
for (var y = 0; y < this.board.length; y++) {
for (var x = 0; x < this.board[y].length; x++) {
if (this.board[y][x]) {
this.el.removeChild(this.board[y][x]);
this.board[y][x] = 0;
}
}
}
}
/**
* Searching for full lines.
* Must go from the bottom of area to the top.
* Returns the number of lines removed - needed for Stats.score.
* @see isLineFull() removeLine()
* @return void
* @access public
*/
this.removeFullLines = function() {
var lines = 0;
for (var y = this.y - 1; y > 0; y--) {
if (this.isLineFull(y)) {
this.removeLine(y);
lines++;
y++;
}
}
return lines;
}
/**
* @param int y
* @return bool
* @access public
*/
this.isLineFull = function(y) {
for (var x = 0; x < this.x; x++) {
if (!this.board[y][x]) { return false; }
}
return true;
}
/**
* Remove given line
* Remove html objects
* All lines that are above given line move down by 1 unit
* @param int y
* @return void
* @access public
*/
this.removeLine = function(y) {
for (var x = 0; x < this.x; x++) {
this.el.removeChild(this.board[y][x]);
this.board[y][x] = 0;
}
y--;
for (; y > 0; y--) {
for (var x = 0; x < this.x; x++) {
if (this.board[y][x]) {
var el = this.board[y][x];
el.style.top = el.offsetTop + this.unit + "px";
this.board[y+1][x] = el;
this.board[y][x] = 0;
}
}
}
}
/**
* @param int y
* @param int x
* @return mixed 0 or Html Object
* @access public
*/
this.getBlock = function(y, x) {
if (y < 0) { return 0; }
if (y < this.y && x < this.x) {
return this.board[y][x];
} else {
throw "Area.getBlock("+y+", "+x+") failed";
}
}
/**
* Add Html Element to the area.
* Find (x,y) position using offsetTop and offsetLeft
* @param object el
* @return void
* @access public
*/
this.addElement = function(el) {
var x = parseInt(el.offsetLeft / this.unit);
var y = parseInt(el.offsetTop / this.unit);
if (y >= 0 && y < this.y && x >= 0 && x < this.x) {
this.board[y][x] = el;
} else {
// not always an error ..
}
}
}
/**
* Puzzle consists of blocks.
* Each puzzle after rotating 4 times, returns to its primitive position.
*/
function Puzzle(tetris, area) {
var self = this;
this.tetris = tetris;
this.area = area;
// timeout ids
this.fallDownID = null;
this.forceMoveDownID = null;
this.type = null; // 0..6
this.nextType = null; // next puzzle
this.position = null; // 0..3
this.speed = null;
this.running = null;
this.stopped = null;
this.board = []; // filled with html elements after placing on area
this.elements = [];
this.nextElements = []; // next board elements
// (x,y) position of the puzzle (top-left)
this.x = null;
this.y = null;
// width & height must be the same
this.puzzles = [
[
[0,0,1],
[1,1,1],
[0,0,0]
],
[
[1,0,0],
[1,1,1],
[0,0,0]
],
[
[0,1,1],
[1,1,0],
[0,0,0]
],
[
[1,1,0],
[0,1,1],
[0,0,0]
],
[
[0,1,0],
[1,1,1],
[0,0,0]
],
[
[1,1],
[1,1]
],
[
[0,0,0,0],
[1,1,1,1],
[0,0,0,0],
[0,0,0,0]
]
];
/**
* Reset puzzle. It does not destroy html elements in this.board.
* @return void
* @access public
*/
this.reset = function() {
if (this.fallDownID) {
clearTimeout(this.fallDownID);
}
if (this.forceMoveDownID) {
clearTimeout(this.forceMoveDownID);
}
this.type = this.nextType;
this.nextType = random(this.puzzles.length);
this.position = 0;
this.speed = 80 + (700 / this.tetris.stats.getLevel());
this.running = false;
this.stopped = false;
this.board = [];
this.elements = [];
for (var i = 0; i < this.nextElements.length; i++) {
document.getElementById("tetris-nextpuzzle").removeChild(this.nextElements[i]);
}
this.nextElements = [];
this.x = null;
this.y = null;
}
this.nextType = random(this.puzzles.length);
this.reset();
/**
* Check whether puzzle is running.
* @return bool
* @access public
*/
this.isRunning = function() {
return this.running;
}
/**
* Check whether puzzle has been stopped by user. It happens when user clicks
* "down" when puzzle is already at the bottom of area. The puzzle may still
* be running with event fallDown(). When puzzle is stopped, no actions will be
* performed when user press a key.
* @return bool
* @access public
*/
this.isStopped = function() {
return this.stopped;
}
/**
* Get X position of puzzle (top-left)
* @return int
* @access public
*/
this.getX = function() {
return this.x;
}
/**
* Get Y position of puzzle (top-left)
* @return int
* @access public
*/
this.getY = function() {
return this.y;
}
/**
* Check whether new puzzle may be placed on the area.
* Find (x,y) in area where beginning of the puzzle will be placed.
* Check if first puzzle line (checking from the bottom) can be placed on the area.
* @return bool
* @access public
*/
this.mayPlace = function() {
var puzzle = this.puzzles[this.type];
var areaStartX = parseInt((this.area.x - puzzle[0].length) / 2);
var areaStartY = 1;
var lineFound = false;
var lines = 0;
for (var y = puzzle.length - 1; y >= 0; y--) {
for (var x = 0; x < puzzle[y].length; x++) {
if (puzzle[y][x]) {
lineFound = true;
if (this.area.getBlock(areaStartY, areaStartX + x)) { return false; }
}
}
if (lineFound) {
lines++;
}
if (areaStartY - lines < 0) {
break;
}
}
return true;
}
/**
* Create empty board, create blocks in area - html objects, update puzzle board.
* Check puzzles on current level, increase level if needed.
* @return void
* @access public
*/
this.place = function() {
// stats
this.tetris.stats.setPuzzles(this.tetris.stats.getPuzzles() + 1);
if (this.tetris.stats.getPuzzles() >= (10 + this.tetris.stats.getLevel() * 2)) {
this.tetris.stats.setLevel(this.tetris.stats.getLevel() + 1);
this.tetris.stats.setPuzzles(0);
}
// init
var puzzle = this.puzzles[this.type];
var areaStartX = parseInt((this.area.x - puzzle[0].length) / 2);
var areaStartY = 1;
var lineFound = false;
var lines = 0;
this.x = areaStartX;
this.y = 1;
this.board = this.createEmptyPuzzle(puzzle.length, puzzle[0].length);
// create puzzle
for (var y = puzzle.length - 1; y >= 0; y--) {
for (var x = 0; x < puzzle[y].length; x++) {
if (puzzle[y][x]) {
lineFound = true;
var el = document.createElement("div");
el.className = "block" + this.type;
el.style.left = (areaStartX + x) * this.area.unit + "px";
el.style.top = (areaStartY - lines) * this.area.unit + "px";
this.area.el.appendChild(el);
this.board[y][x] = el;
this.elements.push(el);
}
}
if (lines) {
this.y--;
}
if (lineFound) {
lines++;
}
}
this.running = true;
this.fallDownID = setTimeout(this.fallDown, this.speed);
// next puzzle
var nextPuzzle = this.puzzles[this.nextType];
for (var y = 0; y < nextPuzzle.length; y++) {
for (var x = 0; x < nextPuzzle[y].length; x++) {
if (nextPuzzle[y][x]) {
var el = document.createElement("div");
el.className = "block" + this.nextType;
el.style.left = (x * this.area.unit) + "px";
el.style.top = (y * this.area.unit) + "px";
document.getElementById("tetris-nextpuzzle").appendChild(el);
this.nextElements.push(el);
}
}
}
}
/**
* Remove puzzle from the area.
* Clean some other stuff, see reset()
* @return void
* @access public
*/
this.destroy = function() {
for (var i = 0; i < this.elements.length; i++) {
this.area.el.removeChild(this.elements[i]);
}
this.elements = [];
this.board = [];
this.reset();
}
/**
* @param int y
* @param int x
* @return array
* @access private
*/
this.createEmptyPuzzle = function(y, x) {
var puzzle = [];
for (var y2 = 0; y2 < y; y2++) {
puzzle.push(new Array());
for (var x2 = 0; x2 < x; x2++) {
puzzle[y2].push(0);
}
}
return puzzle;
}
/**
* Puzzle fall from the top to the bottom.
* After placing a puzzle, this event will be called as long as the puzzle is running.
* @see place() stop()
* @return void
* @access event
*/
this.fallDown = function() {
if (self.isRunning()) {
if (self.mayMoveDown()) {
self.moveDown();
self.fallDownID = setTimeout(self.fallDown, self.speed);
} else {
// move blocks into area board
for (var i = 0; i < self.elements.length; i++) {
self.area.addElement(self.elements[i]);
}
// stats
var lines = self.area.removeFullLines();
if (lines) {
self.tetris.stats.setLines(self.tetris.stats.getLines() + lines);
self.tetris.stats.setScore(self.tetris.stats.getScore() + (1000 * self.tetris.stats.getLevel() * lines));
}
// reset puzzle
self.reset();
if (self.mayPlace()) {
self.place();
} else {
self.tetris.gameOver();
}
}
}
}
/**
* After clicking "space" the puzzle is forced to move down, no user action is performed after
* this event is called. this.running must be set to false. This func is similiar to fallDown()
* Also update score & actions - like Tetris.down()
* @see fallDown()
* @return void
* @access public event
*/
this.forceMoveDown = function() {
if (!self.isRunning() && !self.isStopped()) {
if (self.mayMoveDown()) {
// stats: score, actions
self.tetris.stats.setScore(self.tetris.stats.getScore() + 5 + self.tetris.stats.getLevel());
self.tetris.stats.setActions(self.tetris.stats.getActions() + 1);
self.moveDown();
self.forceMoveDownID = setTimeout(self.forceMoveDown, 30);
} else {
// move blocks into area board
for (var i = 0; i < self.elements.length; i++) {
self.area.addElement(self.elements[i]);
}
// stats: lines
var lines = self.area.removeFullLines();
if (lines) {
self.tetris.stats.setLines(self.tetris.stats.getLines() + lines);
self.tetris.stats.setScore(self.tetris.stats.getScore() + (1000 * self.tetris.stats.getLevel() * lines));
}
// reset puzzle
self.reset();
if (self.mayPlace()) {
self.place();
} else {
self.tetris.gameOver();
}
}
}
}
/**
* Stop the puzzle falling
* @return void
* @access public
*/
this.stop = function() {
this.running = false;
}
/**
* Check whether puzzle may be rotated.
* Check down, left, right, rotate
* @return bool
* @access public
*/
this.mayRotate = function() {
for (var y = 0; y < this.board.length; y++) {
for (var x = 0; x < this.board[y].length; x++) {
if (this.board[y][x]) {
var newY = this.getY() + this.board.length - 1 - x;
var newX = this.getX() + y;
if (newY >= this.area.y) { return false; }
if (newX < 0) { return false; }
if (newX >= this.area.x) { return false; }
if (this.area.getBlock(newY, newX)) { return false; }
}
}
}
return true;
}
/**
* Rotate the puzzle to the left.
* @return void
* @access public
*/
this.rotate = function() {
var puzzle = this.createEmptyPuzzle(this.board.length, this.board[0].length);
for (var y = 0; y < this.board.length; y++) {
for (var x = 0; x < this.board[y].length; x++) {
if (this.board[y][x]) {
var newY = puzzle.length - 1 - x;
var newX = y;
var el = this.board[y][x];
var moveY = newY - y;
var moveX = newX - x;
el.style.left = el.offsetLeft + (moveX * this.area.unit) + "px";
el.style.top = el.offsetTop + (moveY * this.area.unit) + "px";
puzzle[newY][newX] = el;
}
}
}
this.board = puzzle;
}
/**
* Check whether puzzle may be moved down.
* - is any other puzzle on the way ?
* - is it end of the area ?
* If false, then true is assigned to variable this.stopped - no user actions will be performed to this puzzle,
* so this func should be used carefully, only in Tetris.down() and Tetris.puzzle.fallDown()
* @return bool
* @access public
*/
this.mayMoveDown = function() {
for (var y = 0; y < this.board.length; y++) {
for (var x = 0; x < this.board[y].length; x++) {
if (this.board[y][x]) {
if (this.getY() + y + 1 >= this.area.y) { this.stopped = true; return false; }
if (this.area.getBlock(this.getY() + y + 1, this.getX() + x)) { this.stopped = true; return false; }
}
}
}
return true;
}
/**
* Move the puzzle down by 1 unit.
* @return void
* @access public
*/
this.moveDown = function() {
for (var i = 0; i < this.elements.length; i++) {
this.elements[i].style.top = this.elements[i].offsetTop + this.area.unit + "px";
}
this.y++;
}
/**
* Check whether puzzle may be moved left.
* - is any other puzzle on the way ?
* - is the end of the area
* @return bool
* @access public
*/
this.mayMoveLeft = function() {
for (var y = 0; y < this.board.length; y++) {
for (var x = 0; x < this.board[y].length; x++) {
if (this.board[y][x]) {
if (this.getX() + x - 1 < 0) { return false; }
if (this.area.getBlock(this.getY() + y, this.getX() + x - 1)) { return false; }
}
}
}
return true;
}
/**
* Move the puzzle left by 1 unit
* @return void
* @access public
*/
this.moveLeft = function() {
for (var i = 0; i < this.elements.length; i++) {
this.elements[i].style.left = this.elements[i].offsetLeft - this.area.unit + "px";
}
this.x--;
}
/**
* Check whether puzle may be moved right.
* - is any other puzzle on the way ?
* - is the end of the area
* @return bool
* @access public
*/
this.mayMoveRight = function() {
for (var y = 0; y < this.board.length; y++) {
for (var x = 0; x < this.board[y].length; x++) {
if (this.board[y][x]) {
if (this.getX() + x + 1 >= this.area.x) { return false; }
if (this.area.getBlock(this.getY() + y, this.getX() + x + 1)) { return false; }
}
}
}
return true;
}
/**
* Move the puzzle right by 1 unit.
* @return void
* @access public
*/
this.moveRight = function() {
for (var i = 0; i < this.elements.length; i++) {
this.elements[i].style.left = this.elements[i].offsetLeft + this.area.unit + "px";
}
this.x++;
}
}
/**
* Generates random number that is >= 0 and < i
* @return int
* @access private
*/
function random(i) {
return Math.floor(Math.random() * i);
}
/**
* Store highscores in cookie.
*/
function Highscores(maxscores) {
this.maxscores = maxscores;
this.scores = [];
/**
* Load scores from cookie.
* Note: it is automatically called when creating new instance of object Highscores.
* @return void
* @access public
*/
this.load = function() {
var cookie = new Cookie();
var s = cookie.get("tetris-highscores");
this.scores = [];
if (s.length) {
var scores = s.split("|");
for (var i = 0; i < scores.length; ++i) {
var a = scores[i].split(":");
this.scores.push(new Score(a[0], Number(a[1])));
}
}
}
/**
* Save scores to cookie.
* Note: it is automatically called after adding new score.
* @return void
* @access public
*/
this.save = function() {
var cookie = new Cookie();
var a = [];
for (var i = 0; i < this.scores.length; ++i) {
a.push(this.scores[i].name+":"+this.scores[i].score);
}
var s = a.join("|");
cookie.set("tetris-highscores", s, 3600*24*1000);
}
/**
* Is the score high enough to be able to add ?
* @return bool
* @access public
*/
this.mayAdd = function(score) {
if (this.scores.length < this.maxscores) { return true; }
for (var i = this.scores.length - 1; i >= 0; --i) {
if (this.scores[i].score < score) { return true; }
}
return false;
}
/**
* @param string name
* @param int score
* @return void
* @access public
*/
this.add = function(name, score) {
name = name.replace(/[;=:|]/g, "?");
name = name.replace(/</g, "<").replace(/>/g, ">");
if (this.scores.length < this.maxscores) {
this.scores.push(new Score(name, score));
} else {
for (var i = this.scores.length - 1; i >= 0; --i) {
if (this.scores[i].score < score) {
this.scores.removeByIndex(i);
this.scores.push(new Score(name, score));
break;
}
}
}
this.sort();
this.save();
}
/**
* Get array of scores.
* @return array [Score, Score, ..]
* @access public
*/
this.getScores = function() {
return this.scores;
}
/**
* All highscores returned in html friendly format.
* @return string
* @access public
*/
this.toHtml = function() {
var s = '<table cellspacing="0" cellpadding="2"><tr><th></th><th>Name</th><th>Score</th></tr>';
for (var i = 0; i < this.scores.length; ++i) {
s += '<tr><td>?.</td><td>?</td><td>?</td></tr>'.format(i+1, this.scores[i].name, this.scores[i].score);
}
s += '</table>';
return s;
}
/**
* Sort table with scores.
* @return void
* @access private
*/
this.sort = function() {
var scores = this.scores;
var len = scores.length;
this.scores = [];
for (var i = 0; i < len; ++i) {
var el = null, index = null;
for (var j = 0; j < scores.length; ++j) {
if (!el || (scores[j].score > el.score)) {
el = scores[j];
index = j;
}
}
scores.removeByIndex(index);
this.scores.push(el);
}
}
/* Simple score object. */
function Score(name, score) {
this.name = name;
this.score = score;
}
this.load();
}
/**
* Managing cookies.
*/
function Cookie() {
/**
* @param string name
* @return string
* @access public
*/
this.get = function(name) {
var cookies = document.cookie.split(";");
for (var i = 0; i < cookies.length; ++i) {
var a = cookies[i].split("=");
if (a.length == 2) {
a[0] = a[0].trim();
a[1] = a[1].trim();
if (a[0] == name) {
return unescape(a[1]);
}
}
}
return "";
};
/**
* @param string name
* @param string value (do not use special chars like ";" "=")
* @param int seconds
* @param string path
* @param string domain
* @param bool secure
* @return void
* @access public
*/
this.set = function(name, value, seconds, path, domain, secure) {
var cookie = (name + "=" + escape(value));
if (seconds) {
var date = new Date(new Date().getTime()+seconds*1000);
cookie += ("; expires="+date.toGMTString());
}
cookie += (path ? "; path="+path : "");
cookie += (domain ? "; domain="+domain : "");
cookie += (secure ? "; secure" : "");
document.cookie = cookie;
};
/**
* @param name
* @return void
* @access public
*/
this.del = function(name) {
document.cookie = name + "=; expires=Thu, 01-Jan-70 00:00:01 GMT";
};
}
}
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^s*|s*$/g, "");
};
}
if (!Array.prototype.removeByIndex) {
Array.prototype.removeByIndex = function(index) {
this.splice(index, 1);
};
}
if (!String.prototype.format) {
String.prototype.format = function() {
if (!arguments.length) { throw "String.format() failed, no arguments passed, this = "+this; }
var tokens = this.split("?");
if (arguments.length != (tokens.length - 1)) { throw "String.format() failed, tokens != arguments, this = "+this; }
var s = tokens[0];
for (var i = 0; i < arguments.length; ++i) {
s += (arguments[i] + tokens[i + 1]);
}
return s;
};
}
</script>
<style>
html, body { height: 100%; margin: 0; padding: 0; }
body {
background: #E1D4C0;
}
body, table {
font: 11px tahoma;
color: #826C55;
}
/*** tetris 168,308 ***/
#tetris {
position: relative;
width: 300px;
height: 310px;
border: 1px solid #BAA68E;
background: #ffffff;
margin: 0 auto;
}
/*** left ***/
#tetris .left {
background: #F5EDE3;
position: absolute;
width: 130px;
height: 100%;
left: 0px;
top: 0px;
}
#tetris .left h1 {
font-size: 11px;
text-align: center;
margin-top: 10px;
margin-bottom: 10px;
}
#tetris .left h1 a {
color: #3366CC;
text-decoration: none;
}
#tetris .left h1 a:hover {
color: #FF6600;
text-decoration: none;
}
/* menu */
#tetris .left .menu {
text-align: center;
}
#tetris .left input {
font: 10px tahoma;
color: #333333;
text-transform: uppercase;
background: #EAE0D1;
}
#tetris .left .menu input {
width: 90px;
}
/* keyboard */
#tetris .left .keyboard {
position: absolute;
top: 163px;
left: 32px;
width: 85px;
height: 55px;
overflow: visible;
display: none;
}
#tetris .left .keyboard input {
font: 11px tahoma;
width: 25px;
height: 25px;
padding-bottom: 2px;
text-transform: none;
}
* html #tetris .left .keyboard input {
padding-left: 1px;
}
#tetris .left .keyboard .up {
position: absolute;
left: 30px;
top: 0px;
width: 30px;
height: 30px;
}
#tetris .left .keyboard .up input {
font: 15px tahoma;
padding-top: 3px;
}
#tetris .left .keyboard .down {
position: absolute;
left: 30px;
top: 30px;
width: 30px;
height: 30px;
}
#tetris .left .keyboard .down input {
font: 14px tahoma;
}
#tetris .left .keyboard .left {
position: absolute;
left: 0px;
top: 30px;
width: 30px;
height: 30px;
}
#tetris .left .keyboard .right {
position: absolute;
left: 60px;
top: 30px;
width: 30px;
height: 30px;
}
/* game over */
#tetris-gameover {
position: absolute;
width: 100%;
top: 50%;
text-align: center;
font-weight: bold;
display: none;
}
/* next puzzle */
#tetris-nextpuzzle {
position: absolute;
top: 49%;
left: 35%;
background: #ffffff;
overflow: visible;
display: none;
}
/* stats */
#tetris .left .stats {
position: absolute;
left: 35px;
bottom: 10px;
}
#tetris .stats td { padding-bottom: 1px; }
#tetris .stats .level { text-align: right; padding-right: 10px; }
#tetris-stats-level { font-weight: bold; }
#tetris .stats .time { text-align: right; padding-right: 10px; }
#tetris-stats-time { font-weight: bold; }
#tetris .stats .apm { text-align: right; padding-right: 10px; }
#tetris-stats-apm { font-weight: bold; }
#tetris .stats .lines { text-align: right; padding-right: 10px; }
#tetris-stats-lines { font-weight: bold; }
#tetris .stats .score { text-align: right; padding-right: 10px; }
#tetris-stats-score { font-weight: bold; }
/*** area ***/
#tetris-area {
background: #FFFFFF;
position: absolute;
width: 168px;
height: 308px;
left: 131px;
top: 1px;
overflow: hidden;
}
#tetris .block0,
#tetris .block1,
#tetris .block2,
#tetris .block3,
#tetris .block4,
#tetris .block5,
#tetris .block6 {
position: absolute;
width: 13px;
height: 13px;
border: 0.5px solid #ffffff;
/* with margin 0.5px there were problems with offsetLeft and offsetTop */
}
#tetris .block0,
#tetris .block1 {
background: #6699FF;
}
#tetris .block2,
#tetris .block3 {
background: #FF6600;
}
#tetris .block4 {
background: #FFAC1C;
}
#tetris .block5 {
background: #BAA68E;
}
#tetris .block6 {
background: #FF0000;
}
/*** window ***/
#tetris .window {
background: #EFE8DE;
position: absolute;
width: 168px;
height: 308px;
left: 131px;
top: 1px;
z-index: 5;
display: none;
}
#tetris .window .top {
position: relative;
background: #EAE0D1;
color: #666666;
font: 10px tahoma;
letter-spacing: +1px;
height: 20px;
line-height: 20px;
vertical-align: middle;
border-bottom: 1px solid #ffffff;
text-indent: 10px;
}
#tetris .window .top .close {
position: absolute;
background: #EAE0D1;
font: 11px verdana;
font-weight: bold;
right: 0px;
top: 0px;
height: 20px;
line-height: 19px;
text-indent: 7px;
width: 21px;
border-left: 1px solid #ffffff;
cursor: pointer;
}
#tetris .window .top .close:hover {
background: #EFE8DE;
}
#tetris .window .content {
font: 10px tahoma;
margin: 10px;
}
#tetris .window .content table {
font: 10px tahoma;
}
</style>
<table cellspacing="0" cellpadding="0" width="100%" height="100%"><tr><td valign="middle">
<div id="tetris">
<div class="left">
<h1>JsTetris 1.1.0</h1>
<div class="menu">
<div><input type="button" value="New Game" id="tetris-menu-start" /></div>
<div><input type="button" value="Reset" id="tetris-menu-reset" /></div>
<div><input type="button" value="Help" id="tetris-menu-help" /></div>
<div><input type="button" value="Highscores" id="tetris-menu-highscores" /></div>
</div>
<div class="keyboard">
<div class="up"><input type="button" value="^" id="tetris-keyboard-up" /></div>
<div class="down"><input type="button" value="v" id="tetris-keyboard-down" /></div>
<div class="left"><input type="button" value="<" id="tetris-keyboard-left" /></div>
<div class="right"><input type="button" value=">" id="tetris-keyboard-right" /></div>
</div>
<div id="tetris-nextpuzzle"></div>
<div id="tetris-gameover">Game Over</div>
<div class="stats">
<table cellspacing="0" cellpadding="0">
<tr>
<td class="level">Level:</td>
<td><span id="tetris-stats-level">1</span></td>
</tr>
<tr>
<td class="score">Score:</td>
<td><span id="tetris-stats-score">0</span></td>
</tr>
<tr>
<td class="lines">Lines:</td>
<td><span id="tetris-stats-lines">0</span></td>
</tr>
<tr>
<td class="apm">APM:</td>
<td><span id="tetris-stats-apm">0</span></td>
</tr>
<tr>
<td class="time">Time:</td>
<td><span id="tetris-stats-time">0</span></td>
</tr>
</table>
</div>
</div>
<div id="tetris-area"></div>
<div id="tetris-help" class="window">
<div class="top">
Help <span id="tetris-help-close" class="close">x</span>
</div>
<div class="content">
<b>Controllers:</b> <br />
up - rotate <br />
down - move down <br />
left - move left <br />
right - move right <br />
space - fall to the bottom <br />
n - new game <br />
r - reset <br />
<br />
<b>Rules:</b> <br />
1) Puzzle speed = 80+700/level miliseconds, the smaller value the faster puzzle falls <br />
2) If puzzles created in current level >= 10+level*2 then increase level <br />
3) After puzzle falling score is increased by 1000*level*linesRemoved <br />
4) Each "down" action increases score by 5+level (pressing space counts as multiple down actions)
</div>
</div>
<div id="tetris-highscores" class="window">
<div class="top">
Highscores <span id="tetris-highscores-close" class="close">x</span>
</div>
<div class="content">
<div id="tetris-highscores-content"></div>
<br />
Note: these scores are kept in cookies, they are only visible to your computer, other players that visit this page see their own scores.
</div>
</div>
</div>
</td></tr></table>
<script type="text/javascript">
var tetris = new Tetris();
tetris.unit = 14;
tetris.areaX = 12;
tetris.areaY = 22;
</script>