C++实现俄罗斯方块(linux版本)

时间:2022-04-04 06:26:14

本文实例为大家分享了C++实现俄罗斯方块的具体代码,供大家参考,具体内容如下

主程序

RussiaBlock.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//
// 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;//全局变量
 static Table tab(20, 20, level); //构造一个15,20的棋盘
 static Block bl;  //构造一个落下方块
 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;//标准输入
  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);
   //上下左右
   switch (buf[0]) {
    case 'A': {
     //旋转
     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': {
     //向下(加速)
     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': {
     /*向右*/
     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': {
     //左
     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 << "任意键退出" << endl;
    lock.unlock();
    break;
   } else
    lock.unlock();
  }
 });
 getkey.detach();
 printloop.detach();
 int dir, i, c;
 while (true) {
  //生成方块
  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();
  //判断游戏是否结束
  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();
 
  ///////////行动按键判定
  while (true) {
   this_thread::sleep_for(std::chrono::milliseconds(400 / tab.getLevel()));
   /////////////向下移动一格
   table_lock.lock();
   tab.clr_block(bl); //清空上一次方块位置
   bl.move(Block::DOWN); //向下移动一步
   if (-1 == tab.set_block(bl)) { //是否触底
    bl.move(Block::UP); //如果触底,还原触底前位置
    tab.set_block(bl);
    table_lock.unlock();
    break;
   }
   table_lock.unlock();
  }
  //如果满行则消行
  table_lock.lock();
  for (i = 0; i < tab.getHeight(); i++) {
   if (tab.if_full(i)) { //是否满行
    tab.clr_line(i); //如果是,消行
    tab.move_line(i); //将所消行的上面的棋盘信息下移
    i--;  //下移后,重新检查这一行是否满(可能出现几行同时消去)
    tab.set_count(100); //记录得分
   }
  }
  table_lock.unlock();
 }
 return 0;
}

grid.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// 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();
}; //坐标
 
 
 
#endif //UNTITLED_GRID_H

grid.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//
// 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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//
// 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() {
  //顺时针旋
  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的逆向
  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;
   }
    //反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;
   }
    //反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;
   }
    //田
   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;
   }
    //山
   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

?
1
2
3
4
5
6
7
8
9
10
11
//
// Created by adl on 2020/7/17.
//
 
#include "Block.h"
Block::Action Block::Menu[]={
  &Block::up,
  &Block::down,
  &Block::left,
  &Block::right
};

Table.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//
// 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;
  //比如下降之后 table[x][y]上有方块了
  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 << "| 等级:" << getLevel() << std::endl;
  else if (i == 10)
   std::cout << "| 得分:" << 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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//
// 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){    //构造棋盘
  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); //安设方块
 void clr_block(const Block &bl);  //清除方块
 int clr_line(int y);  //消行
 
 int getHeight() const;
 
 //获取棋盘宽度
 int if_full(int y);  //判定是否满行
 int get_table(int x, int y); //获取棋盘上点信息
 void paint();   //绘制棋盘
 void move_line(int y);  //整行下移
 void set_count(int c);  //记录得分
 int get_count();
 
 int getWidth() const;
 //获取得分
 
private:
 int table[TABLE_SIZE][TABLE_SIZE];//棋盘
 int height, width;  //棋盘的高和宽
 int count;   //得分
 int level;
 
};
 
#endif //UNTITLED_TABLE_H

hierarchical_mutex.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//
// 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

积累的经验:

1.生成随机数的uniform_int_distribution,defualt_random_engine(time(0))使用时必须用static,缺点是多次执行程序的第一次生成的数字是一样的

2.与c++primer p743类似,使用成员指针函数表可以使用户调用的函数更加明了。

3.给互斥锁加权值,并以权值大小顺序加锁,可以保证线程加锁顺序一致,避免死锁(出现则抛出异常)(这个纯粹活学活用,因为c++锁和线程接触不多)

4.thread xxx([&](){})是可以放在函数内部的线程

5.利用select监控标准输入

6.利用tcgetattr,tcsetaddr,termiosi结构体
nt.c_lflag &= ~ECHO; nt.c_lflag &= ~ISIG; nt.c_lflag &= ~ICANON;
可以在linux关闭回显,实现getch

7.this_thread::sleep_for(std::chrono::milliseconds(400 / tab.getLevel()));可以咋线程中实现毫秒级别睡眠

8.类中静态对象初始化可以写在其对应.cpp文件中

反思:

未使用类的继承,各种方块理论可以写成子类,因为主线程一开始直接使用了Block对象,后期不容易修改.

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/adlatereturn/article/details/107445014