Make Your Own Game
Make Your Own Game
By Shahzad Arain
http://www.pakdata.net
[email protected]
92-334-5307738
92-334-9564004
This document and the accompanying HTML files show how to construct a game similar
to the popular falling down blocks game called tetris invented by Shahzad Arain Pakistan.
The game has 4-block pieces, 7 distinct shapes that fall from the top. The challenge is to
manipulate them by horizontal and rotation moves so that lines get completely filled up.
You receive points for completing lines. Filled lines are removed. Completing more than
one line (up to 4) at a time results in more points. My so-called 'tinytetris' begins like
this:
DRAFT 1
There are several improvements to make to this game to make it more resemble the 'real'
game: modify the start game action so a new game can be started; change the scoring to
involve speeding up and other features; and implement the actual down button (in the real
game, you gain points by moving a piece all the way down in one move). The grace
period implementation also may need improvement. A pause button would be nice.
I came across an on-line tetris-like game done in JavaScript by Hiro Nakamura and was
inspired to create my own game, with accompanying notes. Though I did not take many
features from Nakamura's game, it served as inspiration to make the attempt to get
something working in JavaScript. Most 'industrial-strength' games are written in
compiled languages such as c++ or Java or application development tools such as Flash.
This JavaScript implementation probably is not very robust. A fast player could click the
buttons too fast for the underlying program to perform—finish the service cycle before it
must start again.
The main lessons to be learned from this work come from studying the process of
building an application revealed in these notes and the documents. Two critical features
of the process are
• I proceeded incrementally, as shown by examination of the files: tinytetris,
tinytetris1 through tinytetris10. I put off inserting timed action until the very last
stage.
DRAFT 2
• I implemented essentially a development environment to test the program without
having to play the game. For example, I made hyperlink style buttons, easily
changeable, to create specific pieces and to move the current piece one unit down
the board. These links were removed for the final version of the game.
It is possible to skip to the exposition of the final program, tinytetris10, which does
contain line by line explanations. However, the 'build-up' has value, especially for new
programmers.
I used the Mozilla browser for its JavaScript console. This facilitated finding errors such
as mis-matched brackets. I used Paint Shop Pro to create image files of 8 blocks. One,
bblock.gif, is used for the blank or open space. The seven others are different colors and
have borders.
Design decisions
For my 'tiny' tetris program, three design decisions deserve special mention.
I chose an essentially static approach. No element moves. Instead, the board contains a
sequence of img tags laid out in a grid. The contents of these img tags (the src values)
change. This makes it easy to determine when a row (line) of the board is filled.
Looking back at my other examples, think of coin toss and image swap and not bouncing
ball or cannonball. Doing it this way means I do not have to worry about browser
differences.
The falling shapes are sets of blocks, four blocks each, put together using what I term
formulas. I did this because the shapes must be considered as separate blocks once they
hit down.
The left, right, rotate and down buttons are implemented as form submit buttons. This
presents an obvious set of options for the player and I do not need to be concerned with
key codes, which can vary with different keyboards.
Development stages
These development stages are not / were not perfect. There were cases in which I needed
to go back and change tactics. However, the whole project was done quickly: 2 days
(and I still went to aerobics, attended a party, had company at the house, and did some
grass-roots political action).
tinytetris create board
tinytetris1 create pieces (2 different ones) using formula in a table
tinytetris2 create pieces of all 7 types. A button can be easily changed to make a
different shape.
tinytetris3 start implementation of left and right moves and rotate
tinytetris4 checks for room for new piece—this is, check if game over
tinytetris5 move current piece down a row (invoked by button). Check if hit bottom
tinytetris6 Add places to put lines and scores (not yet used). Counts lines but only if
player attempts to move down after hitting down.
DRAFT 3
tinytetris7 Add check that piece has hit down and can't go further using
checkifhitdown function. Made change to completefalling function to ease
next step. Added new testing option to get different block types.
tinytetris8 Remove filled lines (cascade down)
tinytetris9 Automatic new, random block when player clicks start game and when
block touches down. Also, added call to checkifhitdown in rotate and
moveover.
tinytetris10 Use setInterval to fall automatically. Set up grace period after touchdown
to move current piece, thus allowing horizontal move after piece touches
down. This involved changing how completefalling is called.
tinytetris
The general outline for the program (loosely following Nakamura) is to create the board
by creating a two-dimensional grid of img tags (I am avoiding using the term table).
These image tags are all in one <td> element. The screen is:
It certainly is not obvious, but this board contains 9 times 15 img tags. The following
made up screen shot shows, somewhat crudely, how the img tags are laid out. The img
tags initially hold a borderless white block held in the file bblock.gif. The images are all
DRAFT 4
the same size. The game pieces consist of arrangements of blocks of 7 different colors.
These blocks have borders.
The code is
<html>
<head>
<title>Simple Tetris </title>
<script language="JavaScript">
/* createboard of images */
function createboard() {
var i;
var j;
for (i=0; i<vheight; i++) {
for (j=0; j<hwidth; j++) {
document.write("<img src='bblock.gif'/>");
}
document.write("<br/>");
}
}
</script>
</head>
<body>
<table border="1"> // border of the board
<td>
<script language="JavaScript">
createboard();
</script>
</td>
</table>
DRAFT 5
<img src="bblock.gif"/>
<a href="javascript:startgame();">Start Game </a>
</body>
</html>
The createboard function is called from within a script element in the <body> element. It
uses what will become a very familiar construction of nested for loops. The variables
vheight and hwidth hold the number of columns and rows, respectively. After each
complete iteration of the inner for loop, a <br/> tag is output to go to the next row. At this
point, you may ask how the img tags can be accessed. The answer is by using the so-
called images collection of the document object. The expression
document.images[imgno].src
can be used to access or set the img tag indicated by the number imgno. A function
called imagenumber, to be described below, will convert from column and row to image
number.
The <a> hyperlink that calls startgame is not yet functional.
**********************************************
tinytetris1
A critical feature of this game is the 4-block sets. Each shape is prescribed by a formula.
(When we get to rotation, we will use an array of arrays of arrays to designate the
formula for each orientation of each of the 7 types.) A formula specifies the x and y
offset from an origin for each of the 4 blocks. In the code below, formulas are given for
the T shape and the straight line shape. The new function is makeblock. It is invoked at
this stage by code in an <a> link. Note also the imagenumber function for generating a
number to use to indicate which image in the images collection corresponds to a given
column and row pair. [See the file for the complete code. This just shows the new
material.]
<html>
<head>
…
var blockformulas = [
[[0,0],[1,0],[2,0],[1,1]], // T shape
[[0,0],[1,0],[2,0],[3,0]] // straight line
];
var blockimages = [
"darkblue.gif",
"lightblue.gif"
];
// generates the image tag number from col and row
function imagenumber(atcol, atrow) {
var imagenum = atrow*hwidth + atcol;
return imagenum;
}
//make a block of type type at column atcol and at row atrow
//used to start off blocks
DRAFT 6
function makeblock(type, atcol, atrow) {
var i;
var block = blockimages[type];
var formula = blockformulas[type];
var imagenum;
for (i=0;i<=3;i++) {
imagenum=imagenumber(atcol+formula[i][0], atrow+formula[i][1]);
document.images[imagenum].src = block;
}
alert("end of makeblock");
}
</script>
</head>
<body>
tinytetris2
Once the last program worked, I had the confidence to create the rest of the formulas. The
javascript in the <a> tag was changed to check each of the 7 formulas.
var blockformulas = [
[[0,0],[1,0],[2,0],[1,1]],
[[0,0],[1,0],[2,0],[3,0]],
[[0,1],[1,1],[1,0],[2,0]],
[[0,0],[1,0],[0,1],[1,1]],
[[0,0],[1,0],[1,1],[2,1]],
[[0,0],[1,0],[2,0],[2,1]],
[[0,1],[1,1],[2,1],[2,0]]
];
var blockimages = [
"darkblue.gif",
"lightblue.gif",
"green.gif",
"yellow.gif",
"red.gif",
"purple.gif",
"gray.gif"
];
DRAFT 7
*********************************
tinytetris3
The challenge in this stage is to add the horizontal moves and the rotate move. The first
step is to provide buttons for the player. This is done by using a table for layout and
putting a <form> containing <input type="button"> tags all in the <body>. These buttons
invoke moveover with an argument indicating the direction and rotate(). The rotate does
not take any argument. Instead, its invocation goes through a fixed sequence of what I
term orientations. The rotation operation required a very large array, orientations of the
same type of formulas as described before. The first element (0th element) of orientations
is the same as blockformulas. I decided not to combine these two. It took some testing to
get these correct.
Doing the horizontal move or even the rotation turned out to be easy. The problem is that
it is necessary to check if a move is possible, that is, not blocked by the edges of the
board or other pieces. The check for the edges makes use of the modulus operator (%).
The code makes use of the break; statement to get out of a for loop. This code uses what
I call 'oksofar coding'. A variable is initialized to true and then set to false if and when
something is detected.
The problem in checking for conflicts with other shapes is that it is necessary to
distinguish between spaces occupied by the current piece and spaces occupied by other
shapes. Spaces occupied by blocks in the current piece may be vacated to make room for
others. This required a multi-step procedure (with several nested for loops) in which it is
necessary to restore blocks if a conflict is detected.
The code to detect if some shape occupies a given position is done by doing a string
search (search method of string object) on the document.images[ ].src (AFTER using
String to convert this to a String from some other internal form). This is necessary
because this string is very long, containing the whole file locator, not just 'bblock.gif' or
'darkblue.gif', etc. My method is to search for 'bblock.gif' and if it is not found, this
indicates that this <img> contains an actual shape.
…
//block formulas for 4 orientations
//orientations[orient][type][block 0 to 3][x and y]
var orientations = [
[
[[0,0],[1,0],[2,0],[1,1]], //
[[0,0],[1,0],[2,0],[3,0]], //
[[0,1],[1,1],[1,0],[2,0]], //
[[0,0],[1,0],[0,1],[1,1]], //
[[0,0],[1,0],[1,1],[2,1]], //
[[0,0],[1,0],[2,0],[2,1]], //
[[0,1],[1,1],[2,1],[2,0]] //
],
[
[[1,0],[1,1],[1,2],[2,1]], //
[[1,0],[1,1],[1,2],[1,3]], //
[[1,2],[1,1],[0,1],[0,0]], //
[[0,0],[1,0],[0,1],[1,1]], //
DRAFT 8
[[1,0],[1,1],[0,1],[0,2]], //
[[1,2],[1,1],[1,0],[2,0]], //
[[2,2],[2,1],[2,0],[1,0]] //
],
[
[[0,1],[1,1],[2,1],[1,0]], //
[[0,0],[1,0],[2,0],[3,0]], //
[[2,0],[1,0],[1,1],[0,1]], //
[[0,0],[1,0],[0,1],[1,1]], //
[[0,0],[1,0],[1,1],[2,1]], //
[[2,1],[1,1],[0,1],[0,0]], //
[[2,0],[1,0],[0,0],[0,1]] //
],
[
[[1,0],[1,1],[1,2],[0,1]], //
[[1,0],[1,1],[1,2],[1,3]], //
[[1,2],[1,1],[0,1],[0,0]], //
[[0,0],[1,0],[0,1],[1,1]], //
[[1,0],[1,1],[0,1],[0,2]], //
[[1,0],[1,1],[1,2],[0,2]], //
[[1,0],[1,1],[1,2],[2,2]] //
]
];
DRAFT 9
// will add check for room to add block. If none, end game.
document.images[imagenum].src = block;
current[i][0]=imagenum;
current[i][1] = atc;
current[i][2] = atr;
}
}
// move left (-1) or right (1)
function moveover(dir) {
var i;
var tests;
var oksofar = true;
var imgno;
var newcurrent = new Array();
var saved = new Array();
for (i=0; i<=3; i++) {
imgno = current[i][0];
break; } }
if (dir == 1) { // moving right
if ((hwidth-1)== imgno % hwidth) { //at right edge
oksofar = false;
break; }
}
newcurrent[i] = imgno+dir;
}
// if oksofar (no blocks at critical edge, newcurrent is set
if (oksofar) {
for (i=0; i<=3; i++) {
saved[i] = current[i][0];
document.images[current[i][0]].src = "bblock.gif";
}
tests = String(document.images[newcurrent[i]].src);
found = tests.search("bblock.gif");
if (found == -1) { // meaning it was not found
oksofar = false;
break;
}
}
if (oksofar) {
for (i=0;i<=3;i++) {
document.images[newcurrent[i]].src= currenttype;
current[i][0] = newcurrent[i];
current[i][1] = current[i][1]+dir;
DRAFT 10
}
currentorigin[0]=currentorigin[0]+dir;
}
else {
for (i=0;i<=3;i++) {
document.images[saved[i]].src = currenttype; //restore
}
}
}
}
DRAFT 11
imagenum=newcurrent[i];
document.images[imagenum].src = block;
current[i][0]=imagenum;
current[i][1] = atcol+formula[i][0];
current[i][2] = atrow+formula[i][1];
}
}
else { //need to restore from saved
for (i=0;i<=3;i++) {
document.images[saved[i]].src = block;
}
currentorientation = savedorientation;
}
} //close first if oksofar
else {
currentorientation = savedorientation;
}
} // close function
</script>
</head>
<body>
<table>
<td>
<table border="1">
<td>
<script language="JavaScript">
createboard();
</script>
</td>
</table>
</td>
<td>
<form>
<input type="button" onClick="moveover(-1);" value="left">
<input type="button" onClick="rotate();" value="rotate">
<input type="button" onClick="moveover(1);" value="right">
<br/>
<input type="button" onClick="movedown();" value="down">
</form>
</td>
</table>
<img src="bblock.gif"/>
<a href="javascript:startgame();">Start Game </a> <br/>
<a href="javascript:makeblock(5,5,1);">Make block 5 5 0 </a> <br/>
<a href="javascript:rotate();">Rotate </a>
</body>
</html>
***********************************
tinytetris4
DRAFT 12
var found;
currentorigin = [atcol, atrow];
currenttypenum = type;
currenttype = blockimages[type];
currentorientation = 0;
var i;
var block = blockimages[type];
var formula = blockformulas[type];
var imagenum;
var atc;
var atr;
for (i=0;i<=3;i++) {
atc = atcol + formula[i][0];
atr = atrow + formula[i][1];
imagenum=imagenumber(atc, atr);
//check for room to add block. If none, end game.
tests = String(document.images[imagenum].src);
found = tests.search("bblock.gif");
if (found>=0) {
document.images[imagenum].src = block;
current[i][0]=imagenum;
current[i][1] = atc;
current[i][2] = atr;
}
else {
alert("No room for new block. Game over.");
break;
}
}
**********************
tinytetris5
To test the game, but without playing the game, I make the down button actually move
the piece down one row. In what I term the real game, the down button is used to send
the piece as far down as it can go all in one step. This stage will check the internal action
to move the shape down until it hits something. This code works like the horizontal and
rotation operations. It is necessary to use a multi-step procedure with provision to restore
pieces if the move was blocked.
//move down one unit
function movedown() {
var i;
var tests;
var oksofar = true;
var imgno;
var atc;
var atr;
var newcurrent = new Array();
var saved = new Array();
var found;
for (i=0; i<=3; i++) {
imgno = current[i][0];
DRAFT 13
atc = current[i][1];
atr = current[i][2];
************************
tinytetris6
In this stage, I added places to put lines and scores, but they are not yet used. This code
does do an examination of the lines, but it counts the blanks and not the filled ones. This
error is corrected in a later stage. The alert command is used to say what is found. Using
alert is a good way to make progress without having to do everything at once. However,
this function, completefalling, is invoked only if player attempts to move down after
actually hitting down. This is changed in the next stages.
DRAFT 14
//move down one unit
function movedown() {
…
if (atr>=(vheight-1)) { //at very bottom already
//need to signal start of new block
hitdown = true;
oksofar = false;
break;
}
…
if (oksofar) {
for (i=0;i<=3; i++) { //saved image nums & blank out current piece
saved[i] = current[i][0];
document.images[current[i][0]].src = "bblock.gif";
} // ends for loop
for (i=0; i<=3; i++) { //check if any blocking
tests = String(document.images[newcurrent[i]].src);
found = tests.search("bblock.gif");
if (found == -1) { // meaning it was not found
oksofar = false;
break;
} //ends if test
} //ends for loop
if (oksofar) {
for (i=0;i<=3; i++) {
document.images[newcurrent[i]].src = currenttype;
current[i][0] = newcurrent[i];
current[i][2]++; // y increases; x stays the same
DRAFT 15
found = tests.search("bblock.gif");
if (found>-1) { // a blank
blankcount++ ;
} //
}
alert("line i "+i+" has "+blankcount+" blanks");
}
**********************
tinytetris7
This stage corrects the last by adding the check to see if a piece has hit down and can't go
further using checkifhitdown function. The blanks are still being counted, and not the
non-blank spaces. I also made change to completefalling function to ease next step. I
added new hyperlinks buttons to allow starting different block types.
DRAFT 16
oksofar = false;
hitdown = true;
break;
} //ends if test
} //ends for loop
//restore blocks in all cases
for (i=0;i<=3; i++) {
document.images[saved[i]].src = currenttype;
} //ends for loop
function completefalling() {
//check for completed lines. Later add call for next piece to fall
var i;
var j;
var imgno;
var blankcount;
var tests;
var found;
i = vheight-1;
while (i>=0) {
blankcount = 0;
for (j=hwidth-1;j>=0;j--) {
imgno = imagenumber(j,i);
tests = String(document.images[imgno].src);
found = tests.search("bblock.gif");
if (found>-1) { // a blank
blankcount++ ;
} // end if test
}
alert("line i "+i+" has "+blankcount+" blanks");
i--;
} // end while loop of rows
} //end completefalling function
**************************
DRAFT 17
tinytetris8
This stage implements the task of removing filled lines and cascading the upper lines
down. The code counts spaces that are not blank and uses a variable, filledcount. There is
a variable named scoring used to give more points for removing multiple lines. My first
approach was to figure out how high up (high up on the board, low down in terms of
index values for the rows) the function needed to go. I made a variable called
lowestoccupiedrow. However, I abandoned this as requiring too much computation.
Instead, the for loop in the cascade function always goes all the way back to the 1st row.
The 0th row is blank because pieces start at row 1.
var scoring= [
1, 4, 8, 16]; // 1 for 1 line, 4 for 2 at a time, etc.
function completefalling() {
//check for completed lines. Later add call for next piece to fall
var i;
var j;
var imgno;
var filledcount;
var tests;
var found;
var linesremoved = 0;
i = vheight-1;
while (i>=0) {
filledcount = 0;
for (j=hwidth-1;j>=0;j--) {
imgno = imagenumber(j,i);
tests = String(document.images[imgno].src);
found = tests.search("bblock.gif");
if (found==-1) { // didn't find blank
filledcount++ ;
} // end if test
}
if (filledcount == hwidth) {
linesremoved++;
cascade(i);
//call cascade to remove line i. Will return here to while loop at new
line i
}
else {
i--;
}
} // end while loop of rows
if (linesremoved>0) {
document.f.lines.value = linesremoved +
parseInt(document.f.lines.value);
document.f.score.value = scoring[linesremoved-
1]+parseInt(document.f.score.value);
DRAFT 18
}
} //end completefalling function
function cascade(cut) {
// the line at row cut is to be removed, replaced by lines above
var upper;
var colindex;
var imgno;
var imgnox;
for (upper=cut;upper>0;upper--) {
for (colindex = 0; colindex<hwidth; colindex++) {
imgno = imagenumber(colindex,upper);
imgnox = imagenumber(colindex,upper-1);
document.images[imgno].src = document.images[imgnox].src;
}
}
}
</script>
…
**********************
tinytetris9
This next to the last stage was where I inserted the automatic start of a new block at the
top. This was made more elaborate at the last stage, when I finally put in timing.
function completefalling() {
…
if (filledcount == hwidth) {
linesremoved++;
cascade(i);
}
else {
i--;
}
} // end while loop of rows
if (linesremoved>0) {
document.f.lines.value = linesremoved +
parseInt(document.f.lines.value);
document.f.score.value = scoring[linesremoved-
1]+parseInt(document.f.score.value);
}
DRAFT 19
startnewpiece();
} //end completefalling function
function startnewpiece() {
var type = Math.floor(Math.random()*7);
var scol = Math.floor(Math.random()*5);
makeblock(type,scol,1); // start at second (index = 1)
row
}
function startgame() {
document.f.lines.value = "0";
document.f.score.value = "0";
startnewpiece();
}
*************************
tinytetris10
This last stage is when I added in the timing, that is, the automatic falling of the pieces.
My initial value for the interval was 2000 milliseconds, to give me time to think while
doing the debugging. At this point, I also decided to put in what I call a grace period.
After a piece hits blocks or the bottom of the board, there is a chance to make horizontal
moves before a new piece becomes the current piece. The 'real' game has this feature.
This involved setting up variables called startnewone and grace as well as the startgame
function. The approach appears to work, but I am not totally comfortable with it.
The startgame function invokes setInterval("clock();",timeperiod). The function clock
uses startnewone and grace. In some situations, it invokes makeblock and in others, it
invokes movedown. The completefalling function has changed. The display also has
changed, with the extra <a> javascript buttons removed.
Here is a table listing the functions with calling structure. This is a useful exercise to do
for applications. You can use the Find feature of NotePad or TextPad and then review to
decide if it makes sense. One question I asked myself was why moveover does not
require imagenumber. The answer is that the new image numbers can be calculated
directly as the originals plus dir, the parameter holding the direction. It may be possible
to extract common code from moveover, movedown and rotate since these do similar
things in preparing for moves.
DRAFT 20
clock action set by call to startnewpiece, movedown,
setInterval completefalling
rotate Button checkifhitdown,
imagenumber
checkifhitdown movedown, rotate, imagenumber
moveover
movedown clock checkifhitdown,
imagenumber
completefalling clock cascade, imagenumber
startgame Hyperlink (calls setInterval which sets
up calls to clock)
cascade completefalling imagenumber
imagenumber multiple places
createboard called when HTML file
loaded
<html>
<head>
<title>Simple Tetris </title>
<script language="JavaScript">
var hwidth = 9; number of columns
var vheight = 15; number of rows
var tid; timer id
var timeperiod = 500; Used in call to setInterval to
set interval between drops. Make
longer and shorter to ease
debugging.
var grace = 0; default grace period
var startnewone = false; flag
var graceperiod = 3; grace period
DRAFT 21
[[0,1],[1,1],[2,1],[2,0]] L shape
];
DRAFT 22
];
var current = [ image number, column, row of
current 4- block shape
[0,0,0],
[0,0,0],
[0,0,0],
[0,0,0]
];
var currenttype; holds image file name
var currenttypenum; 0 to 6
var currentorientation; 0 to 3
var currentorigin; nominal origin [x,y]
DRAFT 23
over.");
clearInterval(tid); Stop timing interval
break; Leave loop
} End of else (no room)
} End of loop
} End of function
if (oksofar) {
for (i=0; i<=3; i++) { Loop to setup saved
saved[i] = current[i][0];
document.images[current[i][0]].s Erase (blank out) current 4-
rc = "bblock.gif"; shape
} End for loop
DRAFT 24
if (oksofar) { If [still] ok, do move
for (i=0;i<=3;i++) { for loop
document.images[newcurrent[i] Move in this image file
].src= currenttype;
current[i][0] = Set new values—for image
newcurrent[i]; number
current[i][1] = Change the column value (row
current[i][1]+dir; stays the same)
} End loop
DRAFT 25
if (atr>=(vheight-1)) { Past the bottom of board?
oksofar = false;
break; } Leave loop. End clause.
newcurrent[i]=imagenumber(atc, atr); Calculate new img number.
} End for loop
if (oksofar) { If no problem so far…
for (i=0;i<=3;i++) { Save img numbers & clear slots
saved[i] = current[i][0];
document.images[current[i][0]].src =
"bblock.gif" }
for (i=0;i<=3;i++) { now go through and check each
target slot for block: for loop
tests = Prepare to check for clashes
String(document.images[newcurrent[i]].src);
found = tests.search("bblock.gif");
if (found == -1) { Something else in src
oksofar = false;
break; } End clause
} End for loop
if (oksofar) { If ok…no clashes
for (i=0;i<=3;i++) { For loop: do the move
imagenum=newcurrent[i];
document.images[imagenum].src = block;
current[i][0]=imagenum; Set new current data
current[i][1] = atcol+formula[i][0];
current[i][2] = atrow+formula[i][1];
}
checkifhitdown(); may have hit bottom as result of
rotate
} End if okay
else { need to restore from saved
for (i=0;i<=3;i++) { for loop
document.images[saved[i]].src = block;
} End for loop
currentorientation = savedorientation; Restore old orientation
} End else clause
} close first if oksofar
else { Else clause for first if
okaysofar
currentorientation = savedorientation; Restore old orientation
} End clause
} close function
function checkifhitdown() { Check if piece can't move
further down (no move). Similar
to code in move functions
var i;
var tests;
var oksofar = true;
var imgno;
var atc;
var atr;
var newcurrent = new Array();
var saved = new Array();
var found;
DRAFT 26
var hitdown = false;
for (i=0; i<=3; i++) {
imgno = current[i][0];
atc = current[i][1];
atr = current[i][2];
if (atr>=(vheight-1)) { at very bottom already
hitdown = true;
oksofar = false;
break;
}
newcurrent[i] = imagenumber(atc,atr+1); virtual move down
}
if (oksofar) {
for (i=0;i<=3; i++) { save image nums & blank out
current piece
saved[i] = current[i][0];
document.images[current[i][0]].src =
"bblock.gif";
} // ends for loop
for (i=0; i<=3; i++) { check if any blocking
tests =
String(document.images[newcurrent[i]].src);
found =
tests.search("bblock.gif");
if (found == -1) { meaning it was not found
oksofar = false;
atc = currentorigin[1];
hitdown = true;
break;
} ends if test
} ends for loop
for (i=0;i<=3; i++) { restore blocks in all cases
document.images[saved[i]].src =
currenttype;
} ends for loop
} ends first if oksofar
startnewone = true; Flag to start new piece, but…
grace = graceperiod; … will allow grace period (3
intervals)
return hitdown; This function returns value
} End function
DRAFT 27
var hitdown = false; Initialize to false
for (i=0; i<=3; i++) { For loop
imgno = current[i][0]; Img number for this block
atc = current[i][1]; Column of this block
atr = current[i][2]; Row of this block
if (atr>=(vheight-1)) { at very bottom already
hitdown = true; Flag
oksofar = false; Flag
break; Leave for loop
} End if clause
newcurrent[i] = imagenumber(atc,atr+1); Set newcurrent (used later to
make the move)
} End for loop
if (oksofar) { No problems so far
for (i=0;i<=3; i++) { save image nums & blank out
current piece
saved[i] = current[i][0]; just in case
document.images[current[i][0]].src = put in blank gif
"bblock.gif";
} ends for loop
for (i=0; i<=3; i++) { Now can check for absence of
other pieces
tests = Extract src
String(document.images[newcurrent[i]].src);
found = Do search
tests.search("bblock.gif");
if (found == -1) { meaning it was not found
oksofar = false; Problem—other piece
break; Leave for loop
} ends if test
} ends for loop
if (oksofar) { No problems
for (i=0;i<=3; i++) { For loop
document.images[newcurrent[i]].src = Do the move
currenttype;
current[i][0] = newcurrent[i]; Set current data
current[i][2]++; y increases; x stays the same
} //ends for loop
currentorigin[1]++;
} ends clause for inner oksofar
else { Else for problem
for (i=0;i<=3; i++) { for loop
document.images[saved[i]].src = Restore current image
currenttype;
hitdown = true; Set flag indicating hitdown
} ends for loop
} ends else of second oksofar
} ends first if oksofar
if (hitdown) { tried to move down beyond
startnewone=true; Set flag to start new piece
grace = 0; No grace period
} End if clause
else { Not down now, but
if (checkifhitdown()) { tests if can go one more
DRAFT 28
startnewone = true; Set flag to start new piece
grace = graceperiod; Allow grace period
} End if
} End else
} End function
function clock () { Called by setInterval
if (startnewone) { Start new piece after any grace
period
if (grace==0) { Check grace
startnewone = false; reset flag
completefalling(); call function to check for
filled lines
startnewpiece(); Call function to start new
piece
} End if grace down to zero
else { Still grace period
grace--; } Decrement grace
} End if startnewone
movedown(); //move current piece down In all cases, move piece down
} End function
DRAFT 29
parseInt(document.f.lines.value);
document.f.score.value = Increase displayed score using
scoring[linesremoved- scoring values
1]+parseInt(document.f.score.value);
} End if test for lines removed
} end completefalling function
DRAFT 30
<input type="button" onClick="moveover(-1);" buttons…
value="left">
<input type="button" onClick="rotate();"
value="rotate">
<input type="button" onClick="moveover(1);"
value="right">
<br/>
<input type="button" onClick="movedown();" Does work, but should be changed
value="down"> to do move all the way down
<br/>
Lines: <input type="text" name="lines" value="0"> displayed lines removed
<br/>
Score: <input type="text" name="score" value="0"> displayed score
<br/>
</form>
</br>
</td>
</table>
<img src="bblock.gif"/>
<a href="javascript:startgame();">Start Game </a> Hyperlink to call startgame
<br/> function
</body>
</html>
DRAFT 31