linux environment C++ to achieve Tetris

  • 2020-06-07 04:52:17
  • OfStack

Example of this article for you to share C++ to achieve the specific code of Tetris, for your reference, the specific content is as follows

The running environment of this program is linux, using multithreading. A thread was created for drawing and a thread for getting keys. There are 1 areas in the program that need to be improved, such as the global variables defined in lines 336-338 and the objects declared. The declared Block and Table objects should be inside the main function and then passed as arguments to the thread function getkey. But it seems that only one object argument can be passed to the thread function. Hope the master can improve the program.

ps: Since multithreading is used, and pthread is not the default library for linux, you need to specify the thread library at compile time. g++ -o ES16en-ES17en block.cpp


#include <iostream> 
#include <cstdlib> 
#include <pthread.h> 
#include <time.h> 
 
#include<termios.h> 
#include<fcntl.h> 
 
 
#define TABLE_SIZE 20 
#define BLOCK_SIZE 4 
#define SLEEP_TIME 500 
 
using namespace std; 
 
struct grid{int x; int y;};    // coordinates  
 
/////////////////////Block  class ////////////////////// 
class Block 
{ 
public: 
  enum direct{UP, DOWN, LEFT, RIGHT};         // Define the direction  
  grid g[BLOCK_SIZE];                 // The coordinates of the square  
 
  void def_block(grid g1, grid g2, grid g3, grid g4); // The definition of square  
  void rotate();                   // gururin  
  void move(int dir);                 // Mobile square  
  void set_cen(grid g);                // Set the center of the square rotation  
  grid get_cen();                   // Gets the center of the square rotation  
  void set_type(int t);                // Set the square type  
  int get_type();                   // Get the block type  
  void back();                    // Rotating reduction  
  void creat_block(int x, int y);           // Randomly generated block  
 
private: 
  grid center;                    // Center of square rotation  
  int type;                      // Square type  
   
 
}; 
 
void Block::def_block(grid g1, grid g2, grid g3, grid g4) { 
  g[0]=g1; g[1]=g2; g[2]=g3; g[3]=g4; 
} 
 
void Block::rotate() { 
  int x, y, i=0; 
 
  for(i; i<=3; i++) { 
    x=g[i].x-center.x; y=g[i].y-center.y; 
    g[i].x=center.x+y; g[i].y=center.y-x; 
  } 
} 
 
void Block::move(int dir) { 
  int d=dir, i=0; 
 
  switch(d) { 
  case UP: {  
    for(i; i<=3; i++) g[i].y++; 
    center.y++; break; 
       } 
  case DOWN: { 
    for(i; i<=3; i++) g[i].y--; 
    center.y--; break; 
        } 
  case LEFT: { 
    for(i; i<=3; i++) g[i].x--; 
    center.x--; break; 
        } 
  case RIGHT: { 
    for(i; i<=3; i++) g[i].x++; 
    center.x++; break; 
        } 
  } 
} 
 
void Block::set_cen(grid g) { 
  center=g; 
} 
 
grid Block::get_cen() { 
  return center; 
} 
 
void Block::set_type(int t) { 
  type=t; 
} 
 
int Block::get_type() { 
  return type; 
} 
 
void Block::back() { 
  int x, y, i=0; 
 
  for(i; i<=3; i++) { 
    x=g[i].x-center.x; y=g[i].y-center.y; 
    g[i].x=center.x-y; g[i].y=center.y+x; 
  } 
} 
 
