接触游戏有一段时间了,也写了一些东西,效果还不错,今天没事,我就把2048 c# 版本的实现贴出来,代码已经测试过,可以正常、完美运行。当然了,在网上有很多有关2048的实现方法,但是没有提出到类里面,只是写的测试代码,我在这里已经完全提到类里面,核心类和核心方法都经过测试,没有问题。由于本人学习有漏,或者不足,也请大家批评指正,大家共同进步。

      该文章分为三个部分,我们分门别类说的,大家也会很清楚。目录如下:

    第一部分:图片展示,最开始,我还是先把程序的运行效果贴出来,大家有一个初步感受。

    第二部分:代码的前端部分,因为我这个程序就是一个在控制台中运行的程序,没有界面。但是有些操作需要在控制台中操作,这部分代码在控制台中。

    第三部分:2048核心的类和辅助类型,所有核心算法和实现都在这里,都已经封装了方法,直接调用就可以。

    其实这三个部分很简单,每个部分都有自己的职责,废话不多说了,直接上代码。

    一、2048 在控制台中的运行效果(主要以贴图为主。)

      1、数据初始化

        
      这是程序第一次执行的效果,数据刚刚完成初始化。

      2、操作开始,点击键盘的 a 字母代表向做移动,分为两张图,移动前和移动后的效果。

        左移前
        

        左移后
        

       3、点击键盘的 D 字符代表向右移动,分为两个图片,分别是移动前和移动后。

          移动前
          
          移动后
          

        4、点击键盘的 w 字符代表向上移动,分为两个图片,分别是移动前和移动后。
          移动前

          

          移动后
          

        5、点击键盘的 s 字符代表向下移动,分为两个图片,分别是移动前和移动后。
          移动前

          

          移动后
          

    二、在控制台中控制逻辑和一些辅助方法,逻辑很简单,就不多说了,直接上代码。

      

       GameCoreManager game = new GameCoreManager(5);

            game.Initail();

            Console.WriteLine("原始数组:");
PrintArray(game.DataContainer);
Console.WriteLine(); while (true)
{
if (game.CalculateEmptyElements(game.DataContainer).Count <= 0)
{
Console.WriteLine("游戏结束");
break;
}
switch (Console.ReadLine())
{
case "w":
game.Move(Direction.Up);
break;
case "s":
game.Move(Direction.Down);
break;
case "a":
game.Move(Direction.Left);
break;
case "d":
game.Move(Direction.Right);
break;
case "exit":
break;
}
if (game.IsChange)
{
game.GenerateRandomNumber();
PrintArray(game.DataContainer);
}
}

        以上代码就是放在控制台的 Main 方法中药执行的代码。

        

        /// <summary>
/// 打印二维数组在控制台上。
/// </summary>
/// <param name="array">要打印数据的数组。</param>
private static void PrintArray(int[,] array)
{
Console.Clear();
if (array == null || array.Length <= 0)
{
Console.WriteLine("没有任何元素可以打印。");
} for (int row = 0; row < array.GetLength(0); row++)
{
for (int column = 0; column < array.GetLength(1); column++)
{
Console.Write(array[row,column]+"\t");
}
Console.WriteLine();
}
}

    这个代码主要适用于打印二维数组的,逻辑不复杂,用于在控制台中显示移动效果。
    在控制台中的代码就是这些,是不是很简单,其实不是很复杂,接下来我们看看核心类的实现。

    三、2048 核心类 GameCoreManager 的实现,里面有完整的备注,所以我就不多说了。大家可以直接使用,测试。

  

    /// <summary>
