A JavaScript Tetris clone

A Tetris clone made in JavaScript called KiKiTrix made in 2007 just for some real-time fun. No external libraries were used.

Javascript game code:
/*
 *	Copyright (C) 2007 Dag Jonny Nedrelid
 *
 *	A Tetris Clone, made for real-time fun.
 */

var Speed = 600;
var boardRowSize = 14;
var boardColSize = 10;
var nextBrick = makeNewBrick();
var activeBrick;
var needNewBrick = true;
var gameRunning = true;
var PointSpdCounter = 0;
var Points = 0;
var Level = 1;
var Paused = false;

// The board. [x,y,fill_state (1 = active brick, 2 = landed brick)].
var Board = [[[0,0,0],[35,0,0],[70,0,0],[105,0,0],[140,0,0],[175,0,0],[210,0,0],[245,0,0],[280,0,0],[315,0,0]],
	[[0,35,0],[35,35,0],[70,35,0],[105,35,0],[140,35,0],[175,35,0],[210,35,0],[245,35,0],[280,35,0],[315,35,0]],
	[[0,70,0],[35,70,0],[70,70,0],[105,70,0],[140,70,0],[175,70,0],[210,70,0],[245,70,0],[280,70,0],[315,70,0]],
	[[0,105,0],[35,105,0],[70,105,0],[105,105,0],[140,105,0],[175,105,0],[210,105,0],[245,105,0],[280,105,0],[315,105,0]],
	[[0,140,0],[35,140,0],[70,140,0],[105,140,0],[140,140,0],[175,140,0],[210,140,0],[245,140,0],[280,140,0],[315,140,0]],
	[[0,175,0],[35,175,0],[70,175,0],[105,175,0],[140,175,0],[175,175,0],[210,175,0],[245,175,0],[280,175,0],[315,175,0]],
	[[0,210,0],[35,210,0],[70,210,0],[105,210,0],[140,210,0],[175,210,0],[210,210,0],[245,210,0],[280,210,0],[315,210,0]],
	[[0,245,0],[35,245,0],[70,245,0],[105,245,0],[140,245,0],[175,245,0],[210,245,0],[245,245,0],[280,245,0],[315,245,0]],
	[[0,280,0],[35,280,0],[70,280,0],[105,280,0],[140,280,0],[175,280,0],[210,280,0],[245,280,0],[280,280,0],[315,280,0]],
	[[0,315,0],[35,315,0],[70,315,0],[105,315,0],[140,315,0],[175,315,0],[210,315,0],[245,315,0],[280,315,0],[315,315,0]],
	[[0,350,0],[35,350,0],[70,350,0],[105,350,0],[140,350,0],[175,350,0],[210,350,0],[245,350,0],[280,350,0],[315,350,0]],
	[[0,385,0],[35,385,0],[70,385,0],[105,385,0],[140,385,0],[175,385,0],[210,385,0],[245,385,0],[280,385,0],[315,385,0]],
	[[0,420,0],[35,420,0],[70,420,0],[105,420,0],[140,420,0],[175,420,0],[210,420,0],[245,420,0],[280,420,0],[315,420,0]],
	[[0,455,0],[35,455,0],[70,455,0],[105,455,0],[140,455,0],[175,455,0],[210,455,0],[245,455,0],[280,455,0],[315,455,0]]];

var NextBrickBoard = [[[0,0,0],[15,0,0],[30,0,0],[45,0,0]],
		[[0,15,0],[15,15,0],[30,15,0],[45,15,0]]];	

function KTrix() {
	if (gameRunning == true && Paused == false) {
		document.getElementById('trixcmd').focus();

		// Create a new brick if needed.
		if (needNewBrick) {
			activeBrick = nextBrick;
			nextBrick = makeNewBrick();
			needNewBrick = false;
			
			// Update point panel.
			updatePointpanel();
		}
	
		// Update board.
		updateBoard();
	
		// Set up brick for next round's fall.
		brickMustFall();
		
		// Check for game over.
		isGameOver();
		
		// Check if we have completed the game.
		winCheck();

		// Loop.
		setTimeout('KTrix()',Speed);
		
	} else if (Paused == true && gameRunning == true) {
		// Do nothing, except wait for un-pause.
		setTimeout('KTrix()',Speed);
	}
}

function doMove(e) {
	if (!gameRunning)
		return;
	
	var keynum, loop_a, loop_b;
	
	if (window.event)
		keynum = e.keyCode; 
	else if (e.which)
		keynum = e.which; 
	
	// 37 = left, 39 = right, 38 = up, 40 = down, 80 = P for pause.
	if ((Paused == false && keynum == 37 && 
		activeBrick[0][1] > 0 && activeBrick[1][1] > 0 && 
		activeBrick[2][1] > 0 && activeBrick[3][1] > 0) && (
		Board[activeBrick[0][0]][activeBrick[0][1]-1][2] != 2 && 
		Board[activeBrick[1][0]][activeBrick[1][1]-1][2] != 2 && 
		Board[activeBrick[2][0]][activeBrick[2][1]-1][2] != 2 && 
		Board[activeBrick[3][0]][activeBrick[3][1]-1][2] != 2)) {
			 	 	
		// No collision, move brick left.
		for (loop_a = 0; loop_a <= 15; loop_a++) {
			activeBrick[loop_a][1] -= 1;
		} 
		updateBoard();
		
	} else if ((Paused == false && keynum == 39 && 
		activeBrick[0][1] < boardColSize-1 && 
		activeBrick[1][1] < boardColSize-1 && 
		activeBrick[2][1] < boardColSize-1 && 
		activeBrick[3][1] < boardColSize-1) && (
		Board[activeBrick[0][0]][activeBrick[0][1]+1][2] != 2 && 
		Board[activeBrick[1][0]][activeBrick[1][1]+1][2] != 2 && 
		Board[activeBrick[2][0]][activeBrick[2][1]+1][2] != 2 && 
		Board[activeBrick[3][0]][activeBrick[3][1]+1][2] != 2)) {
					
		// No collision, move brick right.
		for (loop_a = 0; loop_a <= 15; loop_a++) {
			activeBrick[loop_a][1] += 1;
		}
		updateBoard();
					
	} else if ((Paused == false && keynum == 40 && 
		activeBrick[0][0] < boardRowSize-1 && 
		activeBrick[1][0] < boardRowSize-1 && 
		activeBrick[2][0] < boardRowSize-1 && 
		activeBrick[3][0] < boardRowSize-1) && (
		Board[activeBrick[0][0]+1][activeBrick[0][1]][2] != 2 && 
		Board[activeBrick[1][0]+1][activeBrick[1][1]][2] != 2 && 
		Board[activeBrick[2][0]+1][activeBrick[2][1]][2] != 2 && 
		Board[activeBrick[3][0]+1][activeBrick[3][1]][2] != 2)) {
					
		// No collision, move brick down.
		for (loop_a = 0; loop_a <= 15; loop_a++) {
			activeBrick[loop_a][0] += 1;
		}
		updateBoard();
					
	} else if ((Paused == false && keynum == 38 && 
		activeBrick[4][0] < boardRowSize-1 && 
		activeBrick[5][0] < boardRowSize-1 && 
		activeBrick[6][0] < boardRowSize-1 && 
		activeBrick[7][0] < boardRowSize-1) && (
		activeBrick[4][1] >= 0 && activeBrick[5][1] >= 0 && 
		activeBrick[6][1] >= 0 && activeBrick[7][1] >= 0) && (
		activeBrick[4][1] < boardColSize && 
		activeBrick[5][1] < boardColSize && 
		activeBrick[6][1] < boardColSize && 
		activeBrick[7][1] < boardColSize) && (
		Board[activeBrick[4][0]][activeBrick[4][1]][2] != 2 && 
		Board[activeBrick[5][0]][activeBrick[5][1]][2] != 2 && 
		Board[activeBrick[6][0]][activeBrick[6][1]][2] != 2 && 
		Board[activeBrick[7][0]][activeBrick[7][1]][2] != 2)) {
		
		// After checking if the next shape in line won't 
		// crash in any walls, roof, ground or other landed 
		// bricks, we can transform the brick.
					
		// Make copy of active brick.
		var tmpBrick = [[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
				[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]];
		
		for (loop_a = 0; loop_a <= 15; loop_a++) {
			for (loop_b = 0; loop_b <= 1; loop_b++) {
				tmpBrick[loop_a][loop_b] = activeBrick[loop_a][loop_b];
			}
		}
					
		// Move first brick shape out of play down  
		// to fourth position in transform line.
		activeBrick[12][0] = tmpBrick[0][0];
		activeBrick[13][0] = tmpBrick[1][0];
		activeBrick[14][0] = tmpBrick[2][0];
		activeBrick[15][0] = tmpBrick[3][0];
		activeBrick[12][1] = tmpBrick[0][1];
		activeBrick[13][1] = tmpBrick[1][1];
		activeBrick[14][1] = tmpBrick[2][1];
		activeBrick[15][1] = tmpBrick[3][1];
					
		// Move second shape in line into play.
		activeBrick[0][0] = tmpBrick[4][0];
		activeBrick[1][0] = tmpBrick[5][0];
		activeBrick[2][0] = tmpBrick[6][0];
		activeBrick[3][0] = tmpBrick[7][0];
		activeBrick[0][1] = tmpBrick[4][1];
		activeBrick[1][1] = tmpBrick[5][1];
		activeBrick[2][1] = tmpBrick[6][1];
		activeBrick[3][1] = tmpBrick[7][1];
					
		// Move that which was third, 
		// into second place for next transform.
		activeBrick[4][0] = tmpBrick[8][0];
		activeBrick[5][0] = tmpBrick[9][0];
		activeBrick[6][0] = tmpBrick[10][0];
		activeBrick[7][0] = tmpBrick[11][0];
					
		activeBrick[4][1] = tmpBrick[8][1];
		activeBrick[5][1] = tmpBrick[9][1];
		activeBrick[6][1] = tmpBrick[10][1];
		activeBrick[7][1] = tmpBrick[11][1];
					
		// Move that which was fourth, 
		// into third place for next transform.
		activeBrick[8][0] = tmpBrick[12][0];
		activeBrick[9][0] = tmpBrick[13][0];
		activeBrick[10][0] = tmpBrick[14][0];
		activeBrick[11][0] = tmpBrick[15][0];
					
		activeBrick[8][1] = tmpBrick[12][1];
		activeBrick[9][1] = tmpBrick[13][1];
		activeBrick[10][1] = tmpBrick[14][1];
		activeBrick[11][1] = tmpBrick[15][1];
		
		updateBoard();
					
	} else if (keynum == 80) {
		
		if (Paused) {
			Paused = false;
			updatePointpanel();
		} else {
			Paused = true;
			document.getElementById('Pointpanel').innerHTML = ''
			+ '<br /><br /><br /><br />'
			+ 'GAME PAUSED!<br /><br />'
			+ 'Press P to continue.';
		}
	}
}

function makeNewBrick() {
	/*
		Every brick will contain 4 shapes for rotation/transformation
		(Sub-array 0-3 = shape1, 4-7 = shape2, 8-11 = shape3, 12-15 = shape4).
		3rd and 4th shape is repeated with the first two for proper rotation 
		if a 3rd and 4th brick shape should not exist naturally for the brick.
	*/
	
	// ####
	var Brick1 = [[0,3],[0,4],[0,5],[0,6],
			[0,5],[1,5],[2,5],[3,5],
			[0,3],[0,4],[0,5],[0,6],
			[0,5],[1,5],[2,5],[3,5]];
	
	// ##
	// ##
	var Brick2 = [[0,4],[0,5],[1,4],[1,5],
			[0,4],[0,5],[1,4],[1,5],
			[0,4],[0,5],[1,4],[1,5],
			[0,4],[0,5],[1,4],[1,5]];
	
	// ##
	//  ##
	var Brick3 = [[0,4],[0,5],[1,5],[1,6],
			[0,6],[1,6],[1,5],[2,5],
			[0,4],[0,5],[1,5],[1,6],
			[0,6],[1,6],[1,5],[2,5]];
	
	//  ##
	// ##
	var Brick4 = [[0,5],[0,6],[1,4],[1,5],
			[0,4],[1,4],[1,5],[2,5],
			[0,5],[0,6],[1,4],[1,5],
			[0,4],[1,4],[1,5],[2,5]];
	
	// #
	// ###
	var Brick5 = [[0,4],[1,4],[1,5],[1,6],
			[0,4],[0,5],[1,4],[2,4],
			[0,4],[0,5],[0,6],[1,6],
			[0,6],[1,6],[2,6],[2,5]];
	
	//   #
	// ###
	var Brick6 = [[0,6],[1,4],[1,5],[1,6],
			[0,4],[1,4],[2,4],[2,5],
			[0,4],[0,5],[0,6],[1,4],
			[0,5],[0,6],[1,6],[2,6]];
	
	//  #
	// ###
	var Brick7 = [[0,5],[1,4],[1,5],[1,6],
			[0,5],[1,5],[1,6],[2,5],
			[0,4],[0,5],[0,6],[1,5],
			[0,5],[1,4],[1,5],[2,5]];
	
	// Collect bricks.
	var Bricklist = [Brick1,Brick2,Brick3,Brick4,Brick5,Brick6,Brick7];
	
	// Choose a random brick.
	return Bricklist[Math.round(Math.random()*6)];
}

function getNextShape() {
	var loop_a, loop_b, loop_c;
	
	// Make copy of next brick.
	var tmpBrick = [[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
			[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]];
	
	for (loop_a = 0; loop_a <= 15; loop_a++) {
		for (loop_b = 0; loop_b <= 1; loop_b++) {
			tmpBrick[loop_a][loop_b] = nextBrick[loop_a][loop_b];
		}
	}
	
	// Adapt shape for brick view (move left for smaller board).
	for (loop_a = 0; loop_a <= 3; loop_a++) {
		tmpBrick[loop_a][1] -= 3;
	}
	
	// Reset and update NextBrickBoard.
	for (loop_a = 0; loop_a <= 1; loop_a++) {
		for (loop_b = 0; loop_b <= 3; loop_b++) {
			NextBrickBoard[loop_a][loop_b][2] = 0;
		}
	}
	
	// Put next brick (first shape) on NextBrickBoard.
	for (loop_a = 0; loop_a <= 1; loop_a++) {
		for (loop_b = 0; loop_b <= 3; loop_b++) {
			for (loop_c = 0; loop_c <= 3; loop_c++) {
				if (tmpBrick[loop_c][0] == loop_a && 
					tmpBrick[loop_c][1] == loop_b) {
					
					NextBrickBoard[loop_a][loop_b][2] = 1;	
				}
			}
		}
	}
	
	// Update NextBrickView.
	var whatBrick = 1;
	for (loop_a = 0; loop_a <= 1; loop_a++) {
		for (loop_b = 0; loop_b <= 3; loop_b++) {
			if (NextBrickBoard[loop_a][loop_b][2] == 1) {
				document.getElementById('MiniBrick'+whatBrick).style.left = NextBrickBoard[loop_a][loop_b][0] +'px';
				document.getElementById('MiniBrick'+whatBrick).style.top = NextBrickBoard[loop_a][loop_b][1] +'px';
				document.getElementById('MiniBrick'+whatBrick).style.visibility = 'visible';
				whatBrick += 1;
			}
		}
	}
}

function brickMustFall() {
	var loop_a;
		
	if ((activeBrick[0][0] < boardRowSize-1 &&
		activeBrick[1][0] < boardRowSize-1 && 
		activeBrick[2][0] < boardRowSize-1 && 
		activeBrick[3][0] < boardRowSize-1) && (
		Board[activeBrick[0][0]+1][activeBrick[0][1]][2] != 2 && 
		Board[activeBrick[1][0]+1][activeBrick[1][1]][2] != 2 && 
		Board[activeBrick[2][0]+1][activeBrick[2][1]][2] != 2 && 
		Board[activeBrick[3][0]+1][activeBrick[3][1]][2] != 2)) {
			
		// If the brick isn't gonna hit another landed brick or 
		// the bottom, we can continue with another fall.
		
		for (loop_a = 0; loop_a <= 15; loop_a++) {
			activeBrick[loop_a][0] += 1;
		}
		
	} else { 
		// If the brick is going to hit other bricks or the 
		// bottom of the board, we register it as landed in 
		// the board array and draw it.
		
		for (loop_a = 0; loop_a <= 3; loop_a++) {
			Board[activeBrick[loop_a][0]][activeBrick[loop_a][1]][2] = 2;
		}
		
		for (loop_a = 0; loop_a <= 3; loop_a++) {
			document.getElementById('container').innerHTML += ''
			+ '<div style="width:35px; height:35px; position:absolute; left:'
			+ Board[activeBrick[loop_a][0]][activeBrick[loop_a][1]][0] +'px; top:'
			+ Board[activeBrick[loop_a][0]][activeBrick[loop_a][1]][1] 
			+ 'px; background-image:url(firkant.gif)"> </div>';
		}
		
		// Turn on the switch for a new brick spawn.
		needNewBrick = true; 
	}
}

function updateBoard() {
	var loop_a, loop_b, loop_c;
	
	// Remove all 1 values (bricks in action) 
	// and update with new brick values.
	
	for (loop_a = 0; loop_a < boardRowSize; loop_a++) {
		for (loop_b = 0; loop_b < boardColSize; loop_b++) {
			
			// Remove existing value.
			if (Board[loop_a][loop_b][2] == 1)
				Board[loop_a][loop_b][2] = 0;
			
			// Track brick and update its new position on board.
			for (loop_c = 0; loop_c <= 3; loop_c++) {
				if (loop_a == activeBrick[loop_c][0] && 
					loop_b == activeBrick[loop_c][1]) { 
					
					Board[loop_a][loop_b][2] = 1; 
				}
			}
		}
	}
	
	// Draw board with new active brick values.
	var whatBrick = 1;
	for (loop_a = 0; loop_a < boardRowSize; loop_a++) {
		for (loop_b = 0; loop_b < boardColSize; loop_b++) {
			if (Board[loop_a][loop_b][2] == 1) {
				document.getElementById('Brick'+whatBrick).style.left = Board[loop_a][loop_b][0] +'px';
				document.getElementById('Brick'+whatBrick).style.top = Board[loop_a][loop_b][1] +'px';
				document.getElementById('Brick'+whatBrick).style.visibility = 'visible';
				whatBrick += 1;
			}
		}
	}
	
	// Check for completed rows with landed brick's. 
	// Delete them if found and give player points.
	var colCounter;
	for (loop_a = 0; loop_a < boardRowSize; loop_a++) {
		
		colCounter = 0;
		for (loop_b = 0; loop_b < boardColSize; loop_b++) {
			if (Board[loop_a][loop_b][2] == 2)
				colCounter += 1;
		}
		
		if (colCounter == boardColSize) {
			
			// Delete row if it was full of bricks
			for (loop_b = 0; loop_b < boardColSize; loop_b++) {
				Board[loop_a][loop_b][2] = 0;
			}
			
			// Move all status-2 brick that was 
			// above the removed row 1 step down.
			gravitateBoard(loop_a);
			
			// Give points for 1 row removed.
			// Every row is worth 100 points.
			Points += 100;
			PointSpdCounter += 1;
			
			// For every 1000 points the 
			// speed goes up 50 nanoseconds.
			if (PointSpdCounter == 10) {
				Speed -= (Speed != 0 ? 50 : 0);
				PointSpdCounter = 0;
				Level += 1;
			}
			
			// Update point panel.
			updatePointpanel();
		}
	}
}

function gravitateBoard(rowRemoved) {
	var loop_a, loop_b;
	
	// Move status-2 (landed) bricks above 
	// removed row 1 step down and and redraw.
	
	for (loop_a = rowRemoved-1; loop_a > 0; loop_a--) {
		for (loop_b = 0; loop_b < boardColSize; loop_b++) {
			if (Board[loop_a][loop_b][2] == 2) {
				Board[loop_a][loop_b][2] = 0;
				Board[loop_a+1][loop_b][2] = 2;
			}
		}
	}
	
	document.getElementById('container').innerHTML = '';
	for (loop_a = 0; loop_a < boardRowSize; loop_a++) {
		for (loop_b = 0; loop_b < boardColSize; loop_b++) {
			if (Board[loop_a][loop_b][2] == 2) {
				document.getElementById('container').innerHTML += ''
				+ '<div style="width:35px; height:35px; position:absolute; left:'
				+ Board[loop_a][loop_b][0] +'px; top:'
				+ Board[loop_a][loop_b][1] 
				+ 'px; background-image:url(firkant.gif)"> </div>';
			}
		}
	}
}

function updatePointpanel() {
	document.getElementById('Pointpanel').innerHTML = ''
	+ '<div align="center" id="PointpanelLive">'
	+ '<span class="header">Point panel</span>'
	+ '</div><br />'
	+ 'Points: ' + Points + '<br /><br />'
	+ 'Level: ' + Level + '<br />'
	+ (Level == 1 ? 'Trix Noob<br /><br />' : '')
	+ (Level == 2 ? 'Trix Trainee<br /><br />' : '')
	+ (Level == 3 ? 'Trix Wannabe<br /><br />' : '')
	+ (Level == 4 ? 'Trix Amateur<br /><br />' : '')
	+ (Level == 5 ? 'Trix Average<br /><br />' : '')
	+ (Level == 6 ? 'Trix Trained<br /><br />' : '')
	+ (Level == 7 ? 'Trix Pro<br /><br />':'')
	+ (Level == 8 ? 'Trix Elite<br /><br />':'')
	+ (Level == 9 ? 'Trix Prodigy<br /><br />':'')
	+ (Level == 10 ? 'Trix Master<br /><br />':'')
	+ 'Next brick: <br />'
	+ '<div id="NextBrickView">'
	+ '<div id="MiniBrick1"></div>'
	+ '<div id="MiniBrick2"></div>'
	+ '<div id="MiniBrick3"></div>'
	+ '<div id="MiniBrick4"></div>'
	+ '</div><br />'
	+ 'Brick delay: ' + Speed + '<br />'
	+ '(Nano seconds)<br /><br />'
	+ '<div align="center">Need pause?<br />'
	+ 'Press the P Key.</div><br />';
	getNextShape();
}

function isGameOver() {
	var loop_a;
	
	// Check if there are status-2 bricks on 
	// the top row spawn area on our board
	
	for (loop_a = 3; loop_a < 8; loop_a++) {
		if (Board[0][loop_a][2] == 2) { 
			
			// Game is over
			document.getElementById('Pointpanel').innerHTML = ''
				+ '<br /><br /><br />Points: '
				+ Points + '<br /><br />'
				+ 'GAME OVER' + '<br /><br />'
				+ 'Reload to play again(F5)' + '<br />';
			
			// Stop the game
			gameRunning = false; 
			return;	
		}
	}
}

function winCheck() {
	
	// Check if we have reached level 11.
	
	if (Level == 11) {
		document.getElementById('Pointpanel').innerHTML = ''
			+ '<br /><br />Points: '
			+ Points + '<br /><br />'
			+ 'GAME COMPLETED!<br />'
			+ 'You defeated level 10!<br /><br />'
			+ 'Reload to play again(F5)<br />';
		gameRunning = false;
	}
}

function About() {
	var msg  = "A tetris clone by Dag Jonny Nedrelid.\n"
		+ "Created 26-27 December 2007.\n"
	alert(msg);
}

This document was last updated November 16th, 2011.
Written by: Dag Jonny Nedrelid
©2007-2012 http://thronic.com


Feel free to leave a comment.
Name:
URL:
0