zoukankan      html  css  js  c++  java
  • 游戏2048的核心算法c#版本的实现

        接触游戏有一段时间了,也写了一些东西,效果还不错,今天没事,我就把2048 c# 版本的实现贴出来,代码已经测试过,可以正常、完美运行。当然了,在网上有很多有关2048的实现方法,但是没有提出到类里面,只是写的测试代码,我在这里已经完全提到类里面,核心类和核心方法都经过测试,没有问题。由于本人学习有漏,或者不足,也请大家批评指正,大家共同进步。

          该文章分为三个部分,我们分门别类说的,大家也会很清楚。目录如下:

        第一部分:图片展示,最开始,我还是先把程序的运行效果贴出来,大家有一个初步感受。

        第二部分:代码的前端部分,因为我这个程序就是一个在控制台中运行的程序,没有界面。但是有些操作需要在控制台中操作,这部分代码在控制台中。

        第三部分:2048核心的类和辅助类型,所有核心算法和实现都在这里,都已经封装了方法,直接调用就可以。

        其实这三个部分很简单,每个部分都有自己的职责,废话不多说了,直接上代码。

        一、2048 在控制台中的运行效果(主要以贴图为主。)

          1、数据初始化

            
          这是程序第一次执行的效果,数据刚刚完成初始化。

          2、操作开始,点击键盘的 a 字母代表向做移动,分为两张图,移动前和移动后的效果。

            左移前
            

            左移后
            

           3、点击键盘的 D 字符代表向右移动,分为两个图片,分别是移动前和移动后。

              移动前
              
              移动后
              

            4、点击键盘的 w 字符代表向上移动,分为两个图片,分别是移动前和移动后。
              移动前

              

              移动后
              

            5、点击键盘的 s 字符代表向下移动,分为两个图片,分别是移动前和移动后。
              移动前

              

              移动后
              

        二、在控制台中控制逻辑和一些辅助方法,逻辑很简单,就不多说了,直接上代码。

     1 GameCoreManager game = new GameCoreManager(5);
     2 
     3             game.Initail();
     4 
     5             Console.WriteLine("原始数组:");
     6             PrintArray(game.DataContainer);
     7             Console.WriteLine();
     8 
     9             while (true)
    10             {
    11                 if (game.CalculateEmptyElements(game.DataContainer).Count <= 0)
    12                 {
    13                     Console.WriteLine("游戏结束");
    14                     break;
    15                 }
    16                 switch (Console.ReadLine())
    17                 {
    18                     case "w":
    19                         game.Move(Direction.Up);
    20                         break;
    21                     case "s":
    22                         game.Move(Direction.Down);
    23                         break;
    24                     case "a":
    25                         game.Move(Direction.Left);
    26                         break;
    27                     case "d":
    28                         game.Move(Direction.Right);
    29                         break;
    30                     case "exit":
    31                         break;
    32                 }
    33                 if (game.IsChange)
    34                 {
    35                     game.GenerateRandomNumber();
    36                     PrintArray(game.DataContainer);
    37                 }
    38             }


             以上代码就是放在控制台的 Main 方法中药执行的代码。

             这个代码主要适用于打印二维数组的,逻辑不复杂,用于在控制台中显示移动效果。
            

     1 /// <summary>
     2         /// 打印二维数组在控制台上。
     3         /// </summary>
     4         /// <param name="array">要打印数据的数组。</param>
     5         private static void PrintArray(int[,] array)
     6         {
     7             Console.Clear();
     8             if (array == null || array.Length <= 0)
     9             {
    10                 Console.WriteLine("没有任何元素可以打印。");
    11             }
    12 
    13             for (int row = 0; row < array.GetLength(0); row++)
    14             {
    15                 for (int column = 0; column < array.GetLength(1); column++)
    16                 {
    17                     Console.Write(array[row,column]+"	");
    18                 }
    19                 Console.WriteLine();
    20             }
    21         }


            在控制台中的代码就是这些,是不是很简单,其实不是很复杂,接下来我们看看核心类的实现。


        三、2048 核心类 GameCoreManager 的实现,里面有完整的备注,所以我就不多说了。大家可以直接使用,测试。
          

      1 /// <summary>
      2     /// 游戏核心算法的管理器类型,该类型定义 2048 游戏的核心算法。
      3     /// </summary>
      4     public sealed class GameCoreManager
      5     {
      6         #region 实例字段
      7 
      8         private int[,] _dataContainer;
      9 
     10         #endregion
     11 
     12         #region 构造函数
     13 
     14         /// <summary>
     15         /// 初始化 GameCoreManager 类型的新实例,数据容器维度默认值:4.
     16         /// </summary>
     17         public GameCoreManager() : this(4) { }
     18 
     19         /// <summary>
     20         /// 通过制定的数据维度初始化 GameCoreManager 类型的新实例。
     21         /// </summary>
     22         /// <param name="capacity">数据容量。</param>
     23         public GameCoreManager(int capacity)
     24         {
     25             if (capacity <= 0 || capacity >= 32)
     26             {
     27                 throw new ArgumentNullException("dimensional is null.");
     28             }
     29             DataContainer = new int[capacity, capacity];
     30         }
     31 
     32         #endregion
     33 
     34         #region 实例属性
     35 
     36         /// <summary>
     37         /// 获取数据
     38         /// </summary>
     39         public int[,] DataContainer { get => _dataContainer; private set => _dataContainer = value; }
     40 
     41         #endregion
     42 
     43         #region 实例接口方法
     44 
     45         /// <summary>
     46         /// 初始化游戏数据。
     47         /// </summary>
     48         public void Initail()
     49         {
     50             int length = DataContainer.GetLength(0) / 2 + DataContainer.GetLength(0) % 2;
     51             for (int i = 0; i < length; i++)
     52             {
     53                 GenerateRandomNumber();
     54             }
     55         }
     56 
     57         /// <summary>
     58         /// 数据移动。
     59         /// </summary>
     60         /// <param name="direction">要移动的方向</param>
     61         public void Move(Direction direction)
     62         {
     63             //判断原数组是否发生了变化。
     64             //记录原数组。
     65             int[,] destinationArray = new int[DataContainer.GetLength(0), DataContainer.GetLength(1)];
     66             Array.Copy(DataContainer, destinationArray, DataContainer.Length);
     67             IsChange = false;
     68 
     69             switch (direction)
     70             {
     71                 case Direction.Up:
     72                     MoveUp();
     73                     break;
     74                 case Direction.Down:
     75                     MoveDown();
     76                     break;
     77                 case Direction.Left:
     78                     MoveLeft();
     79                     break;
     80                 case Direction.Right:
     81                     MoveRight();
     82                     break;
     83             }
     84 
     85             //比较现在的数组和以前的数组比较
     86             for (int row = 0; row < DataContainer.GetLength(0); row++)
     87             {
     88                 for (int column = 0; column < DataContainer.GetLength(1); column++)
     89                 {
     90                     if (DataContainer[row, column] != destinationArray[row, column])
     91                     {
     92                         IsChange = true;
     93                         return;
     94                     }
     95                 }
     96             }
     97         }
     98 
     99         /// <summary>
    100         /// 获取或者设置数组元素是否发生变动。true 表示发生变动,false 表示没有变动。
    101         /// </summary>
    102         public bool IsChange { get; private set; }
    103 
    104         /// <summary>
    105         /// 计算空元素的个数,并保存元素的索引位置。
    106         /// </summary>
    107         /// <param name="sourceArray">要处理的二维数组。</param>
    108         /// <returns>返回保存空元素索引位置的列表对象。</returns>
    109         public IList<Position> CalculateEmptyElements(int[,] sourceArray)
    110         {
    111             IList<Position> elements = new List<Position>(DataContainer.GetLength(0) * DataContainer.GetLength(1));
    112             if (sourceArray == null || sourceArray.Length <= 0)
    113             {
    114                 return elements;
    115             }
    116 
    117             for (int row = 0; row < sourceArray.GetLength(0); row++)
    118             {
    119                 for (int column = 0; column < sourceArray.GetLength(1); column++)
    120                 {
    121                     if (sourceArray[row, column] == 0)
    122                     {
    123                         elements.Add(new Position(row, column));
    124                     }
    125                 }
    126             }
    127             return elements;
    128         }
    129 
    130         /// <summary>
    131         /// 随机生成数字元素填充空的数组元素。
    132         /// </summary>
    133         public void GenerateRandomNumber()
    134         {
    135             var list = CalculateEmptyElements(this.DataContainer);
    136             if (list.Count > 0)
    137             {
    138                 Random random = new Random();
    139                 var position = list[random.Next(0, list.Count)];
    140                 DataContainer[position.Row, position.Column] = random.Next(0, 10) == 4 ? 4 : 2;
    141             }
    142         }
    143 
    144         #endregion
    145 
    146         #region 实例私有方法(核心算法)
    147 
    148         /// <summary>
    149         /// 将数组中的为 0 的元素移动到数组最后面。[1,0,0,2],结果为【1,2,0,0】
    150         /// </summary>
    151         /// <param name="array">要处理的数组。</param>
    152         private void MoveZeroToLast(int[] array)
    153         {
    154             if (array == null || array.Length <= 0)
    155             {
    156                 return;
    157             }
    158 
    159             int[] myarray = new int[array.Length];
    160 
    161             int index = 0;
    162             for (int i = 0; i < array.Length; i++)
    163             {
    164                 if (array[i] != 0)
    165                 {
    166                     myarray[index++] = array[i];
    167                 }
    168             }
    169             //通过引用修改元素才可以,修改引用对外界没有影响。
    170             myarray.CopyTo(array, 0);
    171         }
    172 
    173         /// <summary>
    174         /// 合并数组中相邻相同的数字元素,后一个元素清零。[2,2,0,2],结果为【4,2,0,0】
    175         /// </summary>
    176         /// <param name="array">要处理的数组。</param>
    177         private void MergeSameNumber(int[] array)
    178         {
    179             if (array == null || array.Length <= 0)
    180             {
    181                 return;
    182             }
    183 
    184             MoveZeroToLast(array);//2,2,2,0
    185 
    186             for (int i = 0; i < array.Length - 1; i++)
    187             {
    188                 if (array[i] != 0 && array[i] == array[i + 1])
    189                 {
    190                     array[i] += array[i + 1];
    191                     array[i + 1] = 0;
    192                 }
    193             }
    194 
    195             //4,0,2,0
    196 
    197             MoveZeroToLast(array);//4,2,0,0            
    198         }
    199 
    200         /// <summary>
    201         /// 向上移动数据。
    202         /// </summary>
    203         private void MoveUp()
    204         {
    205             //从上往下取数据。
    206             if (DataContainer == null || DataContainer.Length <= 0)
    207             {
    208                 return;
    209             }
    210 
    211             int[] tempArray = new int[DataContainer.GetLength(0)];
    212 
    213             for (int column = 0; column < DataContainer.GetLength(1); column++)
    214             {
    215                 for (int row = 0; row < DataContainer.GetLength(0); row++)
    216                 {
    217                     tempArray[row] = DataContainer[row, column];
    218                 }
    219 
    220                 MergeSameNumber(tempArray);
    221 
    222                 for (int row = 0; row < DataContainer.GetLength(0); row++)
    223                 {
    224                     DataContainer[row, column] = tempArray[row];
    225                 }
    226             }
    227         }
    228 
    229         /// <summary>
    230         /// 向下移动数据。
    231         /// </summary>
    232         private void MoveDown()
    233         {
    234             //从下往上取
    235             if (DataContainer == null || DataContainer.Length <= 0)
    236             {
    237                 return;
    238             }
    239 
    240             int[] tempArray = new int[DataContainer.GetLength(0)];
    241 
    242             for (int column = 0; column < DataContainer.GetLength(1); column++)
    243             {
    244                 for (int row = DataContainer.GetLength(0) - 1; row >= 0; row--)
    245                 {
    246                     tempArray[DataContainer.GetLength(0) - 1 - row] = DataContainer[row, column];
    247                 }
    248 
    249                 MergeSameNumber(tempArray);
    250 
    251                 for (int row = DataContainer.GetLength(0) - 1; row >= 0; row--)
    252                 {
    253                     DataContainer[row, column] = tempArray[DataContainer.GetLength(0) - 1 - row];
    254                 }
    255             }
    256         }
    257 
    258         /// <summary>
    259         /// 向左移动数据。
    260         /// </summary>
    261         private void MoveLeft()
    262         {
    263             if (DataContainer == null || DataContainer.Length <= 0)
    264             {
    265                 return;
    266             }
    267             //从左往右
    268 
    269             int[] tempArray = new int[DataContainer.GetLength(1)];
    270 
    271             for (int i = 0; i < DataContainer.GetLength(0); i++)
    272             {
    273                 for (int j = 0; j < DataContainer.GetLength(1); j++)
    274                 {
    275                     tempArray[j] = DataContainer[i, j];
    276                 }
    277 
    278                 MergeSameNumber(tempArray);
    279 
    280                 for (int j = 0; j < DataContainer.GetLength(1); j++)
    281                 {
    282                     DataContainer[i, j] = tempArray[j];
    283                 }
    284             }
    285         }
    286 
    287         /// <summary>
    288         /// 向右移动数据。
    289         /// </summary>
    290         private void MoveRight()
    291         {
    292             if (DataContainer == null || DataContainer.Length <= 0)
    293             {
    294                 return;
    295             }
    296 
    297             //从右向左取
    298 
    299             //{ 2,2,4,8 },0,4,4,8
    300             //{ 2,4,4,4 },
    301             //{ 0,8,4,0 },
    302             //{ 2,4,0,4 }
    303 
    304             int[] tempArray = new int[DataContainer.GetLength(1)];
    305 
    306             for (int i = 0; i < DataContainer.GetLength(1); i++)
    307             {
    308                 for (int j = DataContainer.GetLength(1) - 1; j >= 0; j--)
    309                 {
    310                     //8,4,2,2
    311                     tempArray[DataContainer.GetLength(1) - 1 - j] = DataContainer[i, j];
    312                 }
    313 
    314                 //8,4,4,0
    315                 MergeSameNumber(tempArray);
    316 
    317                 for (int j = DataContainer.GetLength(1) - 1; j >= 0; j--)
    318                 {
    319                     DataContainer[i, j] = tempArray[DataContainer.GetLength(1) - 1 - j];
    320                 }
    321             }
    322         }        
    323 
    324         #endregion
    325     }

         另外,还有两个枚举类型,代码很简单,直接贴代码了。

     1     /// <summary>
     2     /// 元素的坐标位置。
     3     /// </summary>
     4     public struct Position
     5     {
     6         /// <summary>
     7         /// 获取或者设置元素的行坐标。
     8         /// </summary>
     9         public int Row { get; set; }
    10 
    11         /// <summary>
    12         /// 获取或者设置元素的列坐标。
    13         /// </summary>
    14         public int Column { get; set; }
    15 
    16         /// <summary>
    17         /// 通过行坐标、列坐标初始化 Position 对象实例。
    18         /// </summary>
    19         /// <param name="row">元素的行坐标。</param>
    20         /// <param name="column">元素的列坐标。</param>
    21         public Position(int row, int column)
    22         {
    23             if (row >= 0 && column >= 0)
    24             {
    25                 this.Row = row;
    26                 this.Column = column;
    27             }
    28             else
    29             {
    30                 throw new ArgumentNullException("parameter is null.");
    31             }
    32         }
    33     }
    34 
    35     /// <summary>
    36     /// 移动的方向。
    37     /// </summary>
    38     public enum Direction
    39     {
    40         /// <summary>
    41         /// 向上移动
    42         /// </summary>
    43         Up,
    44 
    45         /// <summary>
    46         /// 向下移动
    47         /// </summary>
    48         Down,
    49 
    50         /// <summary>
    51         /// 向左移动
    52         /// </summary>
    53         Left,
    54 
    55         /// <summary>
    56         /// 向右移动
    57         /// </summary>
    58         Right
    59     }


        这就是2048 核心类的实现,代码都有备注,其实,逻辑也不复杂,大家看也是可以看得懂的,只是需要点耐心。

        文章就到此为止了,这是有关2028的核心算法,我发的代码都是经过测试的,大家可以拿过去直接使用,修改和测试。而且代码都很完整,所以我就没有把源码贴出来。

  • 相关阅读:
    React Native之bugly的使用
    前端面试复习记录(2020-03-21)
    React Native之手写签名
    RN0.56+设置allowFontScaling
    React Native从0.39.x升级到0.49.x的那些事
    React Native组件系列之NetInfo
    React Native组件系列之ImageBackground
    使用Azure SQL Data Sync在Azure 上实现蓝绿部署
    新版Azure Automation Account 浅析(三) --- 用Runbook管理AAD Application Key
    新版Azure Automation Account 浅析(二) --- 更新Powershell模块和创建Runbook
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/13820448.html
Copyright © 2011-2022 走看看