接觸游戲有一段時間了,也寫了一些東西,效果還不錯,今天沒事,我就把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的核心算法,我發的代碼都是經過測試的,大家可以拿過去直接使用,修改和測試。而且代碼都很完整,所以我就沒有把源碼貼出來。
