小言_互联网的博客

2048核心算法

393人阅读  评论(0)

 Note:想了半天开场白,发现真的不知道说啥,得,直接给您阐述思路吧。

首先最基础的,2048只有四种操作手法,就是上下左右的移动(手机上就为划屏)。但是有过一定编程基础的你肯定能想到,很明显要用一个类似方法来实现这四种操作,而不是四种完全不同的方法。所以我们完全可以先实现一种操作,进而类比到其他操作就能很快的实现这个过程。

玩家选定一个移动方向之后,我们希望看到的是相同相邻的数据相加,且所有数据都堆积到移动的方向上。这就是我们最基本的玩法和需求。所有我们就要细分理解实现其中的过程:

1、我么希望相同相邻的数据相加,那么相同但是不相邻中间隔0的数据怎么处理?
2、我们希望两个数相加过后不再参与下一次的相加,如2240,不能2+2之后又+4,这一块怎么设计呢?
3、在不同的移动方向上,我们相加和读取的顺序是不同的,怎么才能找到一个能重载的方法呢?

2048游戏就是一个二维数组上的游戏。我们的所有操作,实际上都是针对这一个二维数组的。而大部分时候,我们都会选择用嵌套的for循环,来分而治之。什么意思呢?我们可以将二维数组看成多个一维数组,我们不去操作整个二维数组而是操作每一行每一列。向左移动时,我们一行一行的处理;向上移动时,我们一列一列的处理。这样问题就简单很多了。所以我们的大概思路就有了,就拿向左移动举个例子。

首先我们希望实现相加,那我们将这一行读取出来,当做一维数组来处理。我们逐个扫描数据,遇到相邻一样的我们就直接相加。这时候问题1就来了,隔着0怎么处理?我们自然可以选择跳过,读到0就跳过不就好了吗。但这时我们需要拿一个数据,记录之前的非零数。你比如说2004008,第一个2我们必须记录下来,直到遇到一个4,我们再拿来做比较,然后我们又得去记录4。这样会不会就显得很麻烦呢?我们换种思路,我们何不直接将0全部移动到最右边呢?从游戏直观上看也确实是0都挪到了右边。所以我们可以考虑新建一个数组,读取一行之后就将所有的非零数一次存进新数组,2004008就成了2480000,这不就方便多了吗?相同相邻的相加,后一个置0,继续遍历下一个数据,处理完后再复制给二维数组不就好了。所以我们把第一个问题解决了。

我们希望不要多次相加,这个其实已经解决掉了,为什么这么说?前面说到了,相加之后,后一个位置数据置0。那么它就不可能和再后面一个相加,也就不用担心参与下一次的加法。当然这里我们要做一个特殊处理,就是0不能拿来判断,不能两个相邻的0你也去做一个加法对不对。所以我们要加一个判断,读到0咱们就不理,因为加了之后必有0,跳过就好了。第二个问题也解决掉了。

前两段,我们已经实现了相加的过程,很简单对不对。关键就是最后一个,我们总不能每一个操作,都设计一个新的算法来实现吧?程序不就要求最好实现重载和复用吗。所以我们需要寻找其中,能够一样用在其他操作上的步骤,然后把它单独提出来写成方法就好了。首先读取,这个可以吗?这就要说到读取那块了。我们观察2048的游戏规则就能发现,数字是往玩家操作的方向堆积的,相加的方向也是不一样的。你比如往左是右边往左加,而往右确实左边往右加。所以读取行和列这块我们不能一视同仁,读取是为了复制到一个新的数组中一起做操作。所以这个操作咱们可以提成一个方法。移动0,相加的过程都是一样的,提出了就好了。剩下的就是每一个操作中循环体的细小差异罢了,这些都是提不出来的。

大概思路就是酱了,语文不好,表述能力不是很强,但是想法都是在代码中的。因为自学的是U3D游戏开发,所以用的是C#语音,和C/C++在我看来差异并不是很大。很容易换成C/C++来实现,所以仍然有借鉴的价值。不多说啦,结合文字和代码能够理解的更快,加油!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Game_2048
{
    class Program
    {
        private static void RemoveZero(int[] array) // 后移0
        { // 将0全部后移,转换一下思路就是将所有非0元素赋值给一个新的数组
            int[] newArray = new int[array.Length]; // 默认赋值全0
            for (int i = 0, j = 0; i < array.Length; i++) // 用array.Length可以让2048扩展到 nxn
            {
                if (array[i] != 0)
                    newArray[j++] = array[i];
            }
            newArray.CopyTo(array, 0); // 将新数组的数据复制给array
        }

        private static void Merge(int[] array) // 相加
        { // 将相邻且相同的数据相加,已经做过加法的就不加
            RemoveZero(array); // 先将所有0后移
            for (int i = 0; i < array.Length - 1; i++)
            {
                if (array[i] != 0 && array[i] == array[i + 1]) // 非0数据才做相加
                {
                    array[i] *= 2; // 相同且相邻则相加
                    array[i + 1] = 0; // i先自增即移到下一个数据,清零后i再自增再移到下一个数据
                }
            }
            RemoveZero(array); // 再次将所有0后移
        }

        private static void MoveToUp(int[,] map) // 上移
        {
            int[] mergeArray = new int[map.GetLength(0)]; // 获取一维维数即行数
            // 遍历列   
            for (int c = 0; c < map.GetLength(1); c++)
            {
                for (int r = 0; r < map.GetLength(0); r++) // 在每一列中遍历行
                    mergeArray[r] = map[r, c]; // 将该列数组赋值给新数组

                Merge(mergeArray); // 进行相加操作

                for (int r = 0; r < map.GetLength(0); r++) // 将相加后的结果又给回二维数组
                    map[r, c] = mergeArray[r];
            }
        }

        private static void MoveToDown(int[,] map) // 下移
        {
            int[] mergeArray = new int[map.GetLength(0)]; // 获取一维维数即行数
            // 遍历列
            for (int c = 0; c < map.GetLength(1); c++)
            {
                for (int r = map.GetLength(0) - 1; r >=0; r--) // 倒着赋值
                    mergeArray[3 - r] = map[r, c];

                Merge(mergeArray); // 进行相加操作

                for (int r = map.GetLength(0) - 1; r >=0; r--) // 将相加后的结果又给回二维数组
                    map[r, c] = mergeArray[3 - r];
            }
        }

        private static void MoveToLeft(int[,] map) // 左移
        {
            int[] mergeArray = new int[map.GetLength(1)]; // 获取二维维数即列数
            // 遍历行
            for (int r = 0; r < map.GetLength(0); r++)
            {
                for (int c = 0; c < map.GetLength(1); c++)
                    mergeArray[c] = map[r, c]; // 将该行数组赋值给新数组

                Merge(mergeArray); // 进行相加操作

                for (int c = 0; c < map.GetLength(1); c++) // 将相加后的结果又给回二维数组
                    map[r, c] = mergeArray[c];
            }
        }

        private static void MoveToRight(int[,] map) // 右移
        {
            int[] mergeArray = new int[map.GetLength(1)]; // 获取二维维数即列数
            // 遍历行
            for (int r = 0; r < map.GetLength(0); r++)
            {
                for (int c = map.GetLength(1) - 1; c >= 0; c--)
                    mergeArray[3 - c] = map[r, c]; // 将该行数组赋值给新数组

                Merge(mergeArray); // 进行相加操作

                for (int c = map.GetLength(1) - 1; c >= 0; c--) // 将相加后的结果又给回二维数组
                    map[r, c] = mergeArray[3 - c];
            }
        }

        private static void PrintArray(int[,] map) // 打印输出
        {
            for (int r = 0; r < map.GetLength(0); r++)
            {
                for (int c = 0; c < map.GetLength(1); c++)
                    Console.Write(map[r, c] + "\t"); // WriteLine会在输出完后自动换行
                Console.WriteLine();
            }
        }

        static void Main(string[] args)
        {
            int[,] map = new int[4, 4] // 初始化赋值
            {
                {2, 2, 4, 8},
                {2, 4, 4, 4},
                {0, 8, 4, 0},
                {2, 4, 0, 4}
            };
            PrintArray(map);
            Console.WriteLine();
            MoveToRight(map);
            PrintArray(map);
        }
    }
}

自己试着不要看把代码码出来哦,测试样例可以直接复制,加油小伙汁小改改,为了更美好的未来呢~~~


转载:https://blog.csdn.net/qq_37701948/article/details/102467695
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场