2048游戏算法优化

2048 游戏算法

游戏介绍

2048 游戏是一款非常经典的游戏,游戏界面是一个 4 × 4 4 \times 4 4×4 的方格,每个方格可以存储一个数字或为空,数字的取值为 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048。

游戏规则如下

  1. 开始时,界面上只有一个方格,其值为 2。
  2. 可以向四个方向滑动,每次滑动时,将所有方格向对应方向移动。每次滑动后新生成一个值为 2 或 4 的方格,方格位置随机。
  3. 移动时,如果方格前方是空格,则方格向前移动,如果与前方方格值相同,则方格与前方方格合并,方格值变为两倍。
  4. 当产生值 2048 的方格时,游戏结束,游戏胜利。
  5. 当移动后没有方格可移动时,游戏结束,游戏失败。

游戏算法实现

先通过最简单的方式来实现 2048 游戏,游戏中的方格可以用一个二维数组表示,数组的行和列分别表示方格行和列,数组中的每个元素表示方格中的数字。然后进行算法的分析。

算法详情

首先声明全局数组arr,用于表示格子情况;

private int[][] arr;

接下来考虑到每次滑动方向不同,可以用 1,2,3,4 分别表示上下左右,由于每次对数组处理时下标不同,如果直接对整个数组进行处理,则需要给每个移动方向写一个方法,但那是多余的,因为每次移动时的处理原理是一样的,都能看做从远端到近端的向前移动,就比如向左移动看作从右端到左端的向前移动。因此我们将向上滑动作为默认方向,将其他方向转变成向上滑动再进行处理。方法如下:

  1. 可以按照滑动方向由前向后,提取每一个格子数据,并依次填充到一维数组中;
  2. 使用处理向上滑动的方法对一维数组进行处理,得到新的一维数组;
  3. 将新的一维数组按照移动方向倒推回去正确填充二维数组中。

例如如下状态进行向下滑动:

2 2 0 2 0 0 0 0 4 2 0 0 0 0 0 0 \begin{matrix} 2 & 2 & 0 & 2 \\ 0 & 0 & 0 & 0 \\ 4 & 2 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ \end{matrix} 2040202000002000

将二维数组每一列由下到上(即滑动方向的由前向后)提取出来:

L(0) = (0,4,0,2)L(1) = (0,2,0,2)

L(2) = (0,0,0,0)L(3) = (0,0,0,2)

进行处理(提取出的数组视为向左合并),得到新的一维数组:

L(0) = (4,2,0,0)L(1) = (4,0,0,0)

L(2) = (0,0,0,0)L(3) = (2,0,0,0)

按照移动方向填充回去,得到新的二维数组:

0 0 0 0 0 0 0 0 2 0 0 0 4 4 0 2 \begin{matrix} 0 & 0 & 0 & 0 \\ 0 & 0& 0&0\\ 2&0&0&0\\ 4&4&0&2 \\ \end{matrix} 0024000400000002

按照以上逻辑,处理二维数组的方法由于是在滑动时调用可以取名为onMove(),然后将处理提取出的一维数组的方法命名为dataProcess()onMove()代码如下:

//滑动时调用,接受一个方向参数

public void onMove(int direction)
{
    int[] L = new int[4]; // 变形后的数组
    switch (direction)
    {
        case 1: // 上
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    L[j] = arr[j][i]; // 每一列由上往下赋值
                }

                L = dataProcess(L);
                for (int j = 0; j < 4; j++)
                {
                    arr[j][i] = L[j];
                }
            }
            break;
        case 2: // 下
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    L[j] = arr[3 - j][i]; // 每一列由下往上赋值
                }
                L = dataProcess(L);
                for (int j = 0; j < 4; j++)
                {
                    arr[j][i] = L[3 - j];
                }
            }
            break;
        case 3: // 左
            for (int i = 0; i < 4; i++)
            {
                L = arr[i]; // 复制一行
                L = dataProcess(L);
                arr[i] = L;
            }
            break;
        case 4: // 右
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    L[j] = arr[i][3 - j]; // 反方向赋值一行
                }
                L = dataProcess(L);
                for (int j = 0; j < 4; j++)
                {
                    arr[i][j] = L[3 - j];
                }
            }
            break;
    }
}

dataProcess()方法用于对提取出的数组进行处理,接受一个一维数组,返回一个一维数组。处理思路是将传入数组去零的同时向左对齐,然后开始进行多轮比较,算法步骤如下:

  1. 遍历数组元素,如果有0,则将其后元素前移,零放到末尾;
  2. 遍历数组元素,如果遍历到的第一个元素为0,则退出以下步骤;如果与后一个位置元素相等,则将前一个元素乘以2,并将后面的元素依次前移,最后一个置零;如果不相等则往后遍历,遍历到最后两个元素也不相等,则退出以下步骤;
  3. 如果步骤2遍历到的元素与下一个元素相等,则处理完后将遍历起始点改为第一轮时的遍历到的位置往前移一位(已经是第一位则不用前移),重复第2步。

代码如下:

