变色方块——解决算法

瞎猫碰上死耗子之——迭代随机算法

随机点击方块,只要次数足够多,总有一次会成功。
以3*3为例,定义一个数组

1
const items = [0,1,2,3,4,5,6,7,8];

定义一个方法,随机从数组items中取出一个数,点击该数字对应的方格,不断迭代,直到所有方格变成蓝色。使用延时函数setTimeout是为了能观察到每一次调用函数所产生的变化。steps数组用于记录每次取出的数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const steps = [9];
function auto(Oli){
let item = items[Math.floor(Math.random()*items.length)];
if(item!=steps[steps.length-1]){
steps.push(item);
}
if(!flag){
setTimeout(function(){
allLryb(item,Oli,3);
auto9(Oli);
},0);
flag = exam(Oli,3);
}else{
console.log(steps);
}
}
auto(Oli);

利用这个方法理论上可以解决所有关卡,只是所需时间长短的问题。在实际操作过程中,33最少需要取出159个数,44需要取出1700+个数。而6*6运行了两个小时都无法解决。除了这一点,这个方法还有一个缺陷——即便解出来了,也不能直接得到解开关卡的正确步骤。
完整代码移步:github

发现规律之——组合算法

通过对11~66的解法的探索,我发现,只要每一个关卡都有固定的方块,只要点击正确的方块,无论顺序都可以解开。在深入研究之后,又发现,只要第一行的方块选对了,然后依次点击未变色方块下一行所对应的方块,就可以解决。以44为例。如图所示,我为它们分别编上编号。图为已点击0和1所出现的效果。
图一
先观察第一行,0,1,3是橙色,所以,下面我们应该点击它们下一行所对应的方格即,4,5,7;效果图如下所示:
图二
观察第二行,6是橙色,所以点击第三行所对应的方块,即10.效果图如下:
图三
观察第三行,9,11是橙色,所以点击第四行所对应的方块,即13和15。
当点击完最后一行,若所有方格都变成蓝色,则说明第一行所选择的方块是正确的。若还有方块是橙色,则说明第一行选的方块不对。根据已解出的关卡,可知,从6
6开始,第一行所选的方格是左右对称的。下图分别为解开66,77,88第一行所选的方格。
图四
所以,只需要得出其中一半的方格,然后进行对称即可。以10
10为例。
假设第一行所需要点击的方块是4块,所以左部分五个方块被点击方块是2块。

1
2
3
var num = 10;
var need = 2;
var atual = num/2;

定义一个数组,存储所有组合

1
var arr = [];

定义一个功能,算出从5个数里面取出2个数的所有组合数。关于组合算法,我采用以下思路。以5取2为例。
被取数为1,否则为0。
方法 :将10组合改为01 => 将其左侧的1全部移动到最左 => 所有的1都位于最右侧,完成所有组合
1 1 0 0 0
1 0 1 0 0
0 1 1 0 0
1 0 0 1 0
0 1 0 1 0
0 0 1 1 0
1 0 0 0 1
0 1 0 0 1
0 0 1 0 1
0 0 0 1 1
代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function zuhe(arr,actual,need){
var sonArr = [];
var flag2 = false;
//初始化,如5取2,则初始化为 1 1 0 0 0
for(var i= 0;i<actual;i++){
if(i<need){
sonArr.push(1);
}else{
sonArr.push(0);
}
}
var temp =sonArr.concat();
//将该组合放到arr里面去;
arr.push(temp);
while(!flag2){
//将10变成01 并将左侧的1移动到最左侧;
for(var i = 0;i<sonArr.length;i++){
if(sonArr[i] == 1 && sonArr[i+1] ==0){
sonArr[i] = 0;
sonArr[i+1] =1;
var sum = 0;
for(var j=0;j<i;j++){
if(sonArr[j] ==1){
sonArr[j] = 0;
sonArr[sum] = 1;
sum++;
}
}
break;
}
}
temp =sonArr.concat();
arr.push(temp);
//如果1全部移动到最右侧,则完成组合;
for(var k=sonArr.length-need;k<sonArr.length;k++){
if(sonArr[k]==1){
if(k==sonArr.length-1){
flag2 = true;
}
}else{
flag2 =false;
break;
}
}
}
}

得到5取2的所有组合之后,需要将数组进行倒序排列,并与原数组进行拼接。例如 1 1 0 0 0,对它进行倒序,得到 0 0 0 1 1,拼接得到 1 1 0 0 0 0 0 0 1 1 1
javascript中自带对数组进行倒序排列的函数:reverse

1
2
3
4
5
var add=0;
for(var i=0;i<arr.length;i++){
var temp = arr[i].concat().reverse();
arr[i] = arr[i].concat(temp);
}

绑定“辅助方块”这个按钮的点击事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let Ohelp = document.getElementById('help');
var flag1 = 0;
Ohelp.onclick = function(){
if(flag1 ==1){
changeBox(num,Oli);
flag1=0;
}else{
if(add<arr.length){
//让第一行发生变化
for(var j=0;j<arr[add].length;j++){
if(arr[add][j]==1){
allLryb(j,Oli,num);
}
}
//让剩下的行发生变化
check(num);
if(check_flag){
console.log(arr[add-1]);
}
add++;
}
flag1 = 1;
}
}

点击辅助方块,从arr二维数组中取出一列一维数组,并按照该一维数组去改变方块。例如[1 1 0 0 0 0 0 0 1 1],即出现点击第 0 、1、8、9块方块的效果。然后调用check()方法,让剩下的方块产生变化,直至最后一行。若方块没有全部变成蓝色,则证明该数组不是正确的数列。此时,再点击“辅助方块”按钮,则将回到初始化状态,即全部变成橙色。再点击“辅助方块”,则取出下一组数组进行测试。
该算法还存在一定的缺陷,要一个一个的点击进行测试,如果没有正确的数组则改变need的值。当数值较小时,是比较容易进行操作的,因为情况不多。但是数值太大时就不太现实了。
完整代码移步:github

您的支持将鼓励我继续创作!