/// 游戏核心算法的管理器类型,该类型定义 2048 游戏的核心算法。
/// </summary>
public sealed class GameCoreManager
{
#region 实例字段 private int[,] _dataContainer; #endregion #region 构造函数 /// <summary>
/// 初始化 GameCoreManager 类型的新实例,数据容器维度默认值:4.
/// </summary>
public GameCoreManager() : this(4) { } /// <summary>
/// 通过制定的数据维度初始化 GameCoreManager 类型的新实例。
/// </summary>
/// <param name="capacity">数据容量。</param>
public GameCoreManager(int capacity)
{
if (capacity <= 0 || capacity >= 32)
{
throw new ArgumentNullException("dimensional is null.");
}
DataContainer = new int[capacity, capacity];
} #endregion #region 实例属性 /// <summary>
/// 获取数据
/// </summary>
public int[,] DataContainer { get => _dataContainer; private set => _dataContainer = value; } #endregion #region 实例接口方法 /// <summary>
/// 初始化游戏数据。
/// </summary>
public void Initail()
{
int length = DataContainer.GetLength(0) / 2 + DataContainer.GetLength(0) % 2;
for (int i = 0; i < length; i++)
{
GenerateRandomNumber();
}
} /// <summary>
/// 数据移动。
/// </summary>
/// <param name="direction">要移动的方向</param>
public void Move(Direction direction)
{
//判断原数组是否发生了变化。
//记录原数组。
int[,] destinationArray = new int[DataContainer.GetLength(0), DataContainer.GetLength(1)];
Array.Copy(DataContainer, destinationArray, DataContainer.Length);
IsChange = false; switch (direction)
{
case Direction.Up:
MoveUp();
break;
case Direction.Down:
MoveDown();
break;
case Direction.Left:
MoveLeft();
break;
case Direction.Right:
MoveRight();
break;
} //比较现在的数组和以前的数组比较
for (int row = 0; row < DataContainer.GetLength(0); row++)
{
for (int column = 0; column < DataContainer.GetLength(1); column++)
{
if (DataContainer[row, column] != destinationArray[row, column])
{
IsChange = true;
return;
}
}
}
} /// <summary>
/// 获取或者设置数组元素是否发生变动。true 表示发生变动,false 表示没有变动。
/// </summary>
public bool IsChange { get; private set; } /// <summary>
/// 计算空元素的个数,并保存元素的索引位置。
/// </summary>
/// <param name="sourceArray">要处理的二维数组。</param>
/// <returns>返回保存空元素索引位置的列表对象。</returns>
public IList<Position> CalculateEmptyElements(int[,] sourceArray)
{
IList<Position> elements = new List<Position>(DataContainer.GetLength(0) * DataContainer.GetLength(1));
if (sourceArray == null || sourceArray.Length <= 0)
{
return elements;
} for (int row = 0; row < sourceArray.GetLength(0); row++)
{
for (int column = 0; column < sourceArray.GetLength(1); column++)
{
if (sourceArray[row, column] == 0)
{
elements.Add(new Position(row, column));
}
}
}
return elements;
} /// <summary>
/// 随机生成数字元素填充空的数组元素。
/// </summary>
public void GenerateRandomNumber()
{
var list = CalculateEmptyElements(this.DataContainer);
if (list.Count > 0)
{
Random random = new Random();
var position = list[random.Next(0, list.Count)];
DataContainer[position.Row, position.Column] = random.Next(0, 10) == 4 ? 4 : 2;
}
} #endregion #region 实例私有方法(核心算法) /// <summary>
/// 将数组中的为 0 的元素移动到数组最后面。[1,0,0,2],结果为【1,2,0,0】
/// </summary>
/// <param name="array">要处理的数组。</param>
private void MoveZeroToLast(int[] array)
{
if (array == null || array.Length <= 0)
{
return;
} int[] myarray = new int[array.Length]; int index = 0;
for (int i = 0; i < array.Length; i++)
{
if (array[i] != 0)
{
myarray[index++] = array[i];
}
}
//通过引用修改元素才可以,修改引用对外界没有影响。
myarray.CopyTo(array, 0);
} /// <summary>
/// 合并数组中相邻相同的数字元素,后一个元素清零。[2,2,0,2],结果为【4,2,0,0】
/// </summary>
/// <param name="array">要处理的数组。</param>
private void MergeSameNumber(int[] array)
{
if (array == null || array.Length <= 0)
{
return;
} MoveZeroToLast(array);//2,2,2,0 for (int i = 0; i < array.Length - 1; i++)
{
if (array[i] != 0 && array[i] == array[i + 1])
{
array[i] += array[i + 1];
array[i + 1] = 0;
}
} //4,0,2,0 MoveZeroToLast(array);//4,2,0,0
} /// <summary>
/// 向上移动数据。
/// </summary>
private void MoveUp()
{
//从上往下取数据。
if (DataContainer == null || DataContainer.Length <= 0)
{
return;
} int[] tempArray = new int[DataContainer.GetLength(0)]; for (int column = 0; column < DataContainer.GetLength(1); column++)
{
for (int row = 0; row < DataContainer.GetLength(0); row++)
{
tempArray[row] = DataContainer[row, column];
} MergeSameNumber(tempArray); for (int row = 0; row < DataContainer.GetLength(0); row++)
{
DataContainer[row, column] = tempArray[row];
}
}
} /// <summary>
/// 向下移动数据。
/// </summary>
private void MoveDown()
{
//从下往上取
if (DataContainer == null || DataContainer.Length <= 0)
{
return;
} int[] tempArray = new int[DataContainer.GetLength(0)]; for (int column = 0; column < DataContainer.GetLength(1); column++)
{
for (int row = DataContainer.GetLength(0) - 1; row >= 0; row--)
{
tempArray[DataContainer.GetLength(0) - 1 - row] = DataContainer[row, column];
} MergeSameNumber(tempArray); for (int row = DataContainer.GetLength(0) - 1; row >= 0; row--)
{
DataContainer[row, column] = tempArray[DataContainer.GetLength(0) - 1 - row];
}
}
} /// <summary>
/// 向左移动数据。
/// </summary>
private void MoveLeft()
{
if (DataContainer == null || DataContainer.Length <= 0)
{
return;
}
//从左往右 int[] tempArray = new int[DataContainer.GetLength(1)]; for (int i = 0; i < DataContainer.GetLength(0); i++)
{
for (int j = 0; j < DataContainer.GetLength(1); j++)
{
tempArray[j] = DataContainer[i, j];
} MergeSameNumber(tempArray); for (int j = 0; j < DataContainer.GetLength(1); j++)
{
DataContainer[i, j] = tempArray[j];
}
}
} /// <summary>
/// 向右移动数据。
/// </summary>
private void MoveRight()
{
if (DataContainer == null || DataContainer.Length <= 0)
{
return;
} //从右向左取 //{ 2,2,4,8 },0,4,4,8
//{ 2,4,4,4 },
//{ 0,8,4,0 },
//{ 2,4,0,4 } int[] tempArray = new int[DataContainer.GetLength(1)]; for (int i = 0; i < DataContainer.GetLength(1); i++)
{
for (int j = DataContainer.GetLength(1) - 1; j >= 0; j--)
{
//8,4,2,2
tempArray[DataContainer.GetLength(1) - 1 - j] = DataContainer[i, j];
} //8,4,4,0
MergeSameNumber(tempArray); for (int j = DataContainer.GetLength(1) - 1; j >= 0; j--)
{
DataContainer[i, j] = tempArray[DataContainer.GetLength(1) - 1 - j];
}
}
} #endregion
}

    这就是2048 核心类的实现,代码都有备注,其实,逻辑也不复杂,大家看也是可以看得懂的,只是需要点耐心。

    文章就到此为止了,这是有关2028的核心算法,我发的代码都是经过测试的,大家可以拿过去直接使用,修改和测试。而且代码都很完整,所以我就没有把源码贴出来。