void Block::creat_block(int x, int y) {  // Randomly create squares  
  int ran; 
  grid g[BLOCK_SIZE]; 
 
   
  ran=1+rand()%7; 
  switch(ran) { 
  //L 
  case 1: { 
    g[0].x=x/2; g[0].y=y-3; 
    g[1].x=g[0].x; g[1].y=g[0].y+1; 
    g[2].x=g[0].x; g[2].y=g[0].y+2; 
    g[3].x=g[0].x+1; g[3].y=g[0].y;  
    set_cen(g[0]); set_type(1); break; 
      } 
  // the L 
  case 2: { 
    g[0].x=x/2; g[0].y=y-3; 
    g[1].x=g[0].x; g[1].y=g[0].y+1; 
    g[2].x=g[0].x; g[2].y=g[0].y+2; 
    g[3].x=g[0].x-1; g[3].y=g[0].y;  
    set_cen(g[0]); set_type(2); break; 
      } 
  //Z 
  case 3: { 
    g[0].x=x/2; g[0].y=y-2; 
    g[1].x=g[0].x; g[1].y=g[0].y+1; 
    g[2].x=g[0].x+1; g[2].y=g[0].y+1; 
    g[3].x=g[0].x-1; g[3].y=g[0].y;  
    set_cen(g[0]); set_type(3); break; 
      } 
  // the Z 
  case 4: { 
    g[0].x=x/2; g[0].y=y-2; 
    g[1].x=g[0].x; g[1].y=g[0].y+1; 
    g[2].x=g[0].x+1; g[2].y=g[0].y+1; 
    g[3].x=g[0].x-1; g[3].y=g[0].y;  
    set_cen(g[0]); set_type(4); break; 
      } 
  // tian  
  case 5: { 
    g[0].x=x/2; g[0].y=y-2; 
    g[1].x=g[0].x; g[1].y=g[0].y+1; 
    g[2].x=g[0].x+1; g[2].y=g[0].y+1; 
    g[3].x=g[0].x+1; g[3].y=g[0].y;  
    set_cen(g[0]); set_type(5); break; 
      } 
  //1 
  case 6: { 
    g[0].x=x/2; g[0].y=y-3; 
    g[1].x=g[0].x; g[1].y=g[0].y+1; 
    g[2].x=g[0].x; g[2].y=g[0].y+2; 
    g[3].x=g[0].x; g[3].y=g[0].y-1;  
    set_cen(g[0]); set_type(6); break; 
      } 
  // mountain  
  case 7: { 
    g[0].x=x/2; g[0].y=y-2; 
    g[1].x=g[0].x; g[1].y=g[0].y+1; 
    g[2].x=g[0].x-1; g[2].y=g[0].y; 
    g[3].x=g[0].x+1; g[3].y=g[0].y;  
    set_cen(g[0]); set_type(7); break; 
      } 
  default: ; 
  } 
  def_block(g[0], g[1], g[2], g[3]); 
} 
 
 
///////////////////////////////////////// 
 
////////////////////Table  class ////////////////////// 
class Table 
{ 
public: 
   
  Table() {             // To construct a board  
    height=20; width=10; count=0; 
    init_table(); 
  } 
  Table(int x, int y); 
  int set_block(Block bl);     // Put a square  
  void clr_block(Block bl);     // Remove squares  
  int clr_line(int y);       // Line elimination  
  int get_h();           // Get the checkerboard height  
  int get_w();           // Get the checkerboard width  
  int if_full(int y);        // Determines whether the row is full  
  int get_table(int x, int y);   // Gets points on the checkerboard  
  void paint();           // Drawing board  
  void move_line(int y);      // The entire line down  
  void set_count(int c);      // Record the score  
  int get_count();         // Get score  
 
private: 
  int table[TABLE_SIZE][TABLE_SIZE];// The board  
  int height, width;        // The height and width of the board  
  int count;            // score  
 
  void init_table();        // Checkerboard initialization  
 
}; 
 
void Table::init_table() { 
  int i=0, j=0; 
 
  for(i; i<width; i++) { 
    for(j=0; j<height; j++) { 
      table[i][j]=0; 
    } 
  } 
} 
 
Table::Table(int x, int y) { 
  height=y; width=x; count=0; 
  init_table(); 
} 
 
int Table::set_block(Block bl) { 
  int x, y; 
  int i; 
  for(i=0; i<=3; i++) { 
    x=bl.g[i].x; y=bl.g[i].y; 
    if(table[x][y]!=0 || x>=width || x<0 || y>=height || y<0) { 
      return 0; 
    } 
  } 
  for(i=0; i<=3; i++) { 
    x=bl.g[i].x; y=bl.g[i].y; 
    table[x][y]=1; 
  } 
  return 1; 
} 
 
