我怎么知道我的益智游戏是否总是可能的?


68

我做了一种益智游戏,目的是摆脱所有白色瓷砖。您可以在问题末尾尝试一下。

每次,该板都是在5 * 5网格上随机放置的白色瓷砖随机生成的。您可以单击该网格上的任意图块,它将切换其颜色以及所有与之接触的图块的颜色。我的困境是我不知道这是否会产生不可能的局面。检查这种事情的最好方法是什么?

function newgame() {
 moves = 0;
    document.getElementById("moves").innerHTML = "Moves: "+moves;

  for (var i = 0; i < 25; i++) {
   if (Math.random() >= 0.5) {
$(document.getElementsByClassName('block')[i]).toggleClass("b1 b2")
   }
}
}
newgame();
function toggle(a,b) {  
  moves += 1;
  document.getElementById("moves").innerHTML = "Moves: "+moves;
$(document.getElementsByClassName('block')[a+(b*5)]).toggleClass("b1 b2");

if (a<4) {$(document.getElementsByClassName('block')[(a+1)+(b*5)]).toggleClass("b1 b2")}
  
  
if (a>0) {$(document.getElementsByClassName('block')[(a-1)+(b*5)]).toggleClass("b1 b2")}
  
  
if (b<4) {$(document.getElementsByClassName('block')[a+((b+1)*5)]).toggleClass("b1 b2")}
  
if (b>0) {$(document.getElementsByClassName('block')[a+((b-1)*5)]).toggleClass("b1 b2")}
}
body {
  background-color: #000000;
}

.game {
  float: left;
  background-color: #000000;
  width: 300px;
  height: 300px;
  overflow: hidden;
  overflow-x: hidden;
  user-select: none;
  display: inline-block;
}

.container {
  border-color: #ffffff;
  border-width: 5px;
  border-style: solid;
  border-radius: 5px;
  width: 600px;
  height: 300px;
  text-align: center;
}

.side {
  float: left;
  background-color: #000000;
  width: 300px;
  height: 300px;
  overflow: hidden;
  overflow-x: hidden;
  user-select: none;
  display: inline-block;
}

.block {
  transition: background-color 0.2s;
  float: left;
}

.b1:hover {
  background-color: #444444;
  cursor: pointer;
}

.b2:hover {
  background-color: #bbbbbb;
  cursor: pointer;
}

.row {
  width: 300px;
  overflow: auto;
  overflow-x: hidden;
}

.b1 {
  display: inline-block;
  height: 50px;
  width: 50px;
  background-color: #000000;
  border-color: #000000;
  border-width: 5px;
  border-style: solid;
}




.b2 {
  display: inline-block;
  height: 50px;
  width: 50px;
  background-color: #ffffff;
  border-color: #000000;
  border-width: 5px;
  border-style: solid;
}



.title {
  width: 200px;
  height: 50px;
  color: #ffffff;
  font-size: 55px;
  font-weight: bold;
  font-family: Arial;
  display: table-cell;
  vertical-align: middle;
}

.button {
  cursor: pointer;
  width: 200px;
  height: 50px;
  background-color: #000000;
  border-color: #ffffff;
  border-style: solid;
  border-width: 5px;
  color: #ffffff;
  font-size: 25px;
  font-weight: bold;
  font-family: Arial;
  display: table-cell;
  vertical-align: middle;
  border-radius: 5px;
  transition: background-color 0.3s, color 0.3s;
}

.button:hover {
  background-color: #ffffff;
  color: #000000;
}

.sidetable {
  padding: 30px 0px;
  height: 200px;
}


#moves {
  width: 200px;
  height: 50px;
  color: #aaaaaa;
  font-size: 30px;
  font-weight: bold;
  font-family: Arial;
  display: table-cell;
  vertical-align: middle;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<center>
  <div class="container">
  
  
  <div class="game"><div class="row"><div onclick="toggle(0,0);" class="block b1"></div><div onclick="toggle(1,0);" class="block b1"></div><div onclick="toggle(2,0);" class="block b1"></div><div onclick="toggle(3,0);" class="block b1"></div><div onclick="toggle(4,0);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,1);" class="block b1"></div><div onclick="toggle(1,1);" class="block b1"></div><div onclick="toggle(2,1);" class="block b1"></div><div onclick="toggle(3,1);" class="block b1"></div><div onclick="toggle(4,1);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,2);" class="block b1"></div><div onclick="toggle(1,2);" class="block b1"></div><div onclick="toggle(2,2);" class="block b1"></div><div onclick="toggle(3,2);" class="block b1"></div><div onclick="toggle(4,2);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,3);" class="block b1"></div><div onclick="toggle(1,3);" class="block b1"></div><div onclick="toggle(2,3);" class="block b1"></div><div onclick="toggle(3,3);" class="block b1"></div><div onclick="toggle(4,3);" class="block b1"></div></div><div class="row"><div onclick="toggle(0,4);" class="block b1"></div><div onclick="toggle(1,4);" class="block b1"></div><div onclick="toggle(2,4);" class="block b1"></div><div onclick="toggle(3,4);" class="block b1"></div><div onclick="toggle(4,4);" class="block b1"></div></div></div>
    
    <div class="side">
      <center class="sidetable">
        <div class="title">Tiles</div>
        <br>
        <div class="button" onclick="newgame()">New Game</div>
        <br><br>
        <div id="moves">Moves: 0</div>
      </center>
    </div>
    
  </div>
    </center>