游戏2048的核心算法c#版本的实现的更多相关文章

  1. Unity3D_(游戏)甜品消消乐02_游戏核心算法

    甜品消消乐01_游戏基础界面 传送门 甜品消消乐02_游戏核心算法 传送门 甜品消消乐03_游戏UI设计    传送门 GameManager脚本上修改Fill Time可以改变消消乐移动速度 实现过 ...

  2. 游戏2048源代码 - C语言控制台界面版

    一.游戏介绍 <2048>是最近比较流行的一款数字游戏.原版2048首先在github上发布,原作者是Gabriele Cirulli.它是基于<1024>和<小3传奇& ...

  3. Android 带你玩转实现游戏2048 其实2048只是个普通的控件(转)

    1.概述 博主本想踏入游戏开放行业,无奈水太深,不会游泳:于是乎,只能继续开发应用,但是原生Android也能开发游戏么,2048.像素鸟.别踩什么来着:今天给大家带来一篇2048的开发篇,别怕不分上 ...

  4. Android 带你玩转实现游戏2048 其实2048只是个普通的控件

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40020137,本文出自:[张鸿洋的博客] 1.概述 博主本想踏入游戏开放行业,无 ...

  5. 游戏数值——LOL篇 以LOL为起点-说游戏数值设计核心思路

      附     文   文档在今年三月份我动笔写了一小部分,但当时思路凌乱,行文梗阻,于是丢在一边构思了半年,现在又回过头来慢慢写,希望能写好写完吧,初衷是希望即时萌新也能看懂,但是好像并不能行——本 ...

  6. 开源游戏“2048”IOS移植版

    简介: 这个游戏是我在今年(2014/05)课余时闲着无聊做的一个益智类小游戏,总共花了4个工作日才完成,为了游戏的效率,做了很多优化,目前在IE5以上浏览器能够流畅运行,运行时如果屏幕分辨率不兼容, ...

  7. SQL关键字转换大写核心算法实现

    1 不跟你多废话 上代码! /// <summary> /// SQL关键字转换器 /// </summary> public class SqlConverter : IKe ...

  8. 用Java实现MVPtree——MVPtree核心算法代码的搭建

    项目需要,需要把MVPtree这种冷门的数据结构写入Java,然网上没有成形的Java实现,虽说C++看惯了不过对C++实现复杂结构也是看得蒙蔽,幸好客户给了个github上job什么的人用Java写 ...

  9. x264代码剖析(十三):核心算法之帧间预測函数x264_mb_analyse_inter_*()

    x264代码剖析(十三):核心算法之帧间预測函数x264_mb_analyse_inter_*() 帧间预測是指利用视频时间域相关性,使用临近已编码图像像素预測当前图像的像素,以达到有效去除视频时域冗 ...

随机推荐

  1. linux6.4内核由2.6升级成3.6

    安装CentOS 6.4之后,内核默认是2.6.32.由于docker需要3.0以上的内核,所以需要对内核进行升级. 1. 安装必要组件# yum -y install ncurses-devel # ...

  2. Linux rndis_host 驱动的一个BUG与解决方案

    关键字 rndis_host, linux, kernel, modem 综述 rndis 是微软定义的一套通讯方案.类似的协议还有 qmi/mbim/ecm/ncm. rndis 协议足够简单,可靠 ...

  3. 【深入理解Linux内核架构】6.6 资源分配

    一段摘自<Linux设备驱动程序>的话: 每种外设都通过读写寄存器进行控制.大部分外设都有多个寄存器,不管是内存地址空间还是I/O地址空间,这些寄存器的访问地址都是连续的. 在硬件层,内存 ...

  4. NOIP2017 Day1 T1 小凯的疑惑

    题目描述 小凯手中有两种面值的金币,两种面值均为正整数且彼此互素.每种金币小凯都有 无数个.在不找零的情况下,仅凭这两种金币,有些物品他是无法准确支付的.现在小凯想知道在无法准确支付的物品中,最贵的价 ...

  5. Scala的对字符串应用

    1.trimEnd:截取末尾几个长度的字符串 import scala.collection.mutable.ArrayBuffer val a = ArrayBuffer[Int]() a+=(1, ...

  6. python判断链表是否有环

    思路:使用快慢指针,快指针每次走两步,慢指针每次走一步,如果有环,则一定会快慢指针指向同一结点: 假设环的长度为n,先让一个指针走n步,另一个再开始走,当他们指针指向同一结点时,该结点就是环入口点 ( ...

  7. Sorted Adjacent Differences(CodeForces - 1339B)【思维+贪心】

    B - Sorted Adjacent Differences(CodeForces - 1339B) 题目链接 算法 思维+贪心 时间复杂度O(nlogn) 1.这道题的题意主要就是让你对一个数组进 ...

  8. 每天一个dos命令-net.

    Rem:关于net命令相关的常用实例(如果cmd中执行net相关命令,报错:Access is denied. 可以右键cmd,以管理员身份运行即可!) 1.创建一个新账号:net user ifsf ...

  9. 【云原生下离在线混部实践系列】深入浅出 Google Borg

    Google Borg 是资源调度管理和离在线混部领域的鼻祖,同时也是 Kubernetes 的起源与参照,已成为从业人员首要学习的典范.本文尝试管中窥豹,简单从<Large-scale clu ...

  10. Centos7防火墙以及端口控制

    开启防火墙 systemctl start firewalld.service --启动firewall systemctl enable firewalld.service --开机时启动firew ...