C#版本实现:探究游戏2048的核心算法
接触游戏有一段时间了,也写了一些东西,效果还不错,今天没事,我就把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上一篇: 用Python实现2048小游戏(终端升级版)相比上篇 增添了撤回功能和历史最高分数的统计
下一篇: 《2048网页版游戏:轻松上手》
推荐阅读
-
C#版本实现:探究游戏2048的核心算法
-
使用Qt实现一个可自定义难度的2048小游戏 [版本:Qt 5.6.2]
-
Java 8新特性探究(十三)JavaFX 8新特性以及开发2048游戏-JavaFX历史## 跟java在服务器端和web端成绩相比,桌面一直是java的软肋,于是Sun公司在2008年推出JavaFX,弥补桌面软件的缺陷,请看下图JavaFX一路走过来的改进 从上图看出,一开始推出时候,开发者需使用一种名为JavaFX Script的静态的、声明式的编程语言来开发JavaFX应用程序。因为JavaFX Script将会被编译为Java bytecode,程序员可以使用Java代码代替。 JavaFX 2.0之后的版本摒弃了JavaFX Script语言,而作为一个Java API来使用。因此使用JavaFX平台实现的应用程序将直接通过标准Java代码来实现。 JavaFX 2.0 包含非常丰富的 UI 控件、图形和多媒体特性用于简化可视化应用的开发,WebView可直接在应用中嵌入网页;另外 2.0 版本允许使用 FXML 进行 UI 定义,这是一个脚本化基于 XML 的标识语言。 从JDK 7u6开始,JavaFx就与JDK捆绑在一起了,JavaFX团队称,下一个版本将是8.0,目前所有的工作都已经围绕8.0库进行。这是因为JavaFX将捆绑在Java 8中,因此该团队决定跳过几个版本号,迎头赶上Java 8。 ##JavaFx8的新特性 ## ###全新现代主题:Modena 新的Modena主题来替换原来的Caspian主题。不过在Application的start方法中,可以通过setUserAgentStylesheet(STYLESHEET_CASPIAN)来继续使用Caspian主题。 参考http://fxexperience.com/2013/03/modena-theme-update/ ###JavaFX 3D 在JavaFX8中提供了3D图像处理API,包括Shape3D (Box, Cylinder, MeshView, Sphere子类),SubScene, Material, PickResult, LightBase (AmbientLight 和PointLight子类),SceneAntialiasing等。Camera类也得到了更新。从JavaDoc中可以找到更多信息。 ###富文本 强化了富文本的支持 ###TreeTableView ###日期控件DatePicker 增加日期控件 ###用于 CSS 结构的公共 API