2048 是练习"类设计"的绝佳素材:把棋盘、分数、操作封装成 Board2048 类,主函数只负责读键和渲染。四方向滑动合并,用"压缩-合并-补零"三步走。
1. Board2048 类
私有成员:4×4 棋盘 + 分数。公开接口:move()、spawn()、gameOver()。外部不关心内部细节——这就是封装:
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
class Board2048 {
int grid[4][4];
int score = 0;
public:
Board2048() {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) grid[i][j] = 0;
spawn(); spawn();
}
void move(int dir); // 0左 1右 2上 3下
void spawn();
bool gameOver() const;
void print() const;
};
2. 滑动与合并
把一行抽成 vector:先去 0(压缩),再相邻相同合并(只合并一次),最后补 0。上下方向用"转置"复用同一套逻辑:
vector<int> slide(vector<int> line) {
vector<int> a;
for (int x : line) if (x) a.push_back(x);
vector<int> b;
for (size_t i = 0; i < a.size(); i++) {
if (i + 1 < a.size() && a[i] == a[i + 1]) {
b.push_back(a[i] * 2); score += a[i] * 2; i++;
} else b.push_back(a[i]);
}
while (b.size() < 4) b.push_back(0);
return b;
}
void Board2048::move(int dir) {
for (int i = 0; i < 4; i++) {
vector<int> line;
for (int j = 0; j < 4; j++)
line.push_back(dir < 2 ? grid[i][j] : grid[j][i]);
if (dir == 1 || dir == 3) reverse(line.begin(), line.end());
line = slide(line);
if (dir == 1 || dir == 3) reverse(line.begin(), line.end());
for (int j = 0; j < 4; j++)
if (dir < 2) grid[i][j] = line[j]; else grid[j][i] = line[j];
}
}
3. 完整代码
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;
class Board2048 {
int grid[4][4];
int score = 0;
public:
Board2048() {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) grid[i][j] = 0;
spawn(); spawn();
}
void spawn() {
vector<pair<int,int> > empty;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (!grid[i][j]) empty.push_back({i, j});
if (empty.empty()) return;
int k = rand() % empty.size();
grid[empty[k].first][empty[k].second] = (rand() % 10 == 0) ? 4 : 2;
}
void move(int dir) {
for (int i = 0; i < 4; i++) {
vector<int> line;
for (int j = 0; j < 4; j++)
line.push_back(dir < 2 ? grid[i][j] : grid[j][i]);
if (dir == 1 || dir == 3) reverse(line.begin(), line.end());
vector<int> a;
for (int x : line) if (x) a.push_back(x);
vector<int> b;
for (size_t k = 0; k < a.size(); k++) {
if (k + 1 < a.size() && a[k] == a[k + 1]) { b.push_back(a[k] * 2); score += a[k] * 2; k++; }
else b.push_back(a[k]);
}
while (b.size() < 4) b.push_back(0);
if (dir == 1 || dir == 3) reverse(b.begin(), b.end());
for (int j = 0; j < 4; j++)
if (dir < 2) grid[i][j] = b[j]; else grid[j][i] = b[j];
}
spawn();
}
bool gameOver() const {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) {
if (!grid[i][j]) return false;
if (i + 1 < 4 && grid[i][j] == grid[i + 1][j]) return false;
if (j + 1 < 4 && grid[i][j] == grid[i][j + 1]) return false;
}
return true;
}
void print() const {
system("clear"); // Windows: system("cls")
cout << "分数: " << score << " (a左 d右 w上 s下 q退出)\n";
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (grid[i][j]) printf("%5d", grid[i][j]);
else printf("%5s", ".");
}
cout << "\n";
}
}
};
int main() {
srand((unsigned)time(NULL));
Board2048 game;
char key;
while (true) {
game.print();
cin >> key;
if (key == 'q') break;
if (key == 'a') game.move(0);
else if (key == 'd') game.move(1);
else if (key == 'w') game.move(2);
else if (key == 's') game.move(3);
if (game.gameOver()) { game.print(); cout << "游戏结束!\n"; break; }
}
return 0;
}
4. 运行与常见问题
保存为 2048.cpp,g++ 2048.cpp -o 2048 后运行。常见问题:①合并只发生一次——这是规则,[2,2,4] 左滑得 [4,4];②没生成新块——move 里每次 spawn(),想"有效才生成"就保存滑动前棋盘对比;③分数不对——合并时当场累加。
💡 改进方向:①撤销;②最高分存档;③动画;④自动演示 AI;⑤颜色渲染。