void Table::clr_block(Block bl) { 
  int x, y; 
 
  for(int i=0; i<=3; i++) { 
    x=bl.g[i].x; y=bl.g[i].y; 
    table[x][y]=0; 
  } 
} 
 
int Table::clr_line(int y) { 
  if(y<0 || y>=height) return 0; 
  for(int i=0; i<width; i++) { 
    table[i][y]=0; 
  } 
  return 1; 
} 
 
int Table::get_h() { 
  return height; 
} 
 
int Table::get_w() { 
  return width; 
} 
 
int Table::if_full(int y) { 
  int i=0; 
 
  for(i; i<width; i++) { 
    if(table[i][y]==0) return 0; 
  } 
  return 1; 
} 
 
int Table::get_table(int x, int y) { 
  return table[x][y]; 
} 
 
void Table::paint() { 
  int i, j; 
 
  for(i=0; i<width+2; i++) cout<<"-"<<flush; 
  cout<<"\n"<<flush; 
  for(i=height-1; i>=0; i--) { 
    cout<<"|"<<flush; 
    for(j=0; j<width; j++) { 
      if(table[j][i]==0) cout<<" "<<flush; 
      else cout<<"▣"<<flush; 
    } 
    if(i==10) 
      cout<<"|   Score: "<<get_count()<<endl; 
    else if(i==7) 
      cout<<"|  Press 'q' to quit!"<<endl; 
    else 
      cout<<"|"<<endl; 
  } 
  for(i=0; i<width+2; i++) cout<<"-"<<flush; 
  cout<<"\n"<<flush; 
  //cout<<" Score: "<<get_count()<<endl; 
} 
 
void Table::move_line(int y) { 
  int i, j; 
 
  for(i=y; i<height-1; i++) { 
    for(j=0; j<width; j++) { 
      table[j][i]=table[j][i+1]; 
    } 
  } 
} 
 
void Table::set_count(int c) { 
  count+=c; 
} 
 
int Table::get_count() { 
  return count; 
} 
 
/////////////////////////////////////////////////////// 
class Mythread 
{ 
public: 
  void init(); 
  static void *getkey(void *arg);// Thread functions must be defined in a class static Type to remove the class pointer.  
  static void *paint_loop(void *arg); 
}; 
 
void Mythread::init() 
{ 
  pthread_t ntid,ntid2; 
  int err,err2;     
  err = pthread_create(&ntid,NULL,getkey,NULL); 
  err2 = pthread_create(&ntid2,NULL,paint_loop,NULL); 
  if(err != 0 || err2 != 0){ 
    cout<<"can't create thread!"<<endl; 
    exit(0); 
  } 
} 
 
unsigned char flag=1,buf[2];// The global variable  
Table tab(15, 20); // structure 1 a 15,20 The board of  
Block bl;      // structure 1 A falling square  
void* Mythread::paint_loop(void *arg) 
{ 
  while(1) 
  { 
    system("clear"); 
    tab.paint(); 
    usleep(50000);    // suspended 50 MS 
  } 
} 
void* Mythread::getkey(void *arg) 
{ 
  struct termios saveterm,nt; 
  fd_set rfds,rs; 
  struct timeval tv; 
  int i=0,q,r,fd=0; 
  tcgetattr(fd,&saveterm); 
  nt=saveterm; 
 
  nt.c_lflag &= ~ECHO; 
  nt.c_lflag &= ~ISIG; 
  nt.c_lflag &= ~ICANON; 
 
  tcsetattr(fd,TCSANOW,&nt); 
 
  FD_ZERO(&rs); 
  FD_SET(fd,&rs); 
  tv.tv_sec=0; 
  tv.tv_usec=0; 
  while(1) 
  {   
    read(0,buf,1); 
    r=select(fd+1,&rfds,NULL,NULL,&tv); 
    if(r<0) 
    { 
      write(1,"select() error.\n",16); 
    } 
    rfds=rs; 
    if(flag==2||buf[0]==113)// The game ends or the user presses 'q' Key, the program exit  
    { 
      tcsetattr(0,TCSANOW,&saveterm); 
      exit(0); 
    } 
    if(buf[0]<=68&&buf[0]>=65) flag=0;// If the key pressed is an arrow key, the position is marked 0 And the corresponding processing is performed . 
    if(flag==0) 
    { 
      if(buf[0]==65) { 
      //if(dir!=0) { 
        if(bl.get_type()==5) continue; // If the field shape appears, it will not be rotated  
        tab.clr_block(bl);      // Clear the square 1 Time position  
        bl.rotate();         // Begin to spin  
        if(!tab.set_block(bl)) {   // Write the rotated squares on the board  
          bl.back();       // If you fail to write ( For example, it's on the edge, or it's stuck ) Then the position before rotation is restored  
          continue; 
          tab.set_block(bl);     
        } 
      } 
      // Down (accelerated fall)  
      //dir=GetAsyncKeyState(VK_DOWN);  // To get down  
      if(buf[0]==66) { 
        tab.clr_block(bl);     // Clear the square 1 Time position  
        bl.move(bl.DOWN);      // Move down the 1 step  
        if(!tab.set_block(bl)) {  // Write the moved square on the board  
          bl.move(bl.UP);     // If this fails, revert to the position before the move (that is, move up 1 Step)  
          tab.set_block(bl); 
        } 
      } 
      // Left (left shift)  
      //dir=GetAsyncKeyState(VK_LEFT); 
      if(buf[0]==68) { 
        tab.clr_block(bl); 
        bl.move(bl.LEFT); 
        if(!tab.set_block(bl)) { 
          bl.move(bl.RIGHT); 
          tab.set_block(bl); 
        } 
      } 
      // Right (right shift)  
      //dir=GetAsyncKeyState(VK_RIGHT); 
      if(buf[0]==67) { 
        tab.clr_block(bl); 
        bl.move(bl.RIGHT); 
        if(!tab.set_block(bl)) { 
          bl.move(bl.LEFT); 
          tab.set_block(bl); 
        } 
      } 
      flag=1; 
    } 
  } 
  tcsetattr(0,TCSANOW,&saveterm); 
} 
 
//////////// Main function part /////////////////////// 
 
int main() 
{ 
  //Table tab(15, 20); // structure 1 a 15,20 The board of  
  //Block bl;      // structure 1 A falling square  
  Mythread thread; 
  thread.init(); 
  int dir,i,c; 
  while(true) { 
    // Generate square  
    srand(time(0)); 
    bl.creat_block(tab.get_w(), tab.get_h()); 
    // Decide if the game is over  
    if( !tab.set_block(bl) ) { 
      system("clear"); 
      cout<<"GAME OVER!"<<endl; 
      flag=2; 
      cout<<"PRESS ANY KEY TO CONTINUE!"<<endl; 
      while(1); 
    } 
    /////////// Action button decision  
    while(true){ 
      usleep(500000);    // suspended 500 MS 
      ///////////// Move down the 1 "  
      tab.clr_block(bl);    // On the empty 1 Subsquare position  
      bl.move(bl.DOWN);    // Move down the 1 step  
      if(!tab.set_block(bl)) {   // Whether the bottom of the  
        bl.move(bl.UP);    // If it hits bottom, restore the position before it hit bottom  
        tab.set_block(bl); 
        break; 
      } 
    } 
    // If the line is full, the line goes away  
    for(i=0; i<tab.get_h(); i++) { 
      if(tab.if_full(i)) { // Whether the full line  
        tab.clr_line(i); // If yes, cancel the line  
        tab.move_line(i); // Move the checkerboard information above the eliminated lines down  
        i--;      // As you move down, recheck this 1 Is the row full (there may be several lines that cancel at the same time)  
        tab.set_count(100); // Record the score  
      } 
    } 
     
  } 
  return 0; 
} 

Related articles: