数据结构 - 数组 - 游戏 2048

用数组来模拟著名游戏 2048。

游戏《2048》

《2048》是一款数字益智游戏,在 \(4*4\) 的方格中通过上下左右滑动来控制数字的变化,游戏胜利的条件是出现 \(2048\) 这个数字。

游戏规则如下:

  • 玩家每次可以选择上下左右其中一个方向去滑动,定义滑动的方向为前,滑动的反方向为后,每滑动一次,所有的数字方块都会向前移动靠拢至边缘。

  • 每一行(列)从最前方第二个方块依次向前方方块发起撞击,相撞的两个方块数字不同时不发生变化,撞击发起块向后顺延,相撞的两个方块相同时变成一个新的数值相加的数字块,后续的数字块依次向前递补空位,撞击发起块变为新生成数字块的后面第二个数字块。

  • 撞击结束后系统会在空白的地方随机出现一个数字方块2或者4。

\(4*4\) 方格中的 \(16\) 个格子分别赋予编号 \(1-16\),即类似下述矩阵

\[\begin{align*} \begin{bmatrix} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \\ 13 & 14 & 15 & 16 \end{bmatrix} \end{align*} \]

输入格式

  • 输入 \(1\)

输入按格子编号 \(1-16\) 输入游戏《2048》的一个状态序列,编号对应的格子没有数字则输入0,如输入0 0 0 0 4 0 2 0 4 0 2 2 2 8 8 8表示状态1

  • 输入 \(2\)

输入一个用户操作和新增块地址序列,a表示向左滑动,s表示向下滑动,d表示向右滑动,w表示向上滑动

如输入w 1 2 a 5 4 s 11 2 d 13 4 d 9 2,表示用户依次进行了如下5次操作:

玩家向上滑动一次,之后在编号 \(1\) 的位置新出现一个数值为 \(2\) 的新增块

玩家向左滑动一次,之后在编号 \(5\) 的位置新出现一个数值为 \(4\) 的新增块

玩家向下滑动一次,之后在编号 \(11\) 的位置新出现一个数值为 \(2\) 的新增块

玩家向右滑动一次,之后在编号 \(13\) 的位置新出现一个数值为 \(4\) 的新增块

玩家向右滑动一次,之后在编号 \(9\) 的位置新出现一个数值为 \(2\) 的新增块

如果编号所在的位置不为空,就重新指定一个新编号进行修改,新编号按 新编号 = 原编号%16+1 进行选择,并判断新编号所在位置是否为空,若编号位置不为空,则新新编号 = 新编号%16+1,直至探索编号位置为空,然后在该编号位置增加新数字块。

输出格式

输出:从编号 \(1\) 到编号 \(16\) 方格的数字,格子为空则输出 \(0\)

输入输出样例

样例 1

输入

0 0 0 0 0 0 4 4 4 4 8 16 4 8 16 16
a 6 2

输出

0 0 0 0 8 2 0 0 8 8 16 0 4 8 32 0

样例 2

输入

0 0 0 0 0 0 4 4 2 4 8 16 4 8 16 32
d 5 2 s 4 4 a 8 4 w 9 2

输出

4 16 32 4 16 0 0 32 4 2 0 0 0 0 0 0

注意上述的输入输出每行末尾都有一个换行符\n

题解

直接使用 \(4 \times 4\) 的数组进行模拟,预先开一个 \(5 \times 5\) 的数组,编号 \(1-16\) 分布在行号 \(1-4\),列号 \(1-4\) 的矩阵中。

该模拟题主要有两步操作:按指令的滑动操作按指令的生成新数字块操作

按指令的滑动操作

共有四个滑动方向,其实只要实现向一个方向的滑动函数(其余方向是类似的)。不妨考虑向上滑动,我们再将该滑动操作分解为空位递补撞击响应。显然这两个分解操作是互不关联的,而实现了这两个操作也就可以完成一次滑动操作。撞击响应是其中实现起来最为复杂的一块,也是问题的核心,但花一些时间详细讨论该过程,可以发现代码并不难实现(不超过 \(20\) 行代码)。

按指令的生成新数字块操作

如果编号所在的位置不为空,要注意到利用新编号 = 原编号%16+1进行新编号选择的次数不超过 \(16\) 次。

另外的实现方式

可以见另一种利用这四种方向滑动的相同特性的一种实现方式,较我自己的实现方式有更高的代码重用度。

题解代码

代码如下:

#include <cstdio>
#include <cstdlib>
#include <iostream>

using namespace std;

int grids[5][5]; // 2048游戏的方格

int row(int x) {
    return ((x - 1) / 4 + 1);
}

int col(int x) {
    return ((x % 4 == 0) ? 4 : (x % 4));
}

// 滑动函数组(分别实现四个方向的滑动函数,我自己的实现方式代码重用度不高)
// 另一种利用这四种方向滑动的相同特性的一种实现方式,见 https://blog.csdn.net/weixin_41207175/article/details/84836749
void wsl()
{
    // 向上滑动(遍历 grids 的每一列)
    for (int j = 1; j <= 4; ++j) {
        int index = 0;
        for (int i = 1; i <= 4; ++i) {
            // 空位递补
            if (grids[i][j] != 0) {
                ++index;
                if (index != i) {
                    grids[index][j] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
        int hit_index = 2;
        int hitted_index = 1;
        while(hit_index <= 4) {
            // 撞击响应(问题的核心)
            if (grids[hit_index][j] == 0) {
                ++hit_index;
                continue;
            }
            if (grids[hitted_index][j] == grids[hit_index][j]) {
                grids[hitted_index][j] += grids[hit_index][j];
                grids[hit_index][j] = 0;
                hitted_index = ++hit_index;
                while (hitted_index <= 4 && grids[hitted_index][j] == 0) 
                    ++hitted_index;
                hit_index = hitted_index + 1;
            }
            else {
                hitted_index = hit_index;
                ++hit_index;
            }
        }
        index = 0;
        for (int i = 1; i <= 4; ++i) {
            // 空位递补
            if (grids[i][j] != 0) {
                ++index;
                if (index != i) {
                    grids[index][j] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
    }
}
void asl()
{
    // 向左滑动(遍历 grids 的每一行)
    for (int i = 1; i <= 4; ++i) {
        int index = 0;
        for (int j = 1; j <= 4; ++j) {
            // 空位递补
            if (grids[i][j] != 0) {
                ++index;
                if (index != j) {
                    grids[i][index] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
        int hit_index = 2;
        int hitted_index = 1;
        while (hit_index <= 4) {
            // 撞击响应(问题的核心)
            if (grids[i][hit_index] == 0) {
                ++hit_index;
                continue;
            }
            if (grids[i][hitted_index] == grids[i][hit_index]) {
                grids[i][hitted_index] += grids[i][hit_index];
                grids[i][hit_index] = 0;
                hitted_index = ++hit_index;
                while (hitted_index <= 4 && grids[i][hitted_index] == 0)
                    ++hitted_index;
                hit_index = hitted_index + 1;
            }
            else {
                hitted_index = hit_index;
                ++hit_index;
            }
        }
        index = 0;
        for (int j = 1; j <= 4; ++j) {
            // 空位递补
            if (grids[i][j] != 0) {
                ++index;
                if (index != j) {
                    grids[i][index] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
    }
}
void ssl()
{
    // 向下滑动(遍历 grids 的每一列)
    for (int j = 1; j <= 4; ++j) {
        int index = 5;
        for (int i = 4; i >= 1; --i) {
            // 空位递补
            if (grids[i][j] != 0) {
                --index;
                if (index != i) {
                    grids[index][j] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
        int hit_index = 3;
        int hitted_index = 4;
        while (hit_index >= 1) {
            // 撞击响应(问题的核心)
            if (grids[hit_index][j] == 0) {
                --hit_index;
                continue;
            }
            if (grids[hitted_index][j] == grids[hit_index][j]) {
                grids[hitted_index][j] += grids[hit_index][j];
                grids[hit_index][j] = 0;
                hitted_index = --hit_index;
                while (hitted_index >= 1 && grids[hitted_index][j] == 0)
                    --hitted_index;
                hit_index = hitted_index - 1;
            }
            else {
                hitted_index = hit_index;
                --hit_index;
            }
        }
        index = 5;
        for (int i = 4; i >= 1; --i) {
            // 空位递补
            if (grids[i][j] != 0) {
                --index;
                if (index != i) {
                    grids[index][j] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
    }
}
void dsl()
{
    // 向右滑动(遍历 grids 的每一行)
    for (int i = 1; i <= 4; ++i) {
        int index = 5;
        for (int j = 4; j >= 1; --j) {
            // 空位递补
            if (grids[i][j] != 0) {
                --index;
                if (index != j) {
                    grids[i][index] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
        int hit_index = 3;
        int hitted_index = 4;
        while (hit_index >= 1) {
            // 撞击响应(问题的核心)
            if (grids[i][hit_index] == 0) {
                --hit_index;
                continue;
            }
            if (grids[i][hitted_index] == grids[i][hit_index]) {
                grids[i][hitted_index] += grids[i][hit_index];
                grids[i][hit_index] = 0;
                hitted_index = --hit_index;
                while (hitted_index >= 1 && grids[i][hitted_index] == 0)
                    --hitted_index;
                hit_index = hitted_index - 1;
            }
            else {
                hitted_index = hit_index;
                --hit_index;
            }
        }
        index = 5;
        for (int j = 4; j >= 1; --j) {
            // 空位递补
            if (grids[i][j] != 0) {
                --index;
                if (index != j) {
                    grids[i][index] = grids[i][j];
                    grids[i][j] = 0;
                }
            }
        }
    }
}
// 滑动函数
void slide(char ch) 
{
    switch (ch)
    {
    case 'w':
        // 向上滑动
        wsl();
        break;
    case 'a':
        // 向左滑动
        asl();
        break;
    case 's':
        // 向下滑动
        ssl();
        break;
    case 'd':
        // 向右滑动
        dsl();
        break;
    default:
        break;
    }
}
// 按指令生成新的数字块
void add_grid(int x, int y)
{
    for (int i = 1; i <= 16; ++i) {
        if (grids[row(x)][col(x)] == 0) {
            grids[row(x)][col(x)] = y;
            break;
        }
        else x = x % 16 + 1;
    }
}
int main()
{
    int x, y;
    // 输入初始方格状态
    for (int i = 1; i <= 16; ++i) {
        cin >> x;
        grids[row(i)][col(i)] = x;
    }
    char ch;
    ch = getchar();
    while ((ch = getchar())) {
        cin >> x >> y;
        // 根据指令滑动方格
        slide(ch);
        // 根据指令生成方格
        add_grid(x, y);
        // 判断输入是否终止
        if ((ch = getchar()) && ch == '\n') break;
    }
    // 输出方格
    for (int i = 1; i <= 16; ++i) {
        if (i == 1)
            cout << grids[row(i)][col(i)];
        else
            cout << ' ' << grids[row(i)][col(i)];
    }
    printf("\n");
    return 0;
}

posted on 2022-03-30 20:43  Black_x  阅读(148)  评论(0编辑  收藏  举报