SoFunction
Updated on 2025-03-02

JS Tetris, including complete design concepts


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:///TR/xhtml1/DTD/">
<html xmlns="http:///1999/xhtml" >
<head>
<title></title>
<script type="text/javascript">
/******************************js Tetris source code************************************/
//Author: Gaoshan Flowing Water QQ: 21243468
//Creation date: 2009-08-06
//Copyright Statement: This work is created by Gaoshan Liushui. Please indicate the source when reprinting. Thank you for your cooperation!
//Game design instructions:
//1. Since the game is a two-dimensional game, laying out the grid is the key to writing the game well, whether it is a game window or a preview window,
//The concept of grid is used by the virtual map of the block set. The benefits of this can avoid frequent acquisition of element positions, and also move the block set
//Perform accurate positioning and deformation. The more important point here is to define the map of the block set in advance. For example, for the L block set, you should define a three-by-three square.
//Grid, then according to each shape of L, determine the position of each square block in the square grid. In addition, you need to save the square set in map
//The position in (game window or preview window), so that at any time, you can determine the position of the block set and the position of the block in the square grid.
//The position of each block in the block set is in the map.
//2. This game mainly uses some OOP ideas. For example, define a base class base, the block set class inherits from the base class, and there are also object encapsulation and attributes,
//Definition of enumeration, etc., of course, there are events, delegations, attributes, garbage collectors, etc. Due to time constraints, I won’t make code demonstrations. If you are interested, you can do it yourself.
//Extend it to deepen your understanding of js OOP.
//Extensions of built-in objects: For example, etc.
/******************************js Tetris source code************************************/
var Sys = null;
function sys() { }
= {
GameMap: [],
PreviewMap: [],
BlocksObj: [],
Timer: null,
HorizontalNum: 10, //The horizontal grid number of game map
VerticalNum: 18, //The vertical grid number of game map
IsGameOver: false, //Judge whether the game is over
ScoreStrategy: [100, 300, 500, 800], //Score strategy
LevelScores: [100, 20000, 40000, 60000, 80000, 100000, 120000, 140000, 160000, 200000], //Score level
IsPlay: false, //The game is in progress
IsFirstPlay: true, //Is it the first time to play
SmallGridNum: 6, //Preview the grid number of map, which is a square
DirectionEnum: { left: 0, right: 1, up: 2, down: 3 }, //Enum of directions
Speeds: [1000, 900, 800, 700, 600, 500, 400, 300, 200, 100], //Speed, or level
CurrentSpeed: 1000, //Current level or speed
TypeEnum: { none: 0, block: 1, blocks: 2 }, //Type
BlocksEnum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], //0 is LL, 1 is LR, 2 is T, 3 is ZL, 4 is ZR, 5 is I, 6 is F, 7 is long T
BlocksStateNum: [4, 4, 4, 2, 2, 2, 1, 4, 4, 2], //The number of deformations corresponding to each block set in BlocksEnum, that is, how many deformations are there
BlocksShapeMaps: [ // Collection of block shape sets
[ //The virtual map collection of block sets corresponds to BlocksEnum, for example, the map is L's map
[[[2, 1]], [[0, 2], [1, 2], [2, 2]]], //One of the deformations in L
[[[1, 0]], [[1, 1]], [[1, 2], [2, 2]]],
[[[0, 1], [1, 1], [2, 1]], [[0, 2]]],
[[[1, 0], [2, 0]], [[2, 1]], [[2, 2]]]
],
[
[[[2, 0]], [[2, 1]], [[1, 2], [2, 2]]],
[[[0, 1]], [[0, 2], [1, 2]], [[2, 2]]],
[[[0, 0], [1, 0]], [[0, 1]], [[0, 2]]],
[[[0, 1], [1, 1], [2, 1]], [[2, 2]]]
],
[
[[[0, 0], [1, 0], [2, 0]], [[1, 1]]],
[[[1, 0]], [[0, 1], [1, 1]], [[1, 2]]],
[[[1, 1]], [[0, 2], [1, 2], [2, 2]]],
[[[0, 0]], [[0, 1], [1, 1]], [[0, 2]]]
],
[
[[[0, 0]], [[0, 1], [1, 1]], [[1, 2]]],
[[[1, 1], [2, 1]], [[0, 2], [1, 2]]]
],
[
[[[1, 0]], [[0, 1], [1, 1]], [[0, 2]]],
[[[0, 1], [1, 1]], [[1, 2], [2, 2]]]
],
[
[[[0, 0]], [[0, 1]], [[0, 2]], [[0, 3]]],
[[[0, 3]], [[1, 3]], [[2, 3]], [[3, 3]]]
],
[
[[[0, 0], [0, 1]], [[1, 0], [1, 1]]]
],
[
[[[0, 0], [1, 0], [2, 0]], [[1, 1]], [[1, 2]]],
[[[2, 0]], [[0, 1], [1, 1], [2, 1]], [[2, 2]]],
[[[1, 0]], [[1, 1]], [[0, 2], [1, 2], [2, 2]]],
[[[0, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2]]]
],
[
[[[0, 1], [1, 1], [2, 1]], [[0, 2]], [[2, 2]]],
[[[1, 0], [2, 0]], [[2, 1]], [[1, 2], [2, 2]]],
[[[0, 1], [2, 1]], [[0, 2], [1, 2], [2, 2]]],
[[[1, 0], [2, 0]], [[1, 1]], [[1, 2], [2, 2]]]
],
[
[[[0, 0], [1, 0]], [[1, 1]], [[1, 2], [2, 2]]],
[[[2, 0]], [[0, 1], [1, 1], [2, 1]], [[0, 2]]]
],
],
ColorEnum: [[0, 0], [-28, 0], [-56, 0], [-84, 0], [-112, 0], [-140, 0], [-168, 0], [0, 0], [-28, 0], [-56, 0]], // Enumeration of colors, corresponding to BlocksEnum
CreateGameMap: function() { //Create game map
for (var i = 0; i < ; i++) {
([]);
for (var j = 0; j < ; j++) {
[i][j] = {};
[i][j][] = null;
}
}
},
GetBlocks: function() { //Get the set of blocks in GameMap
for (var i = 0; i < ; i++) {
if ([i].isInGameMap) {
return [i];
}
}
return null;
},
AllowBlocksMove: function() { //Whether to allow block set to move
var blocksItem = ();
var itemPosArray = this._getBlocksItemPosArray(blocksItem, false);
return (itemPosArray, blocksItem) && (itemPosArray, blocksItem);
},
GetMaxAndMinItemPosArray: function(itemPosArray) { //Get the maximum and minimum block position collection
();
();
return { maxItemPosX: [ - 1], maxItemPosY: [ - 1], minItemPosX: [0], minItemPosY: [0] };
},
NoBlocksInthePlace: function(itemPosArray, blocksItem) { //Check whether there are blocks in the game grid
return this._isOverMapChild(itemPosArray, blocksItem) ? false : true;
},
CheckBoundary: function(itemPosArray, blocksItem) { //Whether the boundary has been reached
var maxAndMinItemPosArray = (itemPosArray);
var isNotToBoundary = false;
switch () {
case :
isNotToBoundary = ( > 0)
break;
case :
isNotToBoundary = ( < - 1)
break;
case :
isNotToBoundary = ( < - 1);
break;
}
return isNotToBoundary;
},
_isOverMapChild: function(itemPosArray, blocksItem) { //Detection whether elements in a certain game square will be overwritten
var isOverMapChild = false;
for (var i = 0; i < ; i++) {
var itemX = [i];
var itemY = [i];
if ( == ) {
itemX--;
}
else if ( == ) {
itemX++;
}
else if ( == ) {
itemY++;
}
if ([itemY] && [itemY][itemX] && [itemY][itemX][] != null) {
isOverMapChild = true;
break;
}
}
return isOverMapChild;
},
_getBlocksItemPosArray: function(blocksItem, isRelative) { //Get the location set of block sets, isRelative=true, which gets the location of the block set map, otherwise it is the location of the block relative to the game map
var itemPosXArray = [];
var itemPosYArray = [];
for (var i = 0; i < ; i++) {
if (isRelative) {
([i].x);
([i].y);
}
else {
([i].x + );
([i].y + );
}
}
return { ItemPosXArray: itemPosXArray, ItemPosYArray: itemPosYArray };
},
GetBlocksInitPos: function(blocks) { //Get the initial position of the block
var blocksItem = null;
if (!blocks)
blocksItem = ();
else
blocksItem = blocks;
var itemPosArray = this._getBlocksItemPosArray(blocksItem, true);
= ();
= ();
var childsNumX = ;
var childsNumY = ;
var maxAndMinItemPosArray = (itemPosArray);
if (blocks) //Get the initial position of the block set in the preview map
return { x: ( - childsNumX - 1) / 2 + 0.5 - , y: ( - childsNumY - 1) / 2 + 0.5 - };
else //Get the initial position of the block set in the game map
return { x: parseInt(( - childsNumX - 1) / 2) + 1 - , y: -(childsNumY + ) };
},
GetNextActiveBrocks: function() { //Get the next activity's block set
for (var i = 0; i < ; i++) {
if ([i].isInGameMap) {
(i);
}
}
[0].isInGameMap = true;
var itemPos = ();
[0].x = ;
[0].y = ;
[0].AddToMap(false, false);
();
},
PlayGame: function() { //Start the game
= true;
();
if (!) {
return;
}
();
},
AddToGameMapGrid: function() { //Add to the game map grid
var blocks = ();
(, blocks);
},
GetScore: function() { //Score processing
var rowIndexArray = [];
for (var i = 0; i < ; i++) { //Get the number of rows full
var entireRow = true;
for (var j = 0; j < ; j++) {
if ([i][j][] == null) {
entireRow = false;
break;
}
}
if (entireRow)
(i);
}
if ( > 0) {
this._FreeMapGrid(rowIndexArray);
("score").innerText = [ - 1] + parseInt(("score").innerText);
();
}
},
CheckTheLevel: function() { //Check whether to enter the next level
var currentScore = parseInt(("score").innerText);
var speedList = ("speed");
var currentLevel = parseInt([].text) - 1;
var levelScore = [currentLevel];
if (currentScore >= levelScore) {
if (currentLevel < ) {
var element = ("gameOver");
= "Congratulations on passing the first" + ( + 1) + "off";
= "block";
();
("btnStart").disabled = true;
("speed").disabled = true;
this._goToNextLevel.delay(3000);
}
else {
this._finishAllTheLevel(element);
}
}
},
_goToNextLevel: function() { // Enter the next level, speed up
= true;
("btnStart").disabled = false;
var speedList = ("speed");
= false;
[ + 1].selected = true;
= [ + 1];
();
("gameOver"). = "none";
},
_finishAllTheLevel: function() { // Complete all game levels
();
},
_FreeMapGrid: function(rowIndexArray) { //Release the grid full of rows from the game map
var gameMap = ;
var startIndex = rowIndexArray[0];
var len = ;
var maxIndex = startIndex + len - 1;
for (var i = startIndex; i <= maxIndex; i++) {
for (var j = 0; j < ; j++) {
if (gameMap[i][j][] != null) {
("map").removeChild(gameMap[i][j][].domElement);
gameMap[i][j][] = null;
}
}
}
(rowIndexArray);
},
ResetMapGrid: function(rowIndexArray) { //Reset the game grid
var gameMap = ;
var maxIndex = rowIndexArray[0];
var len = ;
for (var i = maxIndex - 1; i >= 0; i--) {
for (var j = 0; j < ; j++) {
if (gameMap[i][j][] != null) {
this._resetMapElement(gameMap[i][j][].domElement, len);
gameMap[i + len][j][] = gameMap[i][j][];
gameMap[i][j][] = null;
}
}
}
},
_resetMapElement: function(element, len) { //Reset the dom element, for example, there are two full rows, then the above elements need to be reduced by two
= (parseInt() + 28 * len) + "px";
},
InitSpeed: function() { //Initialize the game level
var speedList = ("speed");
if ( == 0) {
for (var i = 0; i < ; i++) {
var varItem = new Option(i + 1, [i]);
(varItem);
}
}
();
},
SetSpeedSelected: function() { //Select level
var speedList = ("speed");
for (var i = 0; i < ; i++) {
if ([i].value == ) {
[i].selected = true;
break;
}
}
},
GameOver: function() { //The game ends
= true;
();
var element = ("gameOver");
= "Game Over!";
= "block";
("btnStart").value = "try again";
},
PauseGame: function() { //Pause the game
= false;
clearInterval();
},
CreateBlocks: function() { //Create a block set
var currentNum = ();
var blocks = new Blocks(0, 0, [currentNum], currentNum, [currentNum]);
();
if ( == 3)
();
(blocks);
},
NaturalMove: function() { //Natural fall
= setInterval("Moving()", );
}
}
function Base() { } //Define base class
= function(isAddToPreviewMap, isMoving) { //Add a block set to the map
for (var i = 0; i < ; i++) {
var element = null;
if (!) { //If the block set is in the preview map
element = ("DIV");
("PreviewMap").appendChild(element);
(element);
[i].domElement = element;
}
else
element = [i];
if (!isAddToPreviewMap && !isMoving) //When moving from preview map to game map
("map").appendChild(element);
= "absolute";
= (( + [i].x) * 28) + "px"; //Set the location of the map where the element is located
= (( + [i].y) * 28) + "px";
= [0];
= [1];
}
}
= function(map, blocksItem) { //Add block set to the game map
for (var i = 0; i < ; i++) {
var itemX = + [i].x;
var itemY = + [i].y;
if ( < 0) {
();
return;
}
map[itemY][itemX] = {};
map[itemY][itemX][] = [i];
}
}
function Block(x, y) { //Define the block structure
= x;
= y;
= ;
= null;
}
function Blocks(x, y, state, blocksEnum, colorEnum) { //Blockset class
= x;
= y;
= state;
= blocksEnum; //Block type (such as L, I, field shape, Z, etc.)
= colorEnum;
= ; //Abandoned attributes
= []; //The set of blocks under the block set
= []; //The corresponding dom element set of blocks under the block set
= 0; //The current state, such as L, has four types of deformation
= false; //Is it in the game map
= ; //Default direction downward
}
= new Base(); //Inherit base class
= function() {//Initialize blocks
var blocksPoses = [];
= [].getRandom(); //Random get the status of the block set
var blocksPos = blocksPoses[]; //Get the map of the block set
for (var i = 0; i < ; i++) {
for (var j = 0; j < blocksPos[i].length; j++) {
var block = new Block(blocksPos[i][j][0], blocksPos[i][j][1]);
(block);
}
}
var itemPos = (this); //Get the initial position, that is, the position in the preview map
= ;
= ;
(true, false); //Add to the preview map
}
= function() {//Curve shape transformation
var gameMap = ;
var allowChangeShape = true;
var blocksPoses = [];
var num = [];
var currentState1 = -1;
== num - 1 ? currentState1 = 0 : currentState1 = + 1;
var blocksPos = blocksPoses[currentState1];
var k = 0;
for (var i = 0; i < ; i++) { // It mainly detects whether the next deformation of the block set is reasonable
for (var j = 0; j < blocksPos[i].length; j++) {
var block = [k];
var itemX = + blocksPos[i][j][0];
var itemY = + blocksPos[i][j][1];
if ((itemX > - 1) || (itemX < 0) || (itemY > - 1) || itemY >= 0 && gameMap[itemY][itemX] != null && gameMap[itemY][itemX][] != null) {
allowChangeShape = false;
break;
}
k++;
}
}
if (allowChangeShape)//If deformation is allowed
{
== num - 1 ? = 0 : ++; //Set the next deformation state
k = 0;
for (var i = 0; i < ; i++) {
for (var j = 0; j < blocksPos[i].length; j++) {
var block = [k];
= blocksPos[i][j][0];
= blocksPos[i][j][1];
k++;
}
}
(false, true); //Add to the game map after deformation
}
}
= function(isMoving) { //The whereabouts of the block set
= ;
if (!()) { //If movement is not allowed
(); //The position of fixed block set in the game map
(); //Score processing
(); //Get the next block set
}
else { //The whereabouts are
++;
(false, isMoving);
}
}
= function() {//Get a random number between 0 and number
var num = this;
var i = this + 1;
while (i >= num) {
i = (() * 10);
}
return i;
}
= function() { return (compare); } //Array sorting, sorting in ascending order
function compare(a, b) { return a - b; } //Define the sorting rules
= function(dx) { //Clear the array element of the specified index
if (isNaN(dx) || dx > ) { return false; }
for (var i = 0, n = 0; i < ; i++) {
if (this[i] != this[dx])
this[n++] = this[i];
}
-= 1;
}
= function() { //Clear duplicate values ​​in the array
var arr = [];
for (var i = 0; i < ; i++) {
if (!(this[i]))
(this[i]);
}
return arr;
}
= function(item) { //Detection whether the array contains a certain element
for (var i = 0; i < ; i++) {
if (this[i] == item)
return true;
}
return false;
}
= function(time) { var timer = setTimeout(this, time); } //The function delays time execution in milliseconds
= InitGame;
function InitGame() {//Initialize the game
Sys = new sys();
= [];
(); //Initialize the game speed
(); //Create a game map
(); //Create a block set
}
function GameStart(element) {
if ( == "start") { //Start the game
= "pause";
();
= false;
}
else if ( == "pause") { //Pause the game
= "start"
();
}
else { //Start again after the game is over
();
}
}
function Moving() {//Move
().BlocksMoveDown(false);
}
function ChangeSpeed(e) {//Switch level
var speedlist = ("speed");
= [].value;
if (!) {
clearInterval();
();
}
}
function keyDown(e) { // key operation
if ( || !) return;
var blocks = ();
if ( == 37) { //To the left
= ;
if (())
--;
if ( != 0)
(false, true);
}
else if ( == 38) { //Up
= ;
();
}
else if ( == 39) { //To the right
= ;
var oldX = ;
if (())
++;
if ( != oldX)
(false, true);
}
else if ( == 40) //Down
{
= ;
(true);
}
}
</script>
<style type="text/css">
body
{
background-color:#ffffff;
overflow:hidden;
font-size:14px;
}
.gameZone
{
position:absolute;
left:0px;
top:0px;
width:100%;
height:550px;
background-Color:white;
}
.mask
{
position:absolute;
left:100px;
top:0px;
width:300px;
height:20px;
background-color:White;
border:solid 0px;
z-index:5;
}
.map
{
position:absolute;
left:100px;
top:20px;
width:280px;
height:504px;
background-image:url(images/tetris_grid.gif);
border:solid 3px green;
}
.gameOver
{
position:absolute;
left:100px;
top:20px;
width:280px;
height:504px;
font-weight:800;
font-size:xx-large;
color:Red;
text-align:center;
border:solid 3px;
line-height:420px;
display:none;
filter: Alpha(Opacity=80);
background-color:pink;
}
.map div
{
BACKGROUND-IMAGE: url(images/);
WIDTH: 28px;
BACKGROUND-REPEAT: no-repeat;
POSITION: absolute;
HEIGHT: 28px
}
.PreviewMap
{
position:absolute;
left:400px;
top:20px;
width:168px;
height:168px;
background-color:pink;
border:solid 2px green;
}
.PreviewMap div
{
BACKGROUND-IMAGE: url(images/);
WIDTH: 28px;
BACKGROUND-REPEAT: no-repeat;
POSITION: absolute;
HEIGHT: 28px
}
.start
{
position:absolute;
left:400px;
top:240px;
width:168px;
height:40px;
}
.scoreSpeed
{
position:absolute;
left:400px;
top:200px;
width:190px;
height:40px;
}
.score
{
color:pink;
font-weight:bold;
width:20px;
height:20px;
background-color:blue;
padding-left:10px;
padding-right:10px;
font-size:medium;
}
.speed
{
color:pink;
font-weight:bold;
width:20px;
height:20px;
background-color:blue;
padding-left:5px;
padding-right:5px;
font-size:medium;
}
.copyright
{
position:absolute;
left:400px;
top:280px;
word-break:break-all;
width:160px;
height:225px;
border:solid 2px green;
padding:5px;
}
</style>
</head>
<body onkeydown="keyDown(event)">
<div class="gameZone">
<div class="mask"></div>
<div class="map"></div>
<div class="gameOver"></div>
<div class="PreviewMap"></div>
<div class="scoreSpeed">Score: <span class="score">0</span></div>
<div class="start">
<input type="button" value="start" onclick="GameStart(this);" />&nbsp;
Level: <select onchange="ChangeSpeed();"></select>
</div>
<div class="copyright">
<b><center>Copyright</center></b><br />
This Tetris is developed by high mountain flowing water. Everyone is welcome to use it.
If you have any bugs or good comments, please leave me a message, thank you for your support!
If you need to reprint, please indicate the source! <br />
<font color=red><b>By the way, I will promote my MVC qq group: 45660795, welcome to join!</b></font>
<br /><br />
Author: <a href="/JackFeng/" target="_blank">High mountains and flowing water</a><br />
QQ:21243468
</div>
</div>
</body>
</html>