9
如果您对这种益智游戏感兴趣,请查看Simon Tatham的Portable Puzzle Collection。除了这种类型(在那里称为Flip),您还可以找到许多日语和其他拼图的变体。一切都在BSD许可下进行,可能很有趣。
Dubu

10
逆向工程怎么样?从空白板开始,然后进行自动化操作,例如随机点击20次。这样一来,您知道最后必须有解决方案。
AJFaraday

3
我想继续比赛,但是由于您的问题,我是否会真正获胜的不确定性正在困扰着我!有趣的游戏:)
MrDuk '18

@MrDuk codepen.io/qwertyquerty/pen/WMGwVW这是完成的项目!将此固定并抛光。我还制作了一个电子应用程序。
Qwerty

@Qwerty尝试以全页视图查看笔时,收到消息“此笔的所有者需要验证其电子邮件地址才能启用“全页视图”。” 请在CodePen上验证您的电子邮件地址,以便我可以在全窗口中欣赏您的游戏!:)
stephenwade

Answers:


161

在这种类型的游戏中,两次执行相同的举动会将棋盘调回其先前状态。因此,要确保一块木板是可解决的,请通过反向游戏来生成它。从一个已解决的(空白)板开始,然后以编程方式随机“单击”一定次数或直到该板具有所需数量的白方块为止。一种解决方案是简单地以相反的顺序执行相同的动作。可能存在其他更短的解决方案,但可以保证您至少有一个。

另一个更复杂的解决方案是定义一种解决算法,该算法从您的起始位置开始遍历所有可能的游戏状态,以尝试找到解决方案。这将花费更长的时间来实施和运行,但是将允许真正随机生成这些板。我不会详细介绍该解决方案,因为它不是一个好主意。


22
@Qwerty:对于您的特定问题,单击相同的正方形两次将自身取消,因此,没有任何理由要单击任意一个正方形一次以上。您可能希望选择一定数量的正方形以单击而无需重复,或者考虑一种解决方案,将XX%的点击机会分配给板上的每个正方形。(Ed:好答案,+ 1!)
Jeff Bowman '18

3
我之前制作了几乎完全相同的游戏,最终使用了这种方法。一开始我包含了一个动画,显示了从已解决状态到未解决状态的快速变化。很漂亮
Jared Goguen '18

1
@JaredGoguen很奇怪,我添加了它,然后回到这里查看您的评论。
Qwerty

4
@JeffBowman的确,可解决游戏的集合可以视为25位值,每个位对应于一个平方,该平方即为它被翻转到mod 2的次数。因此,一个人可以生成0范围内的随机数。 .33,554,432,然后从中按顺序计算板上的每个正方形的值。
蒙蒂·哈德

7
尽管这是对如何回答该问题的数学问题的正确答案,但从设计的角度来看,这通常是一种可疑的做法。这种生成方式,没有任何特别的计划,通常会导致感到非常“困惑”的难题,而没有任何特定的兴趣点或任何统一的主题。可以为拼图游戏“以程序方式生成”有趣的问题实例,但通常需要更努力地了解拼图的有趣特征是什么。
史蒂文·斯塔德尼克

92

尽管以上答案很聪明(无论如何我也可能会这样做),但这款特定的游戏还是众所周知的。它叫做Lights Out,并且已经在数学上解决了。只有且仅当各种元素的两个总和(在Wikipedia页面上给出)加到零模2(即偶数)时,才有解决方案。通常,一些线性代数应该为任何棋盘上的游戏提供相似的解条件。


2
这是一种悲哀地发现,它已经被提出。我以为我正在做某事。
Qwerty

39
@Qwerty最初的想法很少,您当然不需要一个想法就可以成功(cf Rovio,King)。
OrangeDog

14
这个特殊的游戏存在,但是您可以随时扩展这个想法!添加更多功能!为单击某处时发生的事情添加不同的规则,例如根据激活/停用的方向将颜色加在一起。添加您必须使用的不同“工具”。添加非矩形板!有很多有趣的事情要做。请记住,此举必须始终扭转自己。
Ed Marty

