游戲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]+"\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的核心算法,我發的代碼都是經過測試的,大家可以拿過去直接使用,修改和測試。而且代碼都很完整,所以我就沒有把源碼貼出來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM