C++ Tetris (linux version)

  • 2020-10-23 21:08:23
  • 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 main program

RussiaBlock.cpp


//
// Created by adl on 2020/7/18.
//
#include "Block.h"
#include "Table.h"
#include <thread>
#include <mutex>
#include "hierarchical_mutex.h"
#include "fstream"

using namespace std;
thread_local uint64_t
  hierarchical_mutex::this_thread_hierarchical_value = ULONG_MAX;

int main(int argc, char **argv) {
 int level = 1;
 if (argc == 2) {
  if ((level = atoi(argv[1])) == 0) {
   cerr << "./a.out number " << endl;
   exit(-1);
  }

 }
 static int flag = 1;// The global variable 
 static Table tab(20, 20, level); // structure 1 a 15,20 The board of 
 static Block bl;  // structure 1 A falling square 
 hierarchical_mutex table_mtx(2);
 hierarchical_mutex mtx(1);


 thread getkey([&]() {
  unsigned char buf[2];
  struct termios saveterm, nt;
  fd_set rfds, rs;
  struct timeval tv;
  int i = 0, q, r, fd = 0;// The standard input 
  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_usec = 0;
  tv.tv_sec = 0;
  while (1) {
   read(0, buf, 1);
   buf[1] = '\0';
   r = select(fd + 1, &rfds, nullptr, nullptr, &tv);
   if (r < 0) {
    write(fileno(stderr), "select error.\n", sizeof("select error.\n"));
   }
   rfds = rs;
   std::unique_lock<hierarchical_mutex> table_lock(table_mtx);
   // The up and down or so 
   switch (buf[0]) {
    case 'A': {
     // rotating 
     tab.clr_block(bl);//
     if (bl.get_type() == 5)continue;
     bl.rotate();
     if (tab.set_block(bl) == -1) {
      bl.rotate_back();
      tab.set_block(bl);
      continue;
     }
     break;
    }
    case 'B': {
     // Downward (accelerated) 
     tab.clr_block(bl);
     bl.move(Block::DOWN);
     if (tab.set_block(bl) == -1) {
      bl.move(Block::UP);
      tab.set_block(bl);
     }
     break;
    }
    case 'C': {
     /* To the right */
     tab.clr_block(bl);
     bl.move(Block::RIGHT);
     if (tab.set_block(bl) == -1) {
      bl.move(Block::LEFT);
      tab.set_block(bl);
     }
     break;
    }
    case 'D': {
     // On the left 
     tab.clr_block(bl);
     bl.move(Block::LEFT);
     if (tab.set_block(bl) == -1) {
      bl.move(Block::RIGHT);
      tab.set_block(bl);
     }
     break;
    }
    default:
     break;
   }
   table_lock.unlock();
   std::unique_lock<hierarchical_mutex> lock(mtx);
   if (flag == 2 || buf[0] == 113) {

    lock.unlock();

    tcsetattr(fd, TCSANOW, &saveterm);
    std::cout << "game over" << std::endl;
    exit(0);
   } else {
    lock.unlock();
   }
  }

  tcsetattr(0, TCSANOW, &saveterm);
 });
 thread printloop([&]() {
  while (1) {
   system("clear");
   std::unique_lock<hierarchical_mutex> table_lock(table_mtx);
   tab.paint();
   table_lock.unlock();
   this_thread::sleep_for(std::chrono::milliseconds(200 / tab.getLevel()));
   std::unique_lock<hierarchical_mutex> lock(mtx);

   if (flag == 2) {
    cout << " Any key exit " << endl;
    lock.unlock();
    break;
   } else
    lock.unlock();
  }
 });
 getkey.detach();
 printloop.detach();
 int dir, i, c;
 while (true) {
  // Generate square 
  std::unique_lock<hierarchical_mutex> table_lock(table_mtx);
//  std::unique_lock<std::mutex>table_lock(table_mtx);

  bl.create_block(tab.getWidth(), tab.getHeight());
  table_lock.unlock();
  // Decide if the game is over 
  table_lock.lock();
  if (-1 == tab.set_block(bl)) {
   std::unique_lock<hierarchical_mutex> lock(mtx);
   flag = 2;
   lock.unlock();
   table_lock.unlock();
   while (1);
  } else
   table_lock.unlock();

  /////////// Action button decision 
  while (true) {
   this_thread::sleep_for(std::chrono::milliseconds(400 / tab.getLevel()));
   ///////////// Move down the 1 " 
   table_lock.lock();
   tab.clr_block(bl); // On the empty 1 Subsquare position 
   bl.move(Block::DOWN); // Move down the 1 step 
   if (-1 == tab.set_block(bl)) { // Whether the bottom of the 
    bl.move(Block::UP); // If it hits bottom, restore the position before it hit bottom 
    tab.set_block(bl);
    table_lock.unlock();
    break;
   }
   table_lock.unlock();
  }
  // If the line is full, the line goes away 
  table_lock.lock();
  for (i = 0; i < tab.getHeight(); 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 
   }
  }
  table_lock.unlock();
 }
 return 0;
}

