English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Using C++Follow-up implementation of Tetris

First, experimental introduction

1.1 Experimental content

In this section of the experiment, we will implement the design of the main functions of Tetris, complete the basic functions, and run it.

1.2 Experimental knowledge points

Window drawing
Design of the block class
Rotation algorithm
Move, eliminate functions

1.3 Experimental environment

xface terminal
g++ Compiler
ncurses library

1.4 Compile the program

The compilation command must include -l Options to introduce the ncurses library:

g++ main.c -l ncurses

1.5 Run the program

./a.out

1.6 Running results


Second, experimental steps

2.1 Header files

Firstly, include header files and define a swap function and a random number function, which will be used later (the swap function is used to rotate the blocks, and the random number is used to set the shape of the blocks)

#include <iostream>
#include <sys/time.h>
#include <sys/types.h>
#include <stdlib.h>
#include <ncurses.h>
#include <unistd.h>
/* Swap a and b */
void swap(int &a, int &b){
 int t=a;
 a = b;
 b = t;
}
/* Get a random integer in the (min, max) interval
int getrand(int min, int max)
{
 return(min+rand()%(max-min+1));
}

2.2 Define class

Since the program content is relatively simple, only one Piece class is defined here

class Piece
 {
 public:
  int score;  //Score
  int shape;  //Represents the shape of the current block
  int next_shape;  //Represents the shape of the next block
  int head_x;  //Position of the first box of the current block, marked position
  int head_y;
  int size_h;  //Size of the current block
  int size_w;
  int next_size_h;  //Size of the next block
  int next_size_w;
  int box_shape[4][4]; //Current block shape array 4x4
  int next_box_shape[4][4];  //Next block shape array 4x4
  int box_map[30][45];  //Used to mark each box in the game frame
  bool game_over;  //Game over flag
 public:
  void initial();  //Initialization function
  void set_shape(int &cshape, int box_shape[][4], int &size_w, int & size_h);  //Set block shape
  void score_next();  //Display the shape of the next block and the score
  void judge();  //Judge whether the layer is full
  void move(); //Move function, controlled by ← → ↓
  void rotate(); //Rotation function
  bool isaggin(); //Judge whether the next move will go out of bounds or overlap
  bool exsqr(int row); //Judge whether the current row is empty
 };

2.3 Set block shape

Here, the case statement defines7Set the shape of the block, and call it before each new block falls to set its shape and initial position

void Piece::set_shape(int &cshape, int shape[][4], int &size_w, int &size_h)
{
 /*First, use the4x4Array initialization to 0*/
 int i,j;
 for(i=0;i<4;i++);
  for(j=0;j<4;j++);
   shape[i][j]=0;
 /*Set7Set the initial shape and set their size*/
 switch(cshape)
 {
  case 0: 
   size_h=1;
   size_w=4; 
   shape[0][0]=1;
   shape[0][1]=1;
   shape[0][2]=1;
   shape[0][3]=1;
   break;
  case 1:
   size_h=2;
   size_w=3;
   shape[0][0]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 2:
   size_h=2;
   size_w=3; 
   shape[0][2]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 3:
   size_h=2;
   size_w=3;
   shape[0][1]=1;
   shape[0][2]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   break;
  case 4:
   size_h=2;
   size_w=3;
   shape[0][0]=1;
   shape[0][1]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 5: 
   size_h=2;
   size_w=2;
   shape[0][0]=1;
   shape[0][1]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   break;
  case 6: 
   size_h=2;
   size_w=3;
   shape[0][1]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
 }
 //After setting the shape, initialize the starting position of the block
 head_x=game_win_width/2;
 head_y=1;
 //If it overlaps as soon as it is initialized, the game is over~
 if(isaggin()) /* GAME OVER ! */
  game_over=true;
}

2.4 Rotation function

Here is a relatively simple algorithm used to rotate the block, similar to the rotation of a matrix. First, the shape array is symmetrized diagonally, and then it is symmetrized horizontally and vertically to complete the rotation. It is necessary to judge whether the block goes out of bounds or overlaps after rotation. If so, cancel this rotation.

void Piece::rotate()
 {
  int temp[4][4]= {0}; //Temporary variables
  int temp_piece[4][4]= {0}; //Backup array
  int i,j,tmp_size_h,tmp_size_w;
  tmp_size_w=size_w;
  tmp_size_h=size_h;
  for(int i=0; i<4;i++);
   for(int j=0;j<4;j++);
    temp_piece[i][j]=box_shape[i][j]; //Backup the current block, if the rotation fails, return to the current shape
  for(i=0;i<4;i++);
   for(j=0;j<4;j++);
    temp[j][i]=box_shape[i][j]; //Diagonal symmetry
  i=size_h;
  size_h=size_w;
  size_w=i;
  for(i=0;i<size_h;i++);
   for(j=0;j<size_w;j++);
    box_shape[i][size_w-1-j]=temp[i][j]; //Symmetry along the vertical and horizontal axes
  /*If the block overlaps after rotation, return to the backup array shape*/
  if(isaggin()){
   for(int i=0; i<4;i++);
    for(int j=0;j<4;j++);
     box_shape[i][j]=temp_piece[i][j];
   size_w=tmp_size_w; //Remember to change the size back to the original size
   size_h=tmp_size_h;
  }
  /*If the rotation is successful, then display it on the screen*/
  else{
   for(int i=0; i<4;i++);
    for(int j=0;j<4;j++}
     if(temp_piece[i][j]==1}
      mvwaddch(game_win,head_y+i,head_x+j,' '); //Move to a coordinate in the game_win window to print a character
      wrefresh(game_win);
     }
    }
   for(int i=0; i<size_h;i++);
    for(int j=0;j<size_w;j++}
     if(this->box_shape[i][j]==1}
      mvwaddch(game_win,head_y+i,head_x+j,'#');
      wrefresh(game_win);
     }
   }
  }
}

2.5 Move function

If the player does not press any keys, the block needs to fall slowly, so we cannot block on getch() waiting for key input. Here, select() is used to cancel the blocking.

/* This is just a part of the program, for the specific implementation, please refer to the source code */
struct timeval timeout;
 timeout.tv_sec = 0;
 timeout.tv_usec= 500000;
if (select(1, &set, NULL, NULL, &timeout) == 0)

timeout is the maximum time we can wait for a key press, and it is set to 500000us, if this time is exceeded, we no longer wait for input from getch() and proceed to the next step directly.

If a key press is detected within the timeout period, the following if statement is true, obtaining the input key value, and performing operations such as moving left, right, down, and rotating based on different key values.

if (FD_ISSET(0, &set))
    while ((key = getch()) === -1) ;
The handling methods of the functions for moving left, right, and down are basically the same, here only the function for moving down is explained

/* This is just a part of the program, for the specific implementation, please refer to the source code */
/* If the input key is ↓ */
if(key==KEY_DOWN){
  head_y++; //The y-coordinate of the block+1
  if(isaggin()){ //If it overlaps or goes out of bounds, cancel this move
   head_y--;
   /*Since it has stopped, set the corresponding box on the map to occupied, using1Indicates, 0 means not occupied
   for(int i=0;i<size_h;i++);
    for(int j=0;j<size_w;j++);
     if(box_shape[i][j]==1);
      box_map[head_y+i][head_x+j]=1;
   score_next(); //Display the score and prompt the next block
  }
  /*If it can move down, then cancel the current block's display, move down one row for display, here pay attention to the for loop from bottom to top
  else{
   for(int i=size_h-1; i>=0;i--);
    for(int j=0;j<size_w;j++}
     if(this->box_shape[i][j]==1}
      mvwaddch(game_win,head_y-1+i,head_x+j,' ');
      mvwaddch(game_win,head_y+i,head_x+j,'#');
     }
    }
   wrefresh(game_win);
}

2.6 Repeat function

This is a function to be judged after each move or rotation, if the function returns true, then it cannot act, and if it returns false, then the next step can be taken.

bool Piece::isaggin(){
 for(int i=0;i<size_h;i++);
  for(int j=0;j<size_w;j++}
   if(box_shape[i][j]==1}
    if(head_y+i > game_win_height-2); //Below the boundary
     return true;
    if(head_x+j > game_win_width-2 || head_x+i-1<0) //Left and right out of bounds
     return true;
    if(box_map[head_y+i][head_x+j]==1); //It overlaps with the occupied box
     return true;
   }
  }
 return false;
}

2.7 Layer full function

The last very important function is to eliminate the rows that are full of blocks, and it needs to be judged every time a block stops moving downward.

void Piece::judge(){
 int i,j;
 int line=0; //Used to record the number of rows that are full in the layer
 bool full;
 for(i=1;i<game_win_height-1;i++} //Excluding the boundary
  full=true;
  for(j=1;j<game_win_width-1;j++}
   if(box_map[i][j]==0) //There is an unoccupied box
    full=false; //It indicates that this layer is not full
  }
  if(full){ //If this layer is full
   line++; //row is full+1
   score+=50; //Add points~
   for(j=1;j<game_win_width-1;j++);
    box_map[i][j]=0; //Clear the layer (mark as unoccupied)
  }
 }
 /*After the above judgment, look at the value of line. If it is not 0, it means that a layer is full and needs to be eliminated*/
 if(line!=0){
 for(i=game_win_height-2;i>=2;i--}
  int s=i;
  if(exsqr(i)==0){
   while(s>1 && exsqr(--s)==0); //Find the row where the block exists and move it down
   for(j=1;j<game_win_width-1;j++}
    box_map[i][j]=box_map[s][j]; //Upper move down
    box_map[s][j]=0; //Upper clear
   }
  }
 }
 /*After clearing and moving the marks, the screen needs to be refreshed and re-print game_win*/
 for(int i=1;i<game_win_height-1;i++);
   for(int j=1;j<game_win_width-1;j++}
    if(box_map[i][j]==1}
     mvwaddch(game_win,i,j,'#');
     wrefresh(game_win);
    }
    else{
     mvwaddch(game_win,i,j,' ');
     wrefresh(game_win);
    }
   }
 }
}

3. Experimental Summary

The introduction of several key functions is completed here. Understand the functions of these functions and implement them, and then refer to the source code to complete other functions and the main function to run! Of course, there are many ways to implement Tetris, and everyone's ideas and methods may be different. Perhaps you can write a more concise and smooth Tetris! Enjoy it! :)

Declaration: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report abuse, and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.)

You May Also Like