7
@OrangeDog:甚至“ Lights Out”也不是原创的,那只是90年代流行的品牌名称。举例来说,维基百科文章列出了这个这个
BlueRaja-Danny Pflughoeft

1
您将哪些答案称为“以上答案”?完全不清楚,因为在我的屏幕上,您的答案上方只有一个。请记住,答案会根据投票和用户选项改变顺序。您应该始终链接到特定的答案,而不是引用“上方”的内容。
David Richerby

13

生成谜题时走另一条路。

从空白的石板开始,然后选择这些瓦片,而不是随机选择这些瓦片并将其从白色变成黑色,而是选择该瓦片,而不是将变为黑色,就像用户选择了它一样,导致所有其他瓦片翻转周围。

这样,您将获得至少一个解决方案:用户将不得不撤消您的“ AI”玩家创建关卡的操作。


7

Ed和Alexandre拥有此权利。

但是,如果您确实想知道每种解决方案是否都可行,则有很多方法。

数量有限的难题

两次单击相同的正方形会产生与根本不单击相同的结果,无论它们之间单击了多少次。这意味着可以通过为每个正方形赋予二进制值“单击”或“未单击”来描述每个解决方案。类似地,可以通过给每个方块一个二进制值“切换”或“不切换”来描述每个难题。这意味着有2 ^ 25个可能的难题和2 ^ 25个可能的解决方案。如果您可以证明每种解决方案都能解决一个独特的难题,那么每个难题都必须有解决方案。同样,如果您找到两个解决同一难题的解决方案,那么就不可能对每个难题都找到解决方案。

另外,2 ^ 25是33,554,432。数量很多,但这不是一个难以控制的数字。一个好的算法和一台像样的计算机可能会在几个小时之内强加于人,尤其是当您考虑到一半的难题是另一半的难题时。


4
超过一半是“逆”-除了水平反射之外,您还有垂直反射和旋转。
Clockwork-Muse

@ Clockwork-Muse,是的,但是要计算出确切的数字比较困难,因为尽管非对称设计可以按8个排列进行旋转和翻转,但对称设计的排列却较少。因此,我只提到了白色/黑色反相,因为每个解决方案都具有1个逆。(尽管要使倒数有效,您必须证明可以翻转整个棋盘)
奥术家狼疮

事实证明,正如罗伯特·马斯特拉格斯蒂诺(Robert Mastragostino)在回答中提到的那样,这实际上是一个众所周知的,经过充分研究的问题。每个可解决的难题都有确切的4个解决方案,并且大多数随机棋子都不可解决。搜索所有空间可能很有趣,但是由于已经存在一个证明(math.ksu.edu/math551/math551a.f06/lights_out.pdf),因此您也可以做几个点积,并在几个点中得到相同的答案。微秒。:)
GrandOpener

数学时间:如果要计算所有独立的板的数量(不考虑可溶性),那么要考虑所有对称性,Burnside的引理就是解决方法:有16个对称性(一个琐碎,三个旋转,四个反射,然后这8个中的每一个都与开/关的反转相结合),并且对于每种对称性,某些板的数量完全不变。如果将每个对称的完全不变的电路板取平均值,则等于不同的电路板数。
亚瑟

1
@PeterTaylor与运行结果相比,对模拟器进行编码肯定会花费更长的时间。
corsiKa

4

广义答案:

  1. 创建一个大小为(移动数量)x(发光数量)的矩阵。
  2. 如果进行与该行相对应的移动会切换与该列相对应的灯光,则在单元格中放入1,否则将其设为0。
  3. 对矩阵执行高斯-约旦消除(模2)。
  4. 如果生成的矩阵在每一列中只有一个1,而每一行最多只有一个1,则每个可解决的网格。

1

其他人已经提到寻找随机生成的拼图是否可以解决的方法。您还应该问的一个问题是,您是否真的想要随机生成的谜题。

随机生成的拼图都具有相同的核心缺陷:难度几乎是不可预测的。您可能遇到的难题范围从已经解决到琐碎(解决方案显而易见)到困难(解决方案不明显)再到不可能(难题根本无法解决)。因为难度是不可预测的,所以这会给玩家带来不令人满意的体验,尤其是当他们连续做多个难题时。他们极不可能获得平滑的难度曲线,这会使他们感到无聊或沮丧,这取决于他们得到什么难题。

随机生成的另一个问题是拼图初始化所花费的时间是不可预测的。一般而言,您将(几乎)立即获得一个可解决的难题,但是如果运气不好,您随机生成的难题可能最终会遇到无法解决的难题。

解决这两种问题的一种方法是,通过将每个可用的可解决难题的预定义矢量安排到难度组中,然后根据难度从可解决难题中选择一个随机难题。这样,您将确定每个难题都是可以解决的,难度是可以预见的,并且生成将在固定时间内完成。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.