grid.h


//
// Created by adl on 2020/7/17.
//

#ifndef UNTITLED_GRID_H
#define UNTITLED_GRID_H

struct grid {
 int x;
 int y;

 grid();
 grid(grid&&)noexcept ;
 grid(const grid&);
 grid(int x, int y);
 grid&operator=(const grid&);
 grid&operator=( grid&&);
 virtual ~grid();
}; // coordinates 



#endif //UNTITLED_GRID_H

grid.cpp


//
// Created by adl on 2020/7/17.
//

#include "grid.h"

grid::grid(int x, int y) : x(x), y(y) {}

grid::grid() : x(0), y(0) {}

grid::grid(grid &&rhs) noexcept: x(rhs.x), y(rhs.y) {

}

grid::~grid() {

}

grid::grid(const grid &rhs) : x(rhs.x), y(rhs.y) {
}

grid &grid::operator=(const grid &rhs) {
 if (this != &rhs) {
  x = rhs.x;
  y = rhs.y;
 }
 return *this;

}

grid &grid::operator=(grid &&rhs) {
 if (this != &rhs) {
  x = rhs.x;
  y = rhs.y;
 }
 return *this;
}

Block.h


//
// Created by adl on 2020/7/17.
//

#ifndef UNTITLED_BLOCK_H
#define UNTITLED_BLOCK_H

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <time.h>

#include<termios.h>
#include<fcntl.h>
#include <zconf.h>
#include "grid.h"

#define BLOCK_SIZE 4
#define SLEEP_TIME 500

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<memory>
#include <random>

class Block {
public:
 using Action =Block&(Block::*)();
 enum direct {
  UP, DOWN, LEFT, RIGHT
 };
 grid g[BLOCK_SIZE];

 Block() : center(0, 0), type(0) {}

 void def_block(grid g1, grid g2, grid g3, grid g4) {
  g[0] = g1;
  g[1] = g2;
  g[2] = g3;
  g[3] = g4;
 }

 void rotate() {
  // Clockwise rotation 
  int x, y;
  for (int i = 0; i < 4; 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;

  }
 }

 Block &up() {
  for (int i = 0; i < 4; ++i) {
   g[i].y++;
  }
  center.y++;
  return *this;
 }

 Block &down() {
  for (int i = 0; i < 4; ++i) {
   g[i].y--;
  }
  center.y--;
  return *this;
 }

 Block &left() {
  for (int i = 0; i < 4; ++i) {
   g[i].x--;
  }
  center.x--;
  return *this;
 }

 Block &right() {
  for (int i = 0; i < 4; ++i) {
   g[i].x++;
  }
  center.x++;
  return *this;
 }

 void move(direct dir) {
  (this->*Menu[dir])();
 }


 void set_cen(grid g) {
  center = g;
 }

 grid get_cen() const {
  return center;
 }

 void set_type(int t) {
  type = t;
 }

 int get_type() const {
  return type;
 }

 void rotate_back() {
  //rotate The reverse 
  int x, y;
  for (int i = 0; i < 4; 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 create_block(int x, int y) {
  unsigned int ran;
  grid g[BLOCK_SIZE];
  static std::uniform_int_distribution<unsigned> u(1, 7);
  static std::default_random_engine e(time(0));
  ran = u(e);
  switch (ran) {
   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:
    std::cerr << "someThing err!" << ran << std::endl;
  }
  def_block(g[0], g[1], g[2], g[3]);
 }

private:
 static Action Menu[];
 grid center;
 int type;
};

#endif //UNTITLED_BLOCK_H

Block.cpp


//
// Created by adl on 2020/7/17.
//

#include "Block.h"
Block::Action Block::Menu[]={
  &Block::up,
  &Block::down,
  &Block::left,
  &Block::right
};

Table.cpp


//
// Created by adl on 2020/7/17.
//

#include "Table.h"
#include "Block.h"

int Table::set_block(const Block &bl) {
 int x, y;
 for (int i = 0; i < 4; ++i) {
  x = bl.g[i].x;
  y = bl.g[i].y;
  // Let's say after we go down  table[x][y] There are squares on it 
  if (table[x][y] != 0 || x >= width || x < 0 || y >= height || y < 0) {
   return -1;
  }
 }
 for (int i = 0; i < 4; ++i) {
  x = bl.g[i].x;
  y = bl.g[i].y;
  table[x][y] = 1;
 }
 return 0;
}

void Table::clr_block(const Block &bl) {
 int x, y;
 for (int i = 0; i < 4; ++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 -1;
 for (int i = 0; i < width; i++) {
  table[i][y] = 0;
 }
 return 0;
}

int Table::getHeight() const {
 return height;
}

int Table::getWidth() const {
 return width;
}

int Table::if_full(int y) {
 for (int i = 0; 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;
 system("clear");
 for (i = 0; i < width + 2; i++) std::cout << "-" << std::flush;
 std::cout << "\n" << std::flush;

 for (i = height - 1; i >= 0; i--) {
  std::cout << "|" << std::flush;
  for (j = 0; j < width; j++) {
   if (table[j][i] == 0) std::cout << " " << std::flush;
   else std::cout << "#" << std::flush;
   //▣
  }
  if (i == 13)
   std::cout << "|  Rating: " << getLevel() << std::endl;
  else if (i == 10)
   std::cout << "|  Score: " << get_count() << std::endl;
  else if (i == 7)
   std::cout << "| Press 'q' to quit!" << std::endl;
  else
   std::cout << "|" << std::endl;
 }
 for (i = 0; i < width + 2; i++) std::cout << "-" << std::flush;
 std::cout << "\n" << std::flush;
}

void Table::move_line(int y) {
 for (int i = y; i < height - 1; ++i) {
  for (int 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;
}

int Table::getLevel() const {
 return level;
}

void Table::setLevel(int level) {
 Table::level = level;
}

Table.h


//
// Created by adl on 2020/7/17.
//

#ifndef UNTITLED_TABLE_H
#define UNTITLED_TABLE_H

#include <cstring>

#define TABLE_SIZE 20
class Block;
class Table {
public:

 Table():height(TABLE_SIZE),width(10),count(0),level(1){    // To construct a board 
  for (int i = 0; i < height; ++i) {
   for (int j = 0; j < width; ++j) {
    table[i][j]=0;
   }
  }
 }

 int getLevel() const;

 void setLevel(int level);

 Table(int x, int y,int level):height(y),width(x),count(0),level(level){
  for (int i = 0; i < height; ++i) {
   for (int j = 0; j < width; ++j) {
    table[i][j]=0;
   }
  }
 }
 int set_block(const Block &bl); // Put a square 
 void clr_block(const Block &bl);  // Remove squares 
 int clr_line(int y);  // Line elimination 

 int getHeight() const;

 // 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();

 int getWidth() const;
 // Get score 

private:
 int table[TABLE_SIZE][TABLE_SIZE];// The board 
 int height, width;  // The height and width of the chessboard 
 int count;   // score 
 int level;

};

#endif //UNTITLED_TABLE_H

hierarchical_mutex.h


//
// Created by adl on 2020/7/18.
//

#ifndef UNTITLED_HIERARCHICAL_MUTEX_H
#define UNTITLED_HIERARCHICAL_MUTEX_H
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<memory>
#include <exception>
#include <mutex>
#include <thread>
#include <climits>

class hierarchical_mutex{
private:
 std::mutex internal_mutex;
 uint64_t const hierarchical_value;
 uint64_t previous_value;
 static thread_local uint64_t this_thread_hierarchical_value;

 void check_for_hierarchy() noexcept(false) {
  if(this_thread_hierarchical_value <= hierarchical_value){
   throw std::logic_error("mutex hierarchical violated.");
  }
 }

 void update_hierarchy_value(){
  previous_value = this_thread_hierarchical_value;
  this_thread_hierarchical_value = hierarchical_value;
 }

public:
 constexpr explicit hierarchical_mutex(uint64_t value) :
   hierarchical_value(value), previous_value(0) {}

 void lock() noexcept(false) {
  check_for_hierarchy();
  internal_mutex.lock();
  update_hierarchy_value();
 }

 void unlock(){
  this_thread_hierarchical_value = previous_value;
  internal_mutex.unlock();
 }

 bool try_lock() noexcept(false) {
  check_for_hierarchy();
  if(!internal_mutex.try_lock()) return false;
  update_hierarchy_value();
  return true;
 }
};


#endif //UNTITLED_HIERARCHICAL_MUTEX_H

Accumulated experience:

1. When using uniform_int_distribution, defualt_random_engine(time(0)) to generate random Numbers, static must be used. The disadvantage is that the number generated by the first execution of the program for multiple times is 1 kind

2. Like c++primer p743, using a member pointer function table makes it clearer what functions the user calls.

3. Weight the mutex and lock the threads in order of weight to ensure that the threads are locked in order 1 and avoid deadlocks (exceptions are thrown if they occur) (this is a pure learning exercise, as c++ locks do not touch the threads much).

4.thread xxx([ & ](){}) is a thread that can be placed inside a function

5. Use select to monitor standard input

6. tcgetattr,tcsetaddr and termiosi structures were used
nt.c_lflag & = ~ECHO; nt.c_lflag & = ~ISIG; nt.c_lflag & = ~ICANON;
Echo can be turned off in linux to implement getch

7.this_thread::sleep_for(std::chrono::milliseconds(400 / tab.getLevel())); You can achieve millisecond level sleep in threads

Class static object initialization can be written in its corresponding.cpp file

Reflection:

Inheritance of unused classes, various cube theories can be written as subclasses, since primary thread 1 USES Block objects directly from the beginning, which is not easy to modify later.


Related articles: