(*
National russian game "Tetris"
Created by Alexey Pajitnov
Programmed in JOB by S.Sverdlov
8.03.98/23.04.98
<applet code="Tetris_Game" width=180 height=360>
</applet>
*)
MODULE Tetris;
IMPORT
app := javaapplet,
awt := javaawt,
ut := javautil,
Timers, SYSTEM;
CONST
n = 24;
m = 12;
s = 14;
UP = 1004;
DOWN = 1005;
LEFT = 1006;
RIGHT = 1007;
TYPE
Field = ARRAY n, m OF awt.PColor;
Figure = ARRAY 4, 4 OF BOOLEAN;
Figures = ARRAY 7 OF Figure;
Colors = ARRAY 7 OF awt.PColor;
PGame* = POINTER TO Game;
Game* = RECORD (app.Applet)
g : awt.PGraphics;
field : Field;
figs : Figures;
bc : awt.PColor;
cols : Colors;
ncurr : INTEGER;
curr : Figure;
x, y : INTEGER;
timer : Timers.Timer;
rand : ut.PRandom;
fly : BOOLEAN;
timeevent: BOOLEAN;
keyevent : BOOLEAN;
END;
PROCEDURE ( a : PGame ) paint*( g : awt.PGraphics );
VAR
i, j : INTEGER;
x, y : INTEGER;
BEGIN
y := 0;
FOR i := 0 TO n-1 DO
x := 0;
FOR j := 0 TO m-1 DO
g.setColor(a.field[i,j]);
g.fillRect(x,y,s,s);
INC( x, s+1 );
END;
INC( y, s+1 );
END;
END paint;
PROCEDURE InitField( a : PGame );
VAR
i, j : INTEGER;
BEGIN
FOR i := 0 TO n-1 DO
FOR j := 0 TO m-1 DO
a.field[i,j] := a.bc;
END;
END;
END InitField;
PROCEDURE Draw( a : PGame; col : awt.PColor );
VAR
i, j : INTEGER;
x, y : INTEGER;
BEGIN
y := a.y;
FOR i := 0 TO 3 DO
x := a.x;
FOR j := 0 TO 3 DO
IF a.curr[i,j] & ( 0 <= x ) & ( x < m ) & ( 0 <= y ) & ( y < n ) THEN
a.g.setColor(col);
a.g.fillRect(x*(s+1),y*(s+1),s,s);
END;
INC(x);
END;
INC(y);
END;
END Draw;
PROCEDURE Free( a : PGame; VAR f : Figure; x0, y0 : INTEGER ) : BOOLEAN;
VAR
i, j : INTEGER;
x, y : INTEGER;
F : BOOLEAN;
BEGIN
F := TRUE;
i := 0; y := y0;
REPEAT
j := 0; x := x0;
REPEAT
IF f[i,j] &
( (x<0) OR (x>=m) OR (y>=n) OR
(y>=0) & (a.field[y,x] # a.bc) )
THEN
F := FALSE;
END;
INC(j); INC(x);
UNTIL ~F OR (j=4);
INC(i); INC(y);
UNTIL ~F OR (i=4);
RETURN F;
END Free;
PROCEDURE Move( a : PGame; dx, dy : INTEGER );
BEGIN
Draw( a, a.bc );
INC( a.x, dx ); INC( a.y, dy );
Draw( a, a.cols[a.ncurr] );
END Move;
PROCEDURE Fix( a : PGame );
VAR
i, j : INTEGER;
x, y : INTEGER;
BEGIN
y := a.y;
FOR i := 0 TO 3 DO
x := a.x;
FOR j := 0 TO 3 DO
IF a.curr[i,j] & ( 0<=x ) & ( x<m ) & ( 0<=y ) & ( y<n ) THEN
a.field[y,x] := a.cols[a.ncurr];
END;
INC(x);
END;
INC(y);
END;
END Fix;
PROCEDURE RowEmpty( a : PGame; r : INTEGER ) : BOOLEAN;
VAR
e : BOOLEAN;
j : INTEGER;
BEGIN
j := m;
REPEAT
DEC(j);
e := a.field[r,j] = a.bc;
UNTIL (j=0) OR ~e;
RETURN e;
END RowEmpty;
PROCEDURE ClearRow( a : PGame; r : INTEGER );
BEGIN
WHILE (r>0) & ~RowEmpty(a, r) DO
a.field[r] := a.field[r-1];
DEC(r);
END;
a.paint(a.g);
END ClearRow;
PROCEDURE RowFull( a : PGame; r : INTEGER ) : BOOLEAN;
VAR
j : INTEGER;
full : BOOLEAN;
BEGIN
j := m;
REPEAT
DEC(j);
full := a.field[r,j] # a.bc;
UNTIL (j=0) OR ~full;
RETURN full;
END RowFull;
PROCEDURE TestRows( a : PGame );
VAR
i, j : INTEGER;
r : INTEGER;
BEGIN
i := 3;
r := a.y + i;
REPEAT
IF ( r < n ) & ( r>=0 ) & RowFull( a, r ) THEN
ClearRow( a, r );
ELSE
DEC(r);
END;
DEC(i);
UNTIL i<0;
END TestRows;
PROCEDURE FigDown( a : PGame );
BEGIN
IF Free( a, a.curr, a.x, a.y+1 ) THEN
Move( a, 0, 1 )
ELSE
a.fly := FALSE;
Fix(a);
TestRows(a);
END;
END FigDown;
PROCEDURE Rotate( f : Figure; VAR fr : Figure );
VAR
i, j : INTEGER;
BEGIN
i := 0; j := 3;
REPEAT
fr[i,0] := f[0,j];
fr[3,i] := f[i,0];
fr[j,3] := f[3,i];
fr[0,i] := f[i,3];
INC(i); DEC(j);
UNTIL j<0;
fr[1,1] := f[1,2];
fr[2,1] := f[1,1];
fr[2,2] := f[2,1];
fr[1,2] := f[2,2];
END Rotate;
PROCEDURE FigRotate(a : PGame);
VAR
temp : Figure;
BEGIN
Rotate( a.curr, temp );
IF Free( a, temp, a.x, a.y ) THEN
Draw( a, a.bc );
a.curr := temp;
Draw( a, a.cols[a.ncurr] );
END;
END FigRotate;
PROCEDURE FigRelease(a : PGame);
BEGIN
WHILE Free( a, a.curr, a.x, a.y+1 ) DO
Move( a, 0, 1 );
END;
END FigRelease;
PROCEDURE FigRight(a : PGame);
BEGIN
IF Free( a, a.curr, a.x+1, a.y ) THEN
Move( a, 1, 0 );
END;
END FigRight;
PROCEDURE FigLeft(a : PGame);
BEGIN
IF Free( a, a.curr, a.x-1, a.y ) THEN
Move( a, -1, 0 );
END;
END FigLeft;
PROCEDURE NewColor( r, g, b : INTEGER ) : awt.PColor;
VAR
c : awt.PColor;
BEGIN
SYSTEM.NEW( c, r, g, b );
RETURN c;
END NewColor;
PROCEDURE InitColors( a : PGame );
BEGIN
a.bc := NewColor( 220, 220, 220 );
a.cols[0] := NewColor( 255, 0, 0 );
a.cols[1] := NewColor( 255, 128, 0 );
a.cols[2] := NewColor( 255, 255, 0 );
a.cols[3] := NewColor( 0, 255, 0 );
a.cols[4] := NewColor( 0, 255, 255 );
a.cols[5] := NewColor( 0, 0, 255 );
a.cols[6] := NewColor( 255, 0, 255 );
END InitColors;
PROCEDURE InitFigures( a : PGame );
VAR
i, j, k : INTEGER;
BEGIN
FOR i := 0 TO 6 DO
FOR j := 0 TO 3 DO
FOR k := 0 TO 3 DO
a.figs[i,j,k] := FALSE;
END;
END;
END;
a.figs[0,1,0] := TRUE;
a.figs[0,1,1] := TRUE;
a.figs[0,1,2] := TRUE;
a.figs[0,1,3] := TRUE;
a.figs[1,1,2] := TRUE;
a.figs[1,2,0] := TRUE;
a.figs[1,2,1] := TRUE;
a.figs[1,2,2] := TRUE;
a.figs[2,1,0] := TRUE;
a.figs[2,1,1] := TRUE;
a.figs[2,1,2] := TRUE;
a.figs[2,2,2] := TRUE;
a.figs[3,1,1] := TRUE;
a.figs[3,1,2] := TRUE;
a.figs[3,2,1] := TRUE;
a.figs[3,2,2] := TRUE;
a.figs[4,1,0] := TRUE;
a.figs[4,1,1] := TRUE;
a.figs[4,2,1] := TRUE;
a.figs[4,2,2] := TRUE;
a.figs[5,0,1] := TRUE;
a.figs[5,1,1] := TRUE;
a.figs[5,1,2] := TRUE;
a.figs[5,2,2] := TRUE;
a.figs[6,1,0] := TRUE;
a.figs[6,1,1] := TRUE;
a.figs[6,1,2] := TRUE;
a.figs[6,2,1] := TRUE;
END InitFigures;
PROCEDURE Rnd( r : ut.PRandom; n : INTEGER ) : INTEGER;
BEGIN
RETURN SHORT(ENTIER(n*r.nextFloat()));
END Rnd;
PROCEDURE NewFigure( a : PGame );
VAR
rot : INTEGER;
BEGIN
a.x := m DIV 2 - 2;
a.y := -2;
a.ncurr := Rnd( a.rand, 7 );
a.curr := a.figs[a.ncurr];
rot := Rnd( a.rand, 4 );
WHILE rot > 0 DO
Rotate( a.curr, a.curr );
DEC(rot);
END;
END NewFigure;
PROCEDURE TimerStep( a : PGame );
BEGIN
IF a.fly THEN
FigDown(a);
ELSE
NewFigure(a);
IF Free( a, a.curr, a.x, a.y ) THEN
Draw(a, a.cols[a.ncurr]);
a.fly := TRUE;
ELSE
InitField(a);
a.paint(a.g);
END;
END;
END TimerStep;
PROCEDURE ( a : PGame ) keyDown*( e : awt.PEvent; key : INTEGER ) : BOOLEAN;
BEGIN
REPEAT UNTIL ~a.timeevent;
a.keyevent := TRUE;
CASE key OF
|UP : FigRotate(a);
|DOWN : FigRelease(a);
|LEFT : FigLeft(a);
|RIGHT : FigRight(a);
ELSE
END;
a.keyevent := FALSE;
RETURN TRUE;
END keyDown;
PROCEDURE ( a : PGame ) handleEvent*( e : awt.PEvent ) : BOOLEAN;
BEGIN
IF ( e.id = Timers.TimerEVENT ) & ~a.keyevent THEN
a.timeevent := TRUE;
TimerStep(a);
a.timeevent := FALSE;
RETURN TRUE;
END;
RETURN a.handleEvent^(e);
END handleEvent;
PROCEDURE ( a : PGame ) init*();
BEGIN
InitColors( a );
InitFigures( a );
InitField( a );
SYSTEM.NEW( a.rand );
a.g := a.getGraphics();
a.fly := FALSE;
a.timer.Start( a, 400 );
a.timeevent := FALSE;
a.keyevent := FALSE;
END init;
END Tetris.