/* TETRIS -- C ** @author Clooth */ #include #include #include /* MAIN GAME VARIABLES */ #define SCREEN_WIDTH 10 #define SCREEN_HEIGHT 20 #define SCREEN_POSITION 20 #define TYPES 19 #define SIZE 4 #define STARTSPEED 2000 #define BLOCK_CHARACTER '[]' #define EMPTY_CHARACTER ' ' #define BORDER '+' /* CONTROLS */ #define LEFT 'z' #define RIGHT 'c' #define ROTATE 'm' #define END 'e' enum type { square, horizontal_long, vertical_long, stair1, stair2, stair3, stair4, T_up, T_right, T_down, T_left, L1_up, L1_right, L1_down, L1_left, L2_up, L2_right, L2_down, L2_left }; type modification[TYPES]= { square, horizontal_long, vertical_long, stair1, stair2, stair3, stair4, T_up, T_right, T_down, T_left, L1_up, L1_right, L1_down, L1_left, L2_up, L2_right, L2_down, L2_left }; struct block { int x[SIZE]; int y[SIZE]; type t; }; p; int stuck[SCREEN_WIDTH+1][SCREEN_HEIGHT+1]; int points=0; /* Draw the borders around the game screen */ void draw_borders() { int i; for(i=1;i<=SCREEN_HEIGHT;i++) { gotoxy(SCREEN_POSITION+1,i); putch(BORDER); gotoxy(2*SCREEN_WIDTH+SCREEN_POSITION+2,i); putch(BORDER); } gotoxy(SCREEN_POSITION+1,SCREEN_HEIGHT+1); for(i=1;i<=2*SCREEN_WIDTH+2; i++) putch(BORDER); } /* Form the blocks */ void block_form(block *p, int x, int y) { p->x[1]=x; p->y[1]=y; switch(p->t) { case square: p->x[0]=x-1; p->x[2]=x-1; p->x[3]=x; p->x[0]=y; p->y[2]=y+1; p->y[3]=y+1; break; case horizontal_long: p->x[0]=x-1; p->x[2]=x+1; p->x[3]=x+2; p->y[0]=y; p->y[2]=y; p->y[3]=y; break; case vertical_long: p->x[0]=x; p->x[2]=x; p->x[3]=x; p->y[0]=y-1; p->y[2]=y+1; p->y[3]=y+2; break; case stair1: p->x[0]=x-1; p->x[2]=x+1; p->x[3]=x; p->y[0]=y+1; p->y[2]=y; p->y[3]=y+1; break; case stair2: p->x[0]=x-1; p->x[2]=x-1; p->x[3]=x; p->y[0]=y-1; p->y[2]=y; p->y[3]=y+1; break; case stair3: p->x[0]=x-1; p->x[2]=x; p->x[3]=x+1; p->y[0]=y-1; p->y[2]=y-1; p->y[3]=y; break; case stair4: p->x[0]=x+1; p->x[2]=x+1; p->x[3]=x; p->y[0]=y-1; p->y[2]=y; p->[3]=y+1; break; case T_up: p->x[0]=x; p->x[2]=x-1 p->x[3]=x+1; p->y[0]=y-1; p->y[2]=y; p->y[3]=y; break; case T_right: p->x[0]=x; p->x[2]=x+1; p->x[3]=x; p->y[0]=y-1; p->y[2]=y; p->y[3]=y+1; break; case T_down: p->x[0]=x-1; p->x[2]=x+1; p->x[3]=x; p->y[0]=y; p->y[2]=y; p->y[3]=y+1; break; case T_left: p->x[0]=x; p->x[2]=x-1; p->x[3]=x; p->y[0]=y-1; p->y[2]=y; p->y[3]=y+1; break; case L1_up: p->x[0]=x; p->x[2]=x; p->x[3]=x+1; p->y[0]=y-1; p->y[2]=y+1; p->y[3]=y+1; break; case L1_right: p->x[0]=x-1; p->x[2]=x+1; p->x[3]=x-1; p->y[0]=y; p->y[2]=y; p->y[3]=y+1; break; case L1_down: p->x[0]=x-1; p->x[2]=x; p->x[3]=x; p->y[0]=y-1; p->y[2]=y-1; p->y[3]=y+1; break; case L1_left: p->x[0]=x+1; p->x[2]=x-1; p->x[3]=x+1; p->y[0]=y-1; p->y[2]=y; p->y[3]=y; break; case L2_up: p->x[0]=x; p->x[2]=x; p->x[3]=x-1; p->y[0]=y-1; p->y[2]=y+1; p->y[3]=y+1; break; case L2_right: p->x[0]=x-1; p->x[2]=x-1; p->x[3]=x+1; p->y[0]=y-1; p->y[2]=y; p->y[3]=y; break; case L2_down: p->x[0]=x; p->x[2]=x+1; p->x[3]=x; p->y[0]=y-1; p->y[2]=y-1; p->y[3]=y+1; break; case L2_left: p->x[0]=x-1; p->x[2]=x+1; p->x[3]=x+1; p->y[0]=y; p->y[2]=y; p->y[3]=y+1; break; } } /* There are 7 different blocks, and when we create a new one ** to fall from the top, it can be any of these. */ void create_block(block *p) { switch(random(7)) { case 0: p->t=square; break; case 1: p->t=horizontal_long; break; case 2: p->t=stair1; break; case 3: p->t=stair3; break; case 4: p->t=T_up; break; case 5: p->t=L1_down; break; case 6: p->t=L2_down; break; } place_block(p, SCREEN_WIDTH/2,2); } /* delete_row() removes a full row from the game screen and ** drops the rows above. */ void delete_row(int row) { int i,j; for(j=row; j>1; j--) for(i=1; i<=SCREEN_WIDTH; i++) { stuck[i][j]=stuck[i][j-1]; gotoxy(i*2+SCREEN_POSITION,j); if(stuck[i][j]) { putch(BLOCK_CHARACTER); putch(BLOCK_CHARACTER); } else { putch(EMPTY_CHARACTER); putch(EMPTY_CHARACTER); } } points+=10; } /* freeze() locks the dropping block */ void freeze(block *p) { int i,j,full_row; for(i=0; ix[i]][p->y[i]]=1; for(j=1; j<=SCREEN_HEIGHT; j++) { full_row=1; for(i=1; i<=SCREEN_WIDTH; i++) if(!stuck[i][j]) full_row=0; if(full_row) delete_row(j); } } /* collision() checks if the dropping block is touching already ** existing blocks, aka has it bumped into something. */ void collision(block *p) { int i,touch=0; if(p->y[3]>SCREEN_HEIGHT) touch=1; else for(i=0; ix[i]<1 || p->x[i]>SCREEN_WIDTH || stuck[p->x[i]][p->y[i]]) touch=1; return touch; } void show(block *p, int char) { int i; for(i=0; ix[i]*2+SCREEN_POSITION,p->y[i]); putch(char); putch(char); } } /* a block is lowered by 1 row each round. ** In the same time, we check if it has ** bumped into anything, if so we freeze ** it. */ int lower_block(block *p) { int i; block temp_p=*p; show(p, EMPTY_CHARACTER); for(i=0; iy[i]++; if(collision(p)) { *p=temp_p; show(p, BLOCK_CHARACTER); freeze(p); if(p->y[0]<=2) exit(1); return 1; } show(p, BLOCK_CHARACTER); return 0; } /* control_block() checks ie. that a block could not be ** rotated if a part of it would go over the game ** screen. */ void control_block(block *p, char command) { int i; block temp_p=*p; show(p, EMPTY_CHARACTER); switch(command) { case RIGHT: for(i=0; ix[i]++; break; case LEFT: for(i=0; ix[i]--; break; case ROTATE: p->t=modification[p->t]; place_block(p, p->x[1], p->y[1]); break; case END: exit(1); } if(collision(p)) *p=temp_p; show(p, BLOCK_CHARACTER); } void main() { int i,j,touch,speed=STARTSPEED; randomize(); clrscr(); draw_borders(); for(j=0; j<=SCREEN_HEIGHT; j++) for(i=0; i<=SCREEN_WIDTH; i++) stuck[i][j]=0; while(1) { gotoxy(50,5); printf("SCORE: %5d", points); create_block(&p); i=0; touch=0; do { if(kbhit()) control_block(&p, getch()); if((i=(i+1)%speed)==0) touch=lower_block(&p); } while(!touch); points++; if(speed>1) speed--; } }