接触游戏有一段时间了,也写了一些东西,效果还不错,今天没事,我就把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]+"\t");
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的核心算法,我发的代码都是经过测试的,大家可以拿过去直接使用,修改和测试。而且代码都很完整,所以我就没有把源码贴出来。

posted on 2020-10-15 14:39  可均可可  阅读(1132)  评论(0编辑  收藏  举报