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.