【C#】学习二维数组,2048核心算法,思路过程(26)
(从这里开始,以后的笔记都做的比较细,包括想法思路)
2048对应的就是一个二维数组
这个二维数组就是 int[,] 假设 map = new int[4,4];
int[,] map = new int[4,4];
我们现在讲的代码就是针对
上移 下移 左移 右移 的4个方法
一个操作对应一个方法
那么这四个方法要干什么事儿?
就是对 int[,] map = new int[4,4];操作
现在不用管2048那个数怎么生成
先不用去想它
你就想
有这么一个二维数组(int[,] map = new int[4,4];)
里面有数据
我们假想,打个比方
我给二维数组先赋个值
int[,] map = new int[4,4]{};
二维数组仍然具备初始化 + 赋值的写法
它在写起来的时候大家可以这么写
int[,] map = new int[4,4] { {} {} {} {} };
四行四列
第一行,瞎写哈
{2,2,4,8},
这是第一行,
第二行
{2,4,4,4},
第三行
{0,8,4,0},
第四行
{2,4,0,4}
好!就假写的哈
int[,] map = new int[4,4] { {2,2,4,8}, {0,8,4,0}, {2,4,4,4}, {2,4,0,4} };
这就是二维数组赋值
初始化+赋值
数组就是这样
一维也是这样
只不过一维数组是 {数据,数据,...}
而二维数组呢?
它有行有列
所以现在指明
几行:4行
所以就4个大括号
大括号之间用","隔开
最后一个是没有","的
几列:4列
OK了
二维数组还可以这么写
int[,] map = new int[4,4]{{2,2,4,8},{0,8,4,0},{2,4,4,4},{2,4,0,4}};
都写在一行,或者
int[,] map = new int[4,4] {{2,2,4,8},{0,8,4,0},{2,4,4,4},{2,4,0,4}};
这么写也行
但是没有下边这个明了,
一看就知道几行几列
int[,] map = new int[4,4] { {2,2,4,8}, {0,8,4,0}, {2,4,4,4}, {2,4,0,4} };
在做这个游戏的时候可以这么做
先做一个二维数组
瞎写点儿数据
然后针对这个二维数组
做上移 下移 左移 右移的操作
我们要完成制作这个游戏的逻辑
逻辑,我们用代码把它写出来
逻辑就是什么呢?思想
上下和左右的思想是一样的
就是代码有点儿不同
上下移是以列为单元

列和列之间没有联系
它是以列为单位的
左右移是以行为单位
上移
有几个大的步骤
--获取列的数据
一个2048是一个数组
那么一列是什么?
是4个数,我们可以把它当做一维数组
所以说,当我们探讨一列的问题的时候
就可以吧这个二维数组问题变成一维数组问题
也就是获取列数据,形成一维数组
在接下来怎么合,我们只需要考虑一维数组就行了
第二步遇到相同的合并
合并数据
再具体点儿数据怎么合
--相同相邻,则合并(将后一个元素累加到前一个元素上/或者前一个元素 x 2)
但是现在有个问题

这两个元素不相邻,还要合
你得无视0
这无视0有怎么做.
现在我们拿到的就是一维数组,
比如说
2 2 0 0 满足相邻相同条件,
我们要把它变成
4后面的要变成0
2 2 0 0 --》4 0 0 0
所以合并还要干一个事儿
就是后一个元素清零
如果是2220这个数据该怎么办?
一定要做两件事
一个是累加
一个是清零
不清零后面就会继续合并,出错
2 2 2 0 --》合并清零后变成 4 0 2 0 但是这个结果也不是我们想要的结果,我们想要的是-->4 2 0 0
2 2 2 0 --》4 0 2 0 --> 4 2 0 0
当然还有这个问题
2 0 2 0
这个代码怎么写?2 2相同但不相邻,中间隔着一个0
你还要合
所以这个事儿要怎么办?
合并之前遇到0了,这个0要怎么处理,怎么无视它
可以理解为把0移到后面去
也可以理解为把有用的移到前面来
所以遇到这个情况,按照游戏规则
先要把2提前变成 2 2 0 0 ,接下来再做合并--> 4 0 0 0
2 0 2 0 --》2 2 0 0 --> 4 0 0 0
合并之前先把0元素移动到末尾:简称 去零
而合并完成的时候还有可能出现元素间有0的情况
所以合并后仍然需要去零
到现在为止还不需要写代码
因为到现在为止你要确定自己的思路是正确的,然后再写代码
因为你的思路是错误的写出来的代码也不一定正确
这个思路对不对,能不能满足条件
那你就要再去举例子
比如说 2 0 2 4
按照这个游戏的套路,先去零变成 2 2 4 0
接下来根据相邻相同合并成4 ,后一个清零0,然后是 4 0
4040也不是我们想要的,再去零变成 4 4 0 0
2 0 2 4 --》2 2 4 0 --》4 0 4 0--> 4 4 0 0
OK了
多举几个例子,发现这个方法可行
可行再去写代码
合并是针对一维数组的操作
咱们要做的是二维数组
所以还需要一步
将一维数组元素还原至原列

如图
上移下移的排列结果是不同的
和上移的区别在于合并这一步
去零:把零移到上方开头
判断相邻相同 合并(把前一个元素累加到后一个元素上,然后将前一个元素清零)
然后去零:把零移到上方开头
将一维数组元素还原至原列
上移下移有很多共用的地方
不用将所有代码
上写一遍
下写一遍
尽量的把它们之间共用的东西提出来
放到一起
提不到一起,这件事儿就麻烦了
做很多事儿都麻烦
比如说,我们移动的时候都要统计
哪些方格做了合并
我需要在这些方格上做特效
做动画
让这些方格由大变小
怎么去统计呢
四个方法统计四遍
但是如果你能够把这四个方法共用的地方结合成一个点
那你这个事儿只需要做一遍就行了
具体的合并细节我们都知道
那我们能不能这样
保证上下合并都按照一个套路去做
按照一个什么套路呢?
就都是把0元素移至末尾
那我下移也这么做行不行?
现在看上去不行
它就是要把0移至上面去
那怎么办才能把它们合到一起呢?
咱们是不是可以在获取数据这里把它反过来?
上移取数据是从上到下的获取,形成一维数组
下移取数据是从下到上的获取,形成一维数组
把获取数据的方向变了,对一维数组的处理就变成一样的了
所以说咱们为了达到这个作用

比如说 就这个数据
就这一列
上移怎么办
从上到下取
拿到的一维数组是 8 2 0 2 ,对它做处理8202==》8220
从下到上呢 2 0 2 8
处理的方式都一样,把0元素移至末尾
2028==》2280
紧接着2280把数据还回去
变成这样

也就是取数据的方式变一下,这样合并逻辑就能一样
一样了就能共用
就能抽出这四个方法里的共用代码
思路是这样了具体实施
1.定义去零方法(针对一维数组):将0元素移至末尾
//做这个去零方法的时候你是针对一维数组做操作的
2.合并数据方法(针对一维数组)
去零:把零移到末尾
判断相邻相同 合并(把后一个元素累加到前一个元素上,然后将后一个元素清零)
去零:把零移到末尾
3.上移:
从上到下获取数据,形成一维数组
调用合并数据方法
将一维数组元素,还原至原列
4.下移:
从下到上获取数据,形成一维数组
调用合并数据方法
将一维数组元素,还原至原列
5.左右移思路同上下移
去零
怎么把零元素移动到末尾
private static int [] RemoveZero(int[] array) { }
//讲一下思路
//现有数组 2002
//0000
//将非零元素依次赋值给新一维数组
//一个一个来
//这么做的话,等同把零元素移动到末尾
//先创建一个新数组
//新数组的长度和老数组是一样的
int[] newArray = new int[array.Length];
//但是这个数组里所有元素都是0000
//然后将非零元素拷过来, 一个一个的拷
for (int i = 0; i < array.Length; i++) { if(array[i] !=0) newArray[?]=array[i]; }
//如果array[i]不等于零,就把它赋值给newArray[?]
//"?"是什么,是0.1.2.3.....
//再声明一个变量 int index=0;
private static int [] RemoveZero(int[] array) { int[] newArray = new int[array.Length]; int index=0; for (int i = 0; i < array.Length; i++) { if(array[i] !=0) newArray[index++]=array[i]; } }
//不满足条件index不增加,满足条件index才增加
//这个新的数组就是我们想要的
//接下来return newArray;
private static int [] RemoveZero(int[] array) { int[] newArray = new int[array.Length]; int index=0; for (int i = 0; i < array.Length; i++) { if(array[i] !=0) newArray[index++]=array[i]; } return newArray; }
//结束
合并
private static int [] Merge(int[] array) { }
按照我们之前的思路,首尾去零,相邻相同合并
先把数组去零RemoveZero(array)
再把返回值给array
private static int [] Merge(int[] array) { array = RemoveZero(array);//首 array = RemoveZero(array);//尾 }
首尾去零, 中间合并
相邻相同
例如:
2202去零后2220
2和2作比较, 2和2作比较 ,2和0作比较
22 20 2 22 0 22 20
第一次 第二次 第三次
一共4个元素做了3次比较,所以是:array.Length-1
for (int i = 0; i < array.Length-1; i++) { }
第一次如果array[i]和后一个array[i+1]相等(就是相邻相同)
后一个磊加到前一个上面array[i] += array[i+1]
然后后一个array[i+1]归零
for (int i = 0; i < array.Length-1; i++) { if(array[i] == array[i+1]) { array[i] += array[i+1]; array[i+1] = 0; } }
这就是之前分析的逻辑
最后 return array;
private static int [] Merge(int[] array) { array = RemoveZero(array); for (int i = 0; i < array.Length-1; i++) { if(array[i] == array[i+1]) { array[i] += array[i+1]; array[i+1] = 0; } } array = RemoveZero(array); return array; }
还有一种情况就是如果去零后相邻两个元素都是0的情况不合并
要在if(array[i] == array[i+1])这里再加一个条件array[i] != 0
private static int [] Merge(int[] array) { array = RemoveZero(array); 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; } } array = RemoveZero(array); return array; }
这种虽然最后结果是一样的但是一些情况下还是不一样的
比如 ,统计合并位置
所有合并位置需要做一个统计,然后把它作为一个动画由大到小等等的效果
那我怎么统计
00合并算作统计数量内就是错误的
那么这里就需要一个非零的条件
上移
private static int [] MoveUp(int[] map)
{
}
从上到下 获取列数据 ,形成一维数组
0,0 0,1 0,2 0,3
1,0 1,1 1,2 1,3
2,0 2,1 2,2 2,3
3,0 3,1 3,2 3,3
每列变化的就是行数,列数0
for (int r = 0;r < map.GetLength(0); r++) { }
然后在里面干什么事儿啊
获取
获取给谁?
新的数组,我们做一个一维数组
int [] mergeArray = new int[map.GetLength(0)]; for (int r = 0;r < map.GetLength(0); r++) { mergeArray[r] = map[r,0]; }
拿到第一列数据后,
调用合并方法
传递数组
接收返回值mergeArray = Merge(mergeArray);
int [] mergeArray = new int[map.GetLength(0)]; for (int r = 0;r < map.GetLength(0); r++) { mergeArray[r] = map[r,0]; } mergeArray = Merge(mergeArray);
最后怎么拿的数据再怎么还回去
mergeArray[r] = map[r,0];方向变一下map[r,0] = mergeArray[r];
int [] mergeArray = new int[map.GetLength(0)]; for (int r = 0;r < map.GetLength(0); r++) { mergeArray[r] = map[r,0]; } mergeArray = Merge(mergeArray); for (int r = 0;r < map.GetLength(0); r++) { map[r,0] = mergeArray[r]; }
第二列,第三列,第四列依次变化的是列数
相同的代码要运行很多次
所以在外面套一层for循环
把代表列的0换成c
最后retern map;
int [] mergeArray = new int[map.GetLength(0)]; for (int c = 0; c < map.GetLength(1); c++) { for (int r = 0;r < map.GetLength(0); r++) { mergeArray[r] = map[r,c]; } mergeArray = Merge(mergeArray); for (int r = 0;r < map.GetLength(0); r++) { map[r,c] = mergeArray[r]; } retern map; }
private static int [] MoveUp(int[] map) { int [] mergeArray = new int[map.GetLength(0)]; for (int c = 0; c < map.GetLength(1); c++) { for (int r = 0;r < map.GetLength(0); r++) { mergeArray[r] = map[r,c]; } mergeArray = Merge(mergeArray); for (int r = 0;r < map.GetLength(0); r++) { map[r,c] = mergeArray[r]; } retern map; } }
private static int [] MoveDown(int[] map) { }
0,0 0,1 0,2 0,3
1,0 1,1 1,2 1,3
2,0 2,1 2,2 2,3
3,0 3,1 3,2 3,3
从上到下 获取列数据 ,形成一维数组
int [] mergeArray = new int[map.GetLength(0)];
这个循环从下到上写
从3开始所以int r = map.GetLength(0)-1
for (int r = map.GetLength(0)-1;r >=0; r--)
mergeArray[?] = map[r,0];//第一列
"?"里面填什么?
从头到尾存入一维数组
你得正着往里存,最后才能保证一维数组的值是对的
0,1,2,3的往里存,那0,1,2,3跟r是什么关系?
r第一次循环是3,3-3==0;
r第二次循环是2,3-2==1;
r第三次循环是1,3-1==2;
r第四次循环是0,3-0==3
mergeArray[3-r] = map[r,0];
然后接下来,合并
mergeArray = Merge(mergeArray);
然后,还原,怎么拿的怎么还,
再换一下位置
for (int r = map.GetLength(0)-1;r >=0; r--) map[r,0] = mergeArray[3-r] ;
整理一下
private static int [] MoveDown(int[] map) { int [] mergeArray = new int[map.GetLength(0)]; for (int r = map.GetLength(0)-1;r >=0; r--) mergeArray[3-r] = map[r,0]; mergeArray = Merge(mergeArray); for (int r = map.GetLength(0)-1;r >=0; r--) map[r,0] = mergeArray[3-r] ; }
这样一列就做完了
接着是剩下的几列做个for循环
private static int [] MoveDown(int[] map) { int [] mergeArray = new int[map.GetLength(0)]; 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]; mergeArray = Merge(mergeArray); for (int r = map.GetLength(0)-1;r >=0; r--) map[r,c] = mergeArray[3-r] ; } return map; }
现在回过来看
上下的方法在做什么?
在拿数据,还数据
合并干什么?
就是针对一维数组,根据之前分析的需求,改里面的数据,
去零干嘛?
这是一维数组,把里面的数据,零元素移到后面

浙公网安备 33010602011771号