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: /* 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(); }
I forgot to mention, download the attached Zip File as it contains not only the C File, but also some documentation and some ready-made samples. Ciao, Rajiv PS: A little bit of feedback is always encouragin
I really liked the program but would prefer to have an option of escaping from the game and return back to the code.