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;⑤颜色渲染。