通过游戏玩法来思考,首先最容易想到的是,2048只有四个移动方向,可以用差不多的方法来实现这四种操作。玩家选定一个移动方向之后,该方向上相同的数要进行一次相加操作,且只能加一次,然后所有的非零数堆积到移动方向上。

2048的游戏界面可以看做是一个二维数组。我们的所有操作,实际上都是针对这一个二维数组的。我们可以将二维数组看成多个一维数组来处理,比如左右移动时一行一行的处理,上下移动时一列一列的处理。接下来
以向右移动为例去实现它。

假设当前在某一行,我们希望实现数字的相加。首先将这一行读取出来,当做一维数组来处理。逐个枚举每一个数字,如果有相邻且相等的数就直接相加。这时候问题就来了,
相等的数中间隔着0怎么处理?
我们可以选择用一个变量记录之前的非零数,然后跳过0,继续枚举后面的数,遇到相同的再相加,最后将一整行的非零数都移到右边即可。在这里不妨换种思路,为何不
先将所有的零数移动到最左边后再去执行加法呢?
读取一行数字后,我们先将所有的零存进进一个新数组的左侧,2 0 2 0 就成了 0 0 2 2。然后从最右边开始,将相同且相邻的数字相加,后一个数置0,
防止相加后又参与了相加的问题;接着继续枚举下一个数字,处理完后再进行一次移0操作,然后将结果返回给二维数组。

确定了相加的算法后,继续思考其过程可以发现:数字是往玩家操作的方向堆积的,但相加的方向是反过来的。例如数字向右移动,但却是从最右边开始往左相加的;数字向上移动,但却是从最上面开始往下相加的。不管向哪个方向移动,移0和复制的操作都是一样的,因此在考虑这一块时只需要注意实现上的细微差别即可。

另外就是需要设计随机数的生成。随机数是在空白格子上随机生成的,因此要定义一个空白格子结构体,结构体内存的是空白格子在二维数组中的下标。用一个结构体数组来存储每次移动后的所有空白格子,随机挑选一个空白格子,随机生成2或者4(
两者生成概率最好不要完全一样)即可。

大概思路就是酱了,语文不好,表述能力不是很强,但是想法都是在代码中的。因为自学的是U3D游戏开发,所以用的是 C# 语言。很容易换成 C/C++
来实现,所以仍然有借鉴的价值。不多说啦,结合文字和代码能够理解的更快,加油同学!
using System; using System.Collections.Generic; namespace Console2048 { ///
<summary> /// 游戏核心类,负责处理游戏核心算法,与界面无关 /// </summary> // 定义枚举类型表示移动方向 enum
MoveDirection { Up = 0, Down = 1, Left = 2, Right = 3 } // 空白格子结构体 struct
BlankLocation { public int RIndex { get; set; } public int CIndex { get; set; }
public BlankLocation(int rIndex, int cIndex) : this() { // 结构体自动属性要求给字段赋值
this.RIndex = rIndex; this.CIndex = cIndex; } } class GameCore //
核心类不适合做成静态类,因此方法也不做成静态类 { #region 字段定义 private int[,] map; private int[]
mergeArray; private int[] removeZeroArray; private Random random; private
int[,] oldMap; public bool IsChange { get; set; } // IsChange属性标记是否产生了合并操作 //
public bool IsFull { get; set; } // IsFull属性标记数字是否已经满了 public bool IsWin { get;
set; } // IsWin属性标记玩家是否获胜 public bool CanChange { get; set; } //
CanChange属性标志是否还能合并 public int Score { get; set; } // Score属性记录玩家最终得分 public
List<BlankLocation> emptyBlankList; #endregion #region 属性 public int[,] Map {
get { return this.map; } } #endregion #region 构造函数 public GameCore() { map =
new int[4, 4]; mergeArray = new int[4]; // 暂存每一行或每一列移动之前的数据 removeZeroArray =
new int[4]; // 保存移动后的数据 emptyBlankList = new List<BlankLocation>(16); //
初始化格子列表,长度为16 random = new Random(); // 随机数 oldMap = new int[4, 4]; //
记录每次移动之前所有数据的数组 } #endregion #region 数据合并 private void RemoveZero() // 后移0 { //
将0全部后移,转换一下思路就是将所有非0元素赋值给一个新的数组 Array.Clear(removeZeroArray, 0, 4); // 每次都清零数组
for (int i = 0, j = 0; i < mergeArray.Length; i++) // 长度用 mergeArray.Length
可以让2048扩展到 n x n { if (mergeArray[i] != 0) removeZeroArray[j++] =
mergeArray[i]; } removeZeroArray.CopyTo(mergeArray, 0); // 将新数组的数据复制给mergeArray
} private void Merge() // 相加 { // 将相邻且相同的数据相加,已经做过加法的就不加 RemoveZero(); //
先将所有0后移 for (int i = 0; i < mergeArray.Length - 1; i++) { if (mergeArray[i] !=
0 && mergeArray[i] == mergeArray[i + 1]) // 非0数据才做相加 { mergeArray[i] *= 2; //
相同且相邻则相加 Score += mergeArray[i] * mergeArray[i]; // 更新得分 mergeArray[i + 1] = 0;
// 相邻数据置0 } } RemoveZero(); // 再次将所有0后移 } #endregion #region 移动和检查 private void
MoveToUp() // 上移 { for (int c = 0; c < map.GetLength(1); c++) // GetLength(1)
返回的是二维数组的列数 { for (int r = 0; r < map.GetLength(0); r++) mergeArray[r] = map[r,
c]; // 将 map 中的一列数据复制给 mergeArray Merge(); // 进行相加操作 for (int r = 0; r <
map.GetLength(0); r++) // 将相加后的结果返回给二维数组 map[r, c] = mergeArray[r]; } } private
void MoveToDown() // 下移 { for (int c = 0; c < map.GetLength(1); c++) { for (int
r = map.GetLength(0) - 1; r >= 0; r--) // 倒着复制 mergeArray[3 - r] = map[r, c];
Merge(); // 进行相加操作 for (int r = map.GetLength(0) - 1; r >= 0; r--) //
将相加后的结果返回给二维数组 map[r, c] = mergeArray[3 - r]; } } private void MoveToLeft() //
左移 { for (int r = 0; r < map.GetLength(0); r++) { for (int c = 0; c <
map.GetLength(1); c++) mergeArray[c] = map[r, c]; // 将 map 中的一行数据复制给 mergeArray
Merge(); // 进行相加操作 for (int c = 0; c < map.GetLength(1); c++) // 将相加后的结果返回给二维数组
map[r, c] = mergeArray[c]; } } private void MoveToRight() // 右移 { for (int r =
0; r < map.GetLength(0); r++) { for (int c = map.GetLength(1) - 1; c >= 0; c--)
// 倒着复制 mergeArray[3 - c] = map[r, c]; Merge(); // 进行相加操作 for (int c =
map.GetLength(1) - 1; c >= 0; c--) // 将相加后的结果返回给二维数组 map[r, c] = mergeArray[3 -
c]; } } private void Check() // 检查是否产生不了合并 { CanChange = false; for (int r = 0;
r < map.GetLength(0); r++) // 外层 for 循环按行枚举 { for (int c = 0; c <
map.GetLength(1); c++) // 内层 for 循环按列枚举 { if (r != map.GetLength(1) - 1 && c !=
map.GetLength(1) - 1) { if (map[r, c] == map[r, c + 1] || map[r, c] == map[r +
1, c]) { // 对于中间3x3的格子只需比较向后和向下的元素 CanChange = true; return; } } else if (r ==
map.GetLength(1) - 1 && c != map.GetLength(1) - 1) { //
对于处于第四行但不在第四列的数据只需同后一列数据比较 if (map[r, c] == map[r, c + 1]) { CanChange = true;
return; } } else if (r != map.GetLength(1) - 1 && c == map.GetLength(1) - 1) {
if (map[r, c] == map[r + 1, c]) { // 对于处于第四列但不在第四行的数据只需同后一列数据比较 CanChange =
true; return; } } // 剩下一个就是在第四行第四列的元素就不用继续和谁比较了 } } } public void
Move(MoveDirection direction) { // 移动前记录map Array.Copy(map, oldMap,
map.Length); IsChange = false; // 移动之前赋值“无变化” IsWin = false; CanChange = true;
switch (direction) { case MoveDirection.Up: MoveToUp(); break; case
MoveDirection.Down: MoveToDown(); break; case MoveDirection.Left: MoveToLeft();
break; case MoveDirection.Right: MoveToRight(); break; } // 用户操作之后对比map是否产生变化
for (int r = 0; r < map.GetLength(0); r++) { for (int c = 0; c <
map.GetLength(1); c++) { if (map[r, c] == 2048) // 如果出现了2048,游戏胜利就结束 { IsWin =
true; return; } if (map[r, c] != oldMap[r, c]) { IsChange = true; //
产生变化返回true指示产生随机数 return; } if (!IsChange) Check(); } } } #endregion #region
生成随机数字 // 生成数字 // 需求:在空白位置,随机产生一个2(90%)或者4(10%) //
分析:先统计所有空白位置,再随机选择一个位置随机填入2或4 private void CalculateEmpty() {
emptyBlankList.Clear(); // 每次统计空位置前先清空列表 for (int r = 0; r < map.GetLength(0);
r++) { for (int c = 0; c < map.GetLength(1); c++) { if (map[r, c] == 0) { //
记录空白位置的索引,因为个数不确定,所以用集合 // 类是将多个基本数据类型,封装为一个自定义类型 emptyBlankList.Add(new
BlankLocation(r, c)); } } } } public void GenerateNumber() { CalculateEmpty();
//IsFull = true; if (emptyBlankList.Count > 0) // 如果有空位置的话 { //
随机挑选一个空位置,然后把它的索引返回给loc //IsFull = false; int randomIndex = random.Next(0,
emptyBlankList.Count); BlankLocation loc = emptyBlankList[randomIndex]; //
生成4的概率是30%,相当于0~9生成0,1,2 int ran = random.Next(0, 10); if (ran == 0 || ran == 1
|| ran == 2) map[loc.RIndex, loc.CIndex] = 4; else map[loc.RIndex, loc.CIndex]
= 2; } /*else IsFull = true;*/ } #endregion } class Program { static void
Main() { GameCore core = new GameCore(); // 先生成两个随机数 core.GenerateNumber();
core.GenerateNumber(); // 将控制台背景色改成白色 Console.BackgroundColor =
ConsoleColor.White; // 显示游戏界面 DrawMap(core.Map); while (true) { // 用户操作
UserAction(core); core.GenerateNumber(); DrawMap(core.Map); if (core.IsWin) {
// 合出了2048,游戏胜利 Console.WriteLine("\t\t\t\t\t恭喜你,游戏胜利!");
Console.WriteLine("\t\t\t\t\t您最终的分数是:{0}", core.Score); } if
(core.emptyBlankList.Count == 0 && !core.CanChange) { //
如果没有空位置生成随机数,且在各个方向上都已经无法合并(即没有任何改变了)了则表明游戏结束
Console.WriteLine("\t\t\t\t\t没有空位置了!"); Console.WriteLine("\t\t\t\t\t 游戏结束!");
Console.WriteLine("\t\t\t\t\t您最终的分数是:{0}", core.Score); break; } } } private
static void DrawMap(int[,] map) { // 因为 Main 函数是静态函数,调用不了实例,索引函数声明为静态的
Console.Clear(); Console.WriteLine("\n\n\n\n\n"); int number = 0; for (int r =
0; r < 4; r++) { Console.Write("\t\t\t\t\t"); for (int c = 0; c < 4; c++) {
number = map[r, c]; // 数字暂存二维数组中的数,防止每个分支都访问一次二维数组 // 根据数字不同选择不同的颜色输出 if
(number == 0) Console.ForegroundColor = ConsoleColor.Black; else if (number ==
2) Console.ForegroundColor = ConsoleColor.Gray; else if (number == 4)
Console.ForegroundColor = ConsoleColor.Red; else if (number == 8)
Console.ForegroundColor = ConsoleColor.Green; else if (number == 16)
Console.ForegroundColor = ConsoleColor.Yellow; else if (number == 32)
Console.ForegroundColor = ConsoleColor.Blue; else if (number == 64)
Console.ForegroundColor = ConsoleColor.Magenta; else if (number == 128)
Console.ForegroundColor = ConsoleColor.Cyan; else if (number == 256)
Console.ForegroundColor = ConsoleColor.DarkYellow; else if (number == 512)
Console.ForegroundColor = ConsoleColor.DarkBlue; else if (number == 1024)
Console.ForegroundColor = ConsoleColor.DarkGreen; else Console.ForegroundColor
= ConsoleColor.DarkRed; Console.Write(number + "\t"); }
Console.WriteLine("\n"); // 换行 } } private static void UserAction(GameCore
core) { Console.Write("\t\t\t\t\t"); switch (Console.ReadLine()) { case "w":
case "W": core.Move(MoveDirection.Up); break; case "s": case "S":
core.Move(MoveDirection.Down); break; case "a": case "A":
core.Move(MoveDirection.Left); break; case "d": case "D":
core.Move(MoveDirection.Right); break; } } } }

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信