public int[] dataProcess(int[] M)
{
    int j = 0; // 定位器,指明下一次处理数组的几号元素
    int[] T = {0, 0, 0, 0};
    for (int i = 0; i < 4; i++) // 遍历数组,将0元素移除
    {
        if (M[i] != 0)
        {
            T[j] = M[i];
            j++; // 依次将数组中非0元素放入新数组中
        }
    }
    j = 0;
    int m = 0; // 记录循环次数

    // 循环判断是否可以合并,当判断到最后一个元素时,循环结束
    while (j < 3)
    {
        if (T[j] == 0)
            break; // 如果检索到0,则后面都为0,跳出循环

        // 判断相邻元素是否相等
        if (T[j] == T[j + 1])
        {
            // 相等则前元素乘2,后续元素依次向前一格,最后一个置零
            T[j] = T[j] * 2;
            for (int i = j + 1; i < 3; i++)
            {
                T[i] = T[i + 1];
            }
            T[3] = 0;
            if (j > 0) j--; // 当处理完两个相等元素,应该将定位器前移一格
        } else
            j++; // 两元素不等,继续往后对比
        m++;
        if (m == 100) // 避免死循环
            break;
    }
    return T;
}

至此该算法算法已介绍完毕,此算法获取滑动方向,然后将矩阵中的数据提取成一维数组,对提取出的一维数组先去零,再进行相同元素合并,最后将处理完的一维数组还原成矩阵,则完成一次滑动。

测试代码

以下给出测试代码:

/ --Game类-- /


package project;

public class Game
{
    private int[][] arr;

    public Game(int[][] arr)
    {
        this.arr = arr;
    }

    // 移动函数
    public void onMove(int direction)
    {
        int[] L = new int[4]; // 变形后的数组
        switch (direction)
        {
            case 1: // 上
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        L[j] = arr[j][i]; // 每一列由上往下赋值
                    }

                    L = dataProcess(L);
                    for (int j = 0; j < 4; j++)
                    {
                        arr[j][i] = L[j];
                    }
                }
                break;
            case 2: // 下
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        L[j] = arr[3 - j][i]; // 每一列由下往上赋值
                    }
                    L = dataProcess(L);
                    for (int j = 0; j < 4; j++)
                    {
                        arr[j][i] = L[3 - j];
                    }
                }
                break;
            case 3: // 左
                for (int i = 0; i < 4; i++)
                {
                    L = arr[i]; // 复制一行
                    L = dataProcess(L);
                    arr[i] = L;
                }
                break;
            case 4: // 右
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        L[j] = arr[i][3 - j]; // 反方向赋值一行
                    }
                    L = dataProcess(L);
                    for (int j = 0; j < 4; j++)
                    {
                        arr[i][j] = L[3 - j];
                    }
                }
                break;
        }
        printArray(arr);
    }

    // 数据处理
    public int[] dataProcess(int[] M)
    {
        int j = 0; // 定位器,指明下一次处理数组的几号元素
        int[] T = {0, 0, 0, 0};
        for (int i = 0; i < 4; i++) // 遍历数组,将0元素移除
        {
            if (M[i] != 0)
            {
                T[j] = M[i];
                j++; // 依次将数组中非0元素放入新数组中
            }
        }
        j = 0;
        int m = 0; // 记录循环次数

        // 循环判断是否可以合并,当判断到最后一个元素时,循环结束
        while (j < 3)
        {
            if (T[j] == 0)
                break; // 如果检索到0,则后面都为0,跳出循环

            // 判断相邻元素是否相等
            if (T[j] == T[j + 1])
            {
                // 相等则前元素乘2,后续元素依次向前一格,最后一个置零
                T[j] = T[j] * 2;
                for (int i = j + 1; i < 3; i++)
                {
                    T[i] = T[i + 1];
                }
                T[3] = 0;
                if (j > 0) j--; // 当处理完两个相等元素,应该将定位器前移一格
            } else
                j++; // 两元素不等,继续往后对比
            m++;
            if (m == 100) // 避免死循环
                break;
        }
        return T;
    }

    //可用于打印一维数组
    public void printA(int[] array)
    {
        System.out.print("打印一维数组");
        for (int i = 0; i < array.length; i++)
        {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }

    //可用于打印二维数组
    public void printArray(int[][] array)
    {
        System.out.print("打印二维数组");
        System.out.println();
        for (int i = 0; i < array.length; i++)
        {
            for (int j = 0; j < array[i].length; j++)
            {
                System.out.print(array[i][j] + " ");
            }
            System.out.println();
        }
    }
}

/ --main函数-- /

package project;

public class Main
{
    public static void main(String[] args)
    {
        // 测试矩阵,可自行修改
        int[][] G = new int[][] {
                {2, 0, 0, 2},
                {0, 0, 2, 4},
                {2, 2, 8, 8},
                {8, 4, 2, 2}
        };
        Game m = new Game(G);

        // 移动方向:上、下、左、右对应参数1、2、3、4
        m.onMove(3);
    }
}

测试时请将以上两个类放在project文件夹中,类的文件名要与类名相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值