2048算法是消除类里面比较经典的算法。如下所示,本质上是4x4的二维数组。
2 2 4 0
4 0 2 2
4 4 0 0
4 0 0 4
玩法
分为上移,下移,左移,右移,四个步骤。移动的过程中有相邻相同的数字需要合并,合并后,后面的数字归零。合并的时候要忽略数字0,0不参与合并,并且每次移动后0都要被移到最后面。
算法分析
- 上移
上移操作的是列,首选需要取出各列元素,自上而下取出各列元素。
因为是自上而下取的元素,上移时将0元素移到列的最后。
合并相邻元素,相邻相同的元素值加到前一个元素中,后一个元素归零。
合并过程中,还会产生0元素,需要再次将0元素移到列的最后。
最后,将移动后的各列元素重新给4*4的二维数组赋值。
- 下移
下移同样是操作列,自上而下取出列元素。
下移时,需要将0元素移到列的最前面,与上移相反。
合并相邻元素,相邻相同的元素值加到后一个元素中,前一个元素归零。
合并过程中,还会产生0元素,需要再次将0元素移到列的最前面。
最后,将移动后的各列元素重新给4*4的二维数组赋值。
上移和下移的操作是类似的,只不过方向相反。
如果在第一步取列元素的时候,改成自下而上取出列元素。
下移时,需要将0元素移到列的最后。
合并相邻元素,相邻相同的元素值加到前一个元素中,后一个元素归零。
合并过程中,还会产生0元素,需要再次将0元素移到列的最后。
最后,将移动后的各列元素重新给4*4的二维数组赋值。
这样上移和下移只是取元素的方向不同,其他的操作都是相同的,可以共用部分代码。
- 左移
左移操作的是行,首选需要取出各行元素,自左而右取出各行元素。
因为是自左而右取的元素,左移时将0元素移到行的最后。
合并相邻元素,相邻相同的元素值加到前一个元素中,后一个元素归零。
合并过程中,还会产生0元素,需要再次将0元素移到行的最后。
最后,将移动后的各列元素重新给4*4的二维数组赋值。
- 右移
与下移同样的道理,取行元素的时候,自右向左取出各行元素。
右移时将0元素移到行的最后。
合并相邻元素,相邻相同的元素值加到前一个元素中,后一个元素归零。
合并过程中,还会产生0元素,需要再次将0元素移到行的最后。
最后,将移动后的各列元素重新给4*4的二维数组赋值。
代码
如上分析,上下左右移动,都需要将0元素移动到数组的末尾,因此,需要定义一个通用的方法。
//将0元素移到数组最后的方法
static int[] moveZero(int[] cols)
{
//首先定义一个4元素的一维数组,默认值都是0
int[] c = new int[4];
//遍历cols将非0元素放到c的前面,index用于计数
int index = 0;
foreach (var item in cols)
{
if (item != 0)
{
c[index++] = item;
}
}
return c;
}
相邻相同值合并,将元素值合并到前一个元素,后一个元素归零,也可以定义一个通用方法。
//相邻相同元素合并方法
static int[] merge(int[] cols)
{
//合并之前,先将0元素移到数组最后
int[] c1 = moveZero(cols);
//遍历数组元素,相邻元素作比较
for (int i = 0; i < c1.Length-1; i++)
{
if(c1[i]!=0&&c1[i]==c1[i+1])
{
c1[i] += c1[i + 1];
c1[i + 1] = 0;
}
}
//合并之后,先将0元素移到数组最后
int[] c2 = moveZero(c1);
return c2;
}
为了查看效果,需要定义一个打印二维数组的方法
static void print(int[,] m)
{
for (int i = 0; i < m.GetLength(0); i++)
{
for (int j = 0; j < m.GetLength(1); j++)
{
Console.Write(m[i,j]);
Console.Write(" ");
}
Console.WriteLine();
}
}
demo采用的是控制台应用程序,main方法如下:
static void Main(string[] args)
{
int[,] demos = new int[,] {
{2,2,4,0},
{4,0,2,2},
{4,4,0,0},
{4,0,0,4}
};
Console.WriteLine("原始数据:");
print(demos);
Console.ReadLine();
//上移
for (int c = 0; c < demos.GetLength(1); c++)
{
//1.自上而下取出列元素
int[] cols = new int[demos.GetLength(0)];
for (int r = 0; r < demos.GetLength(0); r++)
{
cols[r] = demos[r, c];
}
//2.元素合并
int[] mergedcols = merge(cols);
//3.将合并后的元素重新给二维数组赋值
for (int i = 0; i < mergedcols.Length; i++)
{
demos[i,c] = mergedcols[i];
}
}
Console.WriteLine("上移:");
print(demos);
Console.ReadLine();
//下移
for (int c = 0; c < demos.GetLength(1); c++)
{
//1.自下而上取出列元素
int[] cols = new int[demos.GetLength(0)];
for (int r = demos.GetLength(0)-1; r>=0; r--)
{
cols[3-r] = demos[r, c];
}
//2.元素合并
int[] mergedcols = merge(cols);
//3.将合并后的元素重新给二维数组赋值
for (int i = 0; i < mergedcols.Length; i++)
{
demos[3-i, c] = mergedcols[i];
}
}
Console.WriteLine("下移:");
print(demos);
Console.ReadLine();
//左移
for (int r = 0; r < demos.GetLength(0); r++)
{
//1.自左而右取出行元素
int[] rows = new int[demos.GetLength(1)];
for (int c = 0; c < demos.GetLength(1); c++)
{
rows[c] = demos[r, c];
}
//2.元素合并
int[] mergedrows = merge(rows);
//3.将合并后的元素重新给二维数组赋值
for (int i = 0; i < mergedrows.Length; i++)
{
demos[r, i] = mergedrows[i];
}
}
Console.WriteLine("左移:");
print(demos);
Console.ReadLine();
//右移
for (int r = 0; r < demos.GetLength(0); r++)
{
//1.自右而左取出行元素
int[] rows = new int[demos.GetLength(1)];
for (int c = demos.GetLength(1)-1; c >=0; c--)
{
rows[3-c] = demos[r, c];
}
//2.元素合并
int[] mergedrows = merge(rows);
//3.将合并后的元素重新给二维数组赋值
for (int i = 0; i < mergedrows.Length; i++)
{
demos[r, 3-i] = mergedrows[i];
}
}
Console.WriteLine("右移:");
print(demos);
Console.ReadLine();
}
运行效果如下: