Introduction
The Source code that follows is for Tetris using BGI under Turbo C/C++ DOS compiler. It is still in the preliminary stages of development and there is a long way to go.
Now, this is a demo version where the user isnt supposed to 'play' the game but be a mute spectator - it just demonstrates the Graphics User Interface and to some extent logic in the program.
Now, I hav achieved vertical motion logic - i.e. till what point can a Tetris Block proceed and when it should stop. While there is horizontal motion in the program, it is uncontrolled - so far, I hav been unable to find a logic that controls horizontal motion of blocks and also fits with the way I hav coded.
I hav provided adequate comments throughout the code, so u guys will hav a fair idea.
PS : Once the Tetris Blocks begin falling, it may seem like its taking too long. There are in all 30 blocks which will fall as part of the demo .... so be patient
.... otherwise simply reduce the value of the "NUM_OF_BLCKS" macro
The Code ...
Source Code:
Code: C
/* Tetris in C++ using BGI ==> DEMO Version 1.0 */
// Execute this in a TC/C++ DOS Compiler
// THIS IS JUST A DEMO VERSION & IS AS SUCH INCOMPLETE
/*
Coded By :
Rajiv A Iyer,
TE Comps, SIES GST
contact : rai.gandalf@gmail.com
*/
// AUTHOR/PROGRAMMER RESERVES ALL COPYRIGHT (C)
/* Source Code : */
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <graphics.h>
#define NUM_OF_BLCKS 30
// Symbolic Constants
const int Sys_MAXX=640,Sys_MAXY=480;
const int EMPTY=-1,TRUE=1,FALSE=0;
// Global Variables
int driver=DETECT,mode,error;
// Structure and Class Declarations
class Window;
class Tetris;
class Window
{
private:
int xleft,ytop,xright,ybottom;
int Win_MAXX_Span,Win_MAXY_Span;
int linecolor,bgcolor;
public:
Window()
{
// NULL CONSTRUCTOR
// Assigning in the Sequence :
// Upper_Left(UL) , Lower_Right(LR)
xleft=ytop=0;
xright=Sys_MAXX;ybottom=Sys_MAXY;
Win_MAXX_Span=Sys_MAXX;
Win_MAXY_Span=Sys_MAXY;
linecolor=WHITE;
bgcolor=BLACK;
}
void set_window_par(int[],int,int);
void draw_window(void);
void putstring(char*,int,int);
int get_parameter(int);
};
void Window :: set_window_par(int xyco[],int lc=WHITE,int bgc=BLACK)
{
// Assigning in the Sequence :
// Upper_Left(UL) , Lower_Right(LR)
xleft=xyco[0];
ytop=xyco[1];
xright=xyco[2];
ybottom=xyco[3];
Win_MAXX_Span=xright-xleft;
Win_MAXY_Span=ybottom-ytop;
linecolor=lc;
bgcolor=bgc;
}
void Window :: draw_window(void)
{
setcolor(linecolor);
setlinestyle(0,0,1);
rectangle(xleft,ytop,xright,ybottom);
setfillstyle(1,bgcolor);
floodfill(xleft+1,ytop+1,linecolor);
}
void Window :: putstring(char s[],int xco,int yco)
{
moveto(xco,yco);
settextstyle(TRIPLEX_FONT,HORIZ_DIR,0);
setcolor(WHITE);
setusercharsize(1,2,1,2);
outtext(s);
}
int Window :: get_parameter(int Para_id)
{
int Para_value;
switch(Para_id)
{
case 1: Para_value=xleft;
case 2: Para_value=ytop;
case 3: Para_value=xright;
case 4: Para_value=ybottom;
case 5: Para_value=Win_MAXX_Span;break;
case 6: Para_value=Win_MAXY_Span;break;
case 7: Para_value=linecolor;break;
case 8: Para_value=bgcolor;break;
}
return Para_value;
}
class Tetris
{
private:
int SCode,PCode,TCode;
int **tarr,fbsize,boxtype;
int VXCh,HYCh,fillcol;
Window W1; // Main Window
Window W2; // Side Window
Window W3; // Next_Piece Window
Window W4; // Total Score Window
Window W5; // "Total Lines Destroyed" Window
Window W6; // "Lines Remaining for Game Over" Window
Window W7; // "Level" Window
public:
Tetris(){fbsize=10;fillcol=RED;}
void regulate_play(void);
void initialize_windows(void);
void gen_randshape(void);
void draw_shape(int,int,int,Window*);
int verify_vert_movement(int,int);
}Temp;
void Tetris :: regulate_play(void)
{
int i,j,r;
int channel,y=0;
int prevchan,prevy;
initialize_windows();
/*
Looping as many times as user desires .Each time, one Tetris-Block is
produced randomly at the top of Main Window, slides in Free-fall
and settles at floor of the Tetris Window. In the meanwhile, next piece
is produced randomly(displayed in "Next Piece Window"), which is used as
the Falling Tetris-Block in next iteration.
Please note, here:
'T' - the invoking object from main() is object for Tetris Main Window
'Temp' - Is the Object for "Next Piece Window"
*/
for(r=0;r<NUM_OF_BLCKS;r++)
{
if(r==0)
// Very 1st Looping
{
// Hence, Generating shape to be used currently/initially by using Next_Piece Object
Temp.gen_randshape();
// Randomly Selecting the FillColor of the Shape that will be drawn in Main Window
do{
fillcol=random(16);
}while(fillcol==WHITE||fillcol==YELLOW||fillcol==BLACK);
}
else
// Subsequent Iterations
{
// Erasing "Next Piece Window" Graphically
W3.draw_window();
// Erasing "Next Piece Window" Logically (i.e. contents of tarr[][] reset)
Temp.draw_shape(3,3,EMPTY,&W3);
}
// Making the Next Piece Obj contents as the Main Window Piece Obj contents
SCode=Temp.SCode;
PCode=Temp.PCode;
TCode=Temp.TCode;
if(r!=0)
fillcol=Temp.fillcol;
// Generating a Random Next-Piece =>
// This will by above assignments become
// the Current Piece in next iteration
Temp.gen_randshape();
// Randomly Selecting the Channel =>
// One of the (45-3) Vertical Channels that the piece can be launched
// into Free-Fall from Top of the Tetris Window
channel=random(VXCh-3);
// Randomly Selecting the FillColor of the Shape that will be drawn in the Next Piece Window
do{
Temp.fillcol=random(16);
}while(Temp.fillcol==WHITE||Temp.fillcol==YELLOW||Temp.fillcol==BLACK);
// Drawing the Pieces in Main & Next Piece Window respectively
draw_shape(channel,y,fillcol,&W1);
Temp.draw_shape(3,3,Temp.fillcol,&W3);
// Erasing Piece from Main Window
W1.draw_window();draw_shape(channel,y,EMPTY,&W1);
// Iterating for Current Piece for Free-Fall Motion to floor of Tetris Window
// Here:
// 1. 'j' will control the FREE-FALLING Motion of Tetris Block
// 2. 'channel' will control Horizontal/Sideways Motion of Tetris Block
for(j=y;(j<HYCh-2&&boxtype==23)||(j<HYCh-1&&boxtype==32)
||(j<HYCh-3&&boxtype==14)||(j<HYCh-1&&boxtype==22)
||(j<HYCh&&boxtype==41);j++)
{
prevchan=channel;
if(channel==0)
// We have reached the LEFTMOST boundary
// Hence, we may either let 'channel' be or Increment it
// We CANNOT and MUST NOT Decrement 'channel'
{
switch(random(2))
{
case 0:break;
case 1:channel++;
}
}
else if((channel==VXCh-2&&boxtype==23) || (channel==VXCh-3&&boxtype==32) ||
(channel==VXCh-1&&boxtype==14) || (channel==VXCh-4&&boxtype==41) ||(channel==VXCh-2&&boxtype==22))
// We have reached the RIGHTMOST boundary
// Hence, we may either let 'channel' be or Decrement it
// We CANNOT and MUST NOT Increment 'channel'
{
switch(random(2))
{
case 0:break;
case 1:channel--;
}
}
else
// We are somewhere in the middle
// Hence, we may move leftwards, rightwards or stay as it is
{
switch(random(3))
{
case 0:channel++;break;
case 1:channel--;break;
case 2:break;
}
}
if(j!=y)
// Executing for all iterations except the 1st one
{
// Erasing contents of Tetris Main Window Graphically
W1.draw_window();
// Erasing contents of Tetris Main Window Logically
// (i.e. contents of tarr[][] are reset)
draw_shape(prevchan,j-1,EMPTY,&W1);
}
// Drawing the Tetris Block with new values of 'channel' & 'j'
draw_shape(channel,j,fillcol,&W1);
// Testing whether further vertical motion is possible or not
// Depending upon that, we either continue or abandon iterating
if(!verify_vert_movement(channel,j))
break;
//delay(100);
}
}
}
void Tetris :: initialize_windows(void)
{
//TESTING Window Drawings
initgraph(&driver,&mode,"\\TC\\BGI");
error=graphresult();
if(error!=grOk)
{
printf("GRAPHICS ERROR!!");
getch();
exit(1);
}
int xy1[4],xy2[4],xy3[4],xy4[4],xy5[4],xy6[4],xy7[4];
// Setting Window Parameters
xy1[0]=10;xy1[1]=10;
xy1[2]=460;xy1[3]=470;
xy2[0]=480;xy2[1]=10;
xy2[2]=630;xy2[3]=470;
xy3[0]=510;xy3[1]=40;
xy3[2]=600;xy3[3]=120;
xy4[0]=495;xy4[1]=160;
xy4[2]=615;xy4[3]=190;
xy5[0]=495;xy5[1]=230;
xy5[2]=615;xy5[3]=260;
xy6[0]=495;xy6[1]=300;
xy6[2]=615;xy6[3]=330;
xy7[0]=495;xy7[1]=370;
xy7[2]=615;xy7[3]=400;
W1.set_window_par(xy1);
W2.set_window_par(xy2);
W3.set_window_par(xy3);
W4.set_window_par(xy4);
W5.set_window_par(xy5);
W6.set_window_par(xy6);
W7.set_window_par(xy7);
// Initializing for the Main Window Tetris Obj 'T/Temp' the following parameters ;
// 1. No. of Vertical Channels or Vertical Span (VXCh)
// 2. No. of Horizontal Channels ot Horizontal Span (HYCh)
// 3. 2D-Array 'tarr' where -
// tarr[i][j] => 10pixel by 10pixel block, 3-4 of which put together forms ONE Tetris Block
VXCh=W1.get_parameter(5)/fbsize;
HYCh=W1.get_parameter(6)/fbsize;
tarr=(int**)malloc((VXCh)*sizeof(int));
for(int i=0;i<VXCh;i++)
tarr[i]=(int*)malloc((HYCh)*sizeof(int));
for(int j=0;j<VXCh;j++)
for(int k=0;k<HYCh;k++)
tarr[j][k]=EMPTY;
// Initializing for the Next Piece Window Tetris Obj 'Temp' the following parameters ;
// 1. No. of Vertical Channels or Vertical Span (HYCh)
// 2. No. of Horizontal Channels ot Horizontal Span (HYCh)
// 3. 2D-Array 'tarr' where -
// tarr[i][j] => 10pixel by 10pixel block, 3-4 of which put together forms ONE Tetris Block
Temp.VXCh=W3.get_parameter(5)/fbsize;
Temp.HYCh=W3.get_parameter(6)/fbsize;
Temp.tarr=(int**)malloc((Temp.VXCh)*sizeof(int));
for(i=0;i<Temp.VXCh;i++)
Temp.tarr[i]=(int*)malloc((Temp.HYCh)*sizeof(int));
for(j=0;j<Temp.VXCh;j++)
for(k=0;k<Temp.HYCh;k++)
Temp.tarr[j][k]=EMPTY;
// Drawing all the Windows
W1.draw_window();
W2.draw_window();
W3.draw_window();
W4.draw_window();
W5.draw_window();
W6.draw_window();
W7.draw_window();
// Writing String Entries in Windows
W2.putstring("Next Piece :",495,20);
W2.putstring("SCORE :",495,140);
W2.putstring("Tot. Lines Dest. :",495,210);
W2.putstring("Lines Rem. :",495,280);
W2.putstring("LEVEL :",495,350);
}
void Tetris :: gen_randshape(void)
{
SCode=random(7);
// Scode=0 --> L-Shape , Scode=1 --> InvL-Shape
// Scode=2 --> T-Shape , Scode=3 --> Z-Shape
// Scode=4 --> InvZ-Shape , Scode=5 --> I-Shape
// Scode=6 --> Sq-Shape
// Tcode=0 --> L1 , Tcode=1 --> L2 , Tcode=2 --> L3
// Tcode=3 --> L4 , Tcode=4 --> IL1 , Tcode=5 --> IL2
// Tcode=6 --> IL3 , Tcode=7 --> IL4 , Tcode=8 --> T1
// Tcode=9 --> T2 , Tcode=10 --> T3 , Tcode=11 --> T4
// Tcode=12 --> Z1 , Tcode=13 --> Z2 , Tcode=14 --> IZ1
// Tcode=15 --> IZ2 , Tcode=16 --> I1 , Tcode=17 --> I2
// Tcode=18 --> Sq
/*
NOTE:
1. TCode=0 --> L1 => Normal 'L' Shaped Block, i.e. '|_'
Give Anti-Clockwise Rotation to visualize L2, L3 & L4
2. TCode=4 --> IL1 => Inverted 'L' Shaped Block, i.e. '_|'
Give Anti-Clockwise Rotation to visualize IL2, IL3 & IL4
3. TCode=8 --> T1 => '_|_' Shaped Block
Give Anti-Clockwise Rotation to visualize T2, T3 & T4
_
4. TCode=12 -->Z1 => Normal 'Z' Shaped Block, i.e. ' |_'
Give Anti-Clockwise Rotation to visualize Z2
_
5. TCode=14 -->IZ1 => Inverted 'Z' Shaped Block, i.e. '_| '
Give Anti-Clockwise Rotation to visualize IZ2
6. TCode=16 -->I1 => Long Vertically Standing Block
7. TCode=17 -->I2 => Long Horizontally Sleeping Block
8. TCode=18 -->Sq => Sq Block consisting of 2 by 2 fundamental blocks
Each of the above 8 sets consist of 4 Fundamental Blocks
*/
switch(SCode)
{
case 0:
case 1:
case 2: PCode=random(4);TCode=4*SCode+PCode;break;
case 3: PCode=random(2);TCode=12+PCode;break;
case 4: PCode=random(2);TCode=14+PCode;break;
case 5: PCode=random(2);TCode=16+PCode;break;
case 6: PCode=0;TCode=18;
}
}
void Tetris :: draw_shape(int channel,int y,int DMode,Window *W)
{
if(TCode==18)
{
// Boxtype is 2Col by 2Rows (i.e. 2VXPix by 2HYPix)
boxtype=22;
// Drawing the 4 Blocks Logically
tarr[channel][y]=tarr[channel][y+1]=tarr[channel+1][y]=tarr[channel+1][y+1]=DMode;
}
else if(TCode==16)
{
// Boxtype is 1Col by 4Row (i.e. 1VXPix by 4HYPix)
boxtype=14;
// Drawing the 4 Blocks Logically
tarr[channel][y]=tarr[channel][y+1]=tarr[channel][y+2]=tarr[channel][y+3]=DMode;
}
else if(TCode==17)
{
// Boxtype is 4Col by 1Row (i.e. 4VXPix by 1HYPix)
boxtype=41;
// Drawing the 4 Blocks Logically
tarr[channel][y]=tarr[channel+1][y]=tarr[channel+2][y]=tarr[channel+3][y]=DMode;
}
else if(TCode==0||TCode==2||TCode==4||TCode==6||
TCode==9||TCode==11||TCode==13||TCode==15)
{
// Boxtype is 2Col by 3Rows (i.e. 2VXPix by 3HYPix)
boxtype=23;
if(TCode==0||TCode==6||TCode==9)
// Drawing the Common Blocks Logically
tarr[channel][y]=tarr[channel][y+1]=tarr[channel][y+2]=DMode;
else if(TCode==2||TCode==4||TCode==11)
// Drawing the Common Blocks Logically
tarr[channel+1][y]=tarr[channel+1][y+1]=tarr[channel+1][y+2]=DMode;
else if(TCode==13||TCode==15)
// Drawing the Common Blocks Logically
tarr[channel][y+1]=tarr[channel+1][y+1]=DMode;
}
else if(TCode==1||TCode==3||TCode==5||TCode==7||
TCode==8||TCode==10||TCode==12||TCode==14)
{
// Boxtype is 3Col by 2Rows (i.e. 3VXPix by 2HYPix)
boxtype=32;
if(TCode==1||TCode==7||TCode==8)
// Drawing the Common Blocks Logically
tarr[channel][y+1]=tarr[channel+1][y+1]=tarr[channel+2][y+1]=DMode;
else if(TCode==3||TCode==5||TCode==10)
// Drawing the Common Blocks Logically
tarr[channel][y]=tarr[channel+1][y]=tarr[channel+2][y]=DMode;
else if(TCode==12||TCode==14)
// Drawing the Common Blocks Logically
tarr[channel+1][y]=tarr[channel+1][y+1]=DMode;
}
// Drawing the Specialized Xtra Blocks Logically
switch(TCode)
{
case 0: tarr[channel+1][y+2]=DMode;break;
case 1: tarr[channel+2][y]=DMode;break;
case 2: tarr[channel][y]=DMode;break;
case 3: tarr[channel][y+1]=DMode;break;
case 4: tarr[channel][y+2]=DMode;break;
case 5: tarr[channel+2][y+1]=DMode;break;
case 6: tarr[channel+1][y]=DMode;break;
case 7: tarr[channel][y]=DMode;break;
case 8: tarr[channel+1][y]=DMode;break;
case 9: tarr[channel+1][y+1]=DMode;break;
case 10:tarr[channel+1][y+1]=DMode;break;
case 11:tarr[channel][y+1]=DMode;break;
case 12:tarr[channel][y]=DMode;tarr[channel+2][y+1]=DMode;break;
case 13:tarr[channel+1][y]=DMode;tarr[channel][y+2]=DMode;break;
case 14:tarr[channel][y+1]=DMode;tarr[channel+2][y]=DMode;break;
case 15:tarr[channel][y]=DMode;tarr[channel+1][y+2]=DMode;break;
}
// Actually Drawing it Graphically for Window W1
if(W==&W1)
{
for(int i=0;i<VXCh;i++)
for(int j=0;j<HYCh;j++)
if(tarr[i][j]!=EMPTY)
{
setcolor(YELLOW);
setlinestyle(0,0,1);
rectangle(fbsize*(i+1),fbsize*(j+1),fbsize*(i+2),fbsize*(j+2));
setfillstyle(1,tarr[i][j]);
floodfill(fbsize*(i+1)+1,fbsize*(j+1)+1,YELLOW);
}
}
else
{
for(int i=0;i<VXCh;i++)
for(int j=0;j<HYCh;j++)
if(tarr[i][j]!=EMPTY)
{
setcolor(YELLOW);
setlinestyle(0,0,1);
rectangle(fbsize*(i+51),fbsize*(j+4),fbsize*(i+52),fbsize*(j+5));
setfillstyle(1,tarr[i][j]);
floodfill(fbsize*(i+51)+1,fbsize*(j+4)+1,YELLOW);
}
}
}
int Tetris :: verify_vert_movement(int channel,int y)
{
if(TCode==18)
{
if(tarr[channel][y+2]!=EMPTY||tarr[channel+1][y+2]!=EMPTY)
return FALSE;
else
return TRUE;
}
else if(TCode==16)
{
if(tarr[channel][y+4]!=EMPTY)
return FALSE;
else
return TRUE;
}
else if(TCode==17)
{
if(tarr[channel][y+1]!=EMPTY||tarr[channel+1][y+1]!=EMPTY||tarr[channel+2][y+1]!=EMPTY||tarr[channel+3][y+1]!=EMPTY)
return FALSE;
else
return TRUE;
}
else if(TCode==0||TCode==2||TCode==4||TCode==6||
TCode==9||TCode==11||TCode==13||TCode==15)
{
if(TCode==0||TCode==6||TCode==9)
{
// Analysing the Common Blocks Logically
if(tarr[channel][y+3]!=EMPTY)
return FALSE;
}
else if(TCode==2||TCode==4||TCode==11)
{
// Analysing the Common Blocks Logically
if(tarr[channel+1][y+3]!=EMPTY)
return FALSE;
}
else if(TCode==13)
{
// Analysing the Blocks Logically
if(tarr[channel][y+3]!=EMPTY||tarr[channel+1][y+2]!=EMPTY)
return FALSE;
else
return TRUE;
}
else if(TCode==15)
{
// Analysing the Blocks Logically
if(tarr[channel][y+2]!=EMPTY||tarr[channel+1][y+3]!=EMPTY)
return FALSE;
else
return TRUE;
}
}
else if(TCode==1||TCode==3||TCode==5||TCode==7||
TCode==8||TCode==10||TCode==12||TCode==14)
{
if(TCode==1||TCode==7||TCode==8)
{
// Analysing the Common Blocks Logically
if(tarr[channel][y+2]!=EMPTY||tarr[channel+1][y+2]!=EMPTY||tarr[channel+2][y+2]!=EMPTY)
return FALSE;
else
return TRUE;
}
else if(TCode==12)
{
// Analysing the Blocks Logically
if(tarr[channel][y+1]!=EMPTY||tarr[channel+1][y+2]!=EMPTY||tarr[channel+2][y+2]!=EMPTY)
return FALSE;
else
return TRUE;
}
else if(TCode==14)
{
// Analysing the Blocks Logically
if(tarr[channel][y+2]!=EMPTY||tarr[channel+1][y+2]!=EMPTY||tarr[channel+2][y+1]!=EMPTY)
return FALSE;
else
return TRUE;
}
}
// Analysing the Specialized Xtra Blocks Logically
switch(TCode)
{
case 0: if(tarr[channel+1][y+3]!=EMPTY)return FALSE;break;
case 6: if(tarr[channel+1][y+1]!=EMPTY)return FALSE;break;
case 9: if(tarr[channel+1][y+2]!=EMPTY)return FALSE;break;
case 2: if(tarr[channel][y+1]!=EMPTY)return FALSE;break;
case 4: if(tarr[channel][y+3]!=EMPTY)return FALSE;break;
case 11:if(tarr[channel][y+2]!=EMPTY)return FALSE;break;
case 3: if(tarr[channel][y+2]!=EMPTY||tarr[channel+1][y+1]!=EMPTY||tarr[channel+2][y+1]!=EMPTY)return FALSE;break;
case 5: if(tarr[channel][y+1]!=EMPTY||tarr[channel+1][y+1]!=EMPTY||tarr[channel+2][y+2]!=EMPTY)return FALSE;break;
case 10:if(tarr[channel][y+1]!=EMPTY||tarr[channel+1][y+2]!=EMPTY||tarr[channel+2][y+1]!=EMPTY)return FALSE;//break;
}
return TRUE;
}
// Main Prog :
void main()
{
//Seeding the Random Number Generator
randomize();
Tetris T;
printf("\n\n\aWelcome to Tetris 2.0C (BETA) DEMO!!\n\n\a");
printf("\n\n\aThis Demo is a glimpse into a preliminary Version and just shows the Movement of Various Tetris Pieces along the Tetris Play Space\a\n\n");
printf("\n\n\aUser I/P is NOT Expected\a\n\n");
printf("\n\n\n\aPress ENTER to goto Graphics Mode and See the DEMO\a\n\n\n");
getch();clrscr();
T.regulate_play();
getch();
}

