我的问题是关于级别创建。我希望让关卡随机生成(并至少能够自己解决,以便可以给玩家提示),但我不知道要使用什么算法。 有什么建议吗?
注意:图像显示了Flow Free的目标,这也是我正在开发的目标。
谢谢你的帮助。 :)
我的问题是关于级别创建。我希望让关卡随机生成(并至少能够自己解决,以便可以给玩家提示),但我不知道要使用什么算法。 有什么建议吗?
注意:图像显示了Flow Free的目标,这也是我正在开发的目标。
谢谢你的帮助。 :)
g
中流出液体,填补那个空位。现在g
比这次迭代开始时多了一个方块。(谜题也已经被解决)f
的尾点重复上一步骤。目前的方法存在局限性(点将始终是相邻的),但易于扩展:
f
的主体,寻找更棘手的方法来交换空间和其他流...这里的总体解决方案可能不如您所期望的理想解决方案,但现在您有两个简单的算法,可以进一步完善,以发挥一个大而全面的算法的作用。最终,我认为这种方法是可管理的、不神秘的、易于调整的,如果没有别的,就是一个好的起点。
更新: 我根据上述步骤编写了一个概念验证。从下面的第一个 5x5 网格开始,该过程产生了随后的 5 个不同的棋盘。有些有趣,有些则不是,但它们总是合法的,且具有已知的一个解决方案。
起始点
5 种随机结果 (对于错位的截图表示抱歉)
为了充分说明,这里再来放一张8x8的随机图。起点跟上面那个简单列的方法一样。
h7
旁边没有其他头/尾。h0
旁边也没有其他头/尾。同样适用于 g0
、f0
、e0
、d11
、d0
、b11
等等。实际上,在16个头/尾中,我只数到5个是相连的。至于无聊的图形,你得到你所付出的代价。;) 答案展示了如何以可靠和易于接近的方式创建保证可解决的图形。让它们变得有趣则取决于开发人员。 - user1201210我的numberlink格式使用ASCII字符而不是数字。这里是一个例子:
$ bin/numberlink --generate=35x20
Warning: Including non-standard characters in puzzle
35 20
....bcd.......efg...i......i......j
.kka........l....hm.n....n.o.......
.b...q..q...l..r.....h.....t..uvvu.
....w.....d.e..xx....m.yy..t.......
..z.w.A....A....r.s....BB.....p....
.D.........E.F..F.G...H.........IC.
.z.D...JKL.......g....G..N.j.......
P...a....L.QQ.RR...N....s.....S.T..
U........K......V...............T..
WW...X.......Z0..M.................
1....X...23..Z0..........M....44...
5.......Y..Y....6.........C.......p
5...P...2..3..6..VH.......O.S..99.I
........E.!!......o...."....O..$$.%
.U..&&..J.\\.(.)......8...*.......+
..1.......,..-...(/:.."...;;.%+....
..c<<.==........)./..8>>.*.?......@
.[..[....]........:..........?..^..
..._.._.f...,......-.`..`.7.^......
{{......].....|....|....7.......@..
我将它输入我的求解器中(使用相同的种子):
$ bin/numberlink --generate=35x20 | bin/numberlink --tubes
Found a solution!
┌──┐bcd───┐┌──efg┌─┐i──────i┌─────j
│kka│└───┐││l┌─┘│hm│n────n┌o│┌────┐
│b──┘q──q│││l│┌r└┐│└─h┌──┐│t││uvvu│
└──┐w┌───┘d└e││xx│└──m│yy││t││└──┘│
┌─z│w│A────A┌┘└─r│s───┘BB││┌┘└p┌─┐│
│D┐└┐│┌────E│F──F│G──┐H┐┌┘││┌──┘IC│
└z└D│││JKL┌─┘┌──┐g┌─┐└G││N│j│┌─┐└┐│
P──┐a││││L│QQ│RR└┐│N└──┘s││┌┘│S│T││
U─┐│┌┘││└K└─┐└─┐V││└─────┘││┌┘││T││
WW│││X││┌──┐│Z0││M│┌──────┘││┌┘└┐││
1┐│││X│││23││Z0│└┐││┌────M┌┘││44│││
5│││└┐││Y││Y│┌─┘6││││┌───┐C┌┘│┌─┘│p
5││└P│││2┘└3││6─┘VH│││┌─┐│O┘S┘│99└I
┌┘│┌─┘││E┐!!│└───┐o┘│││"│└─┐O─┘$$┌%
│U┘│&&│└J│\\│(┐)┐└──┘│8││┌*└┐┌───┘+
└─1└─┐└──┘,┐│-└┐│(/:┌┘"┘││;;│%+───┘
┌─c<<│==┌─┐││└┐│)│/││8>>│*┌?│┌───┐@
│[──[└─┐│]││└┐│└─┘:┘│└──┘┌┘┌┘?┌─^││
└─┐_──_│f││└,│└────-│`──`│7┘^─┘┌─┘│
{{└────┘]┘└──┘|────|└───7└─────┘@─┘
dMin(orange) = 1
dMin(red) = 1
dMin(green) = 5
dMin(yellow) = 3
dMin(blue) = 5
U = 15 - (dMin(orange) + dMin(red) + dMin(green) + dMin(yellow) + dMin(blue))
由于这些单元格可以采用任何颜色,因此我们还可以确定具有特定颜色的单元格的最大数量:
dMax(orange) = dMin(orange) + U
dMax(red) = dMin(red) + U
dMax(green) = dMin(green) + U
dMax(yellow) = dMin(yellow) + U
dMax(blue) = dMin(blue) + U
在这个特定的例子中,U=0,因此每种颜色的单元格数量的最小值也是最大值。
如果我们要使用这些颜色约束来枚举所有可能的组合,那么我们需要担心的组合将少得多。更具体地说,在这个特定的例子中,我们会有:
15! / (1! * 1! * 5! * 3! * 5!)
= 1307674368000 / 86400
= 15135120 combinations left, about a factor 2000 less.
function SolveLevel(initialBoard of size NxN)
{
foreach(colour on initialBoard)
{
Find startCell(colour) and endCell(colour)
minDistance(colour) = Length(ShortestPath(initialBoard, startCell(colour), endCell(colour)))
}
//Determine the number of uncoloured cells remaining after all shortest paths have been applied.
U = N^(N^2 - 2N) - (Sum of all minDistances)
firstColour = GetFirstColour(initialBoard)
ExplorePathsForColour(
initialBoard,
firstColour,
startCell(firstColour),
endCell(firstColour),
minDistance(firstColour),
U)
}
}
function ExplorePathsForColour(board, colour, startCell, endCell, minDistance, nrOfUncolouredCells)
{
maxDistance = minDistance + nrOfUncolouredCells
paths = FindAllPaths(board, colour, startCell, endCell, minDistance, maxDistance)
foreach(path in paths)
{
//Render all cells in 'path' on a copy of the board
boardCopy = Copy(board)
boardCopy = ApplyPath(boardCopy, path)
uRemaining = nrOfUncolouredCells - (Length(path) - minDistance)
//Recursively explore all paths for the next colour.
nextColour = NextColour(board, colour)
if(nextColour exists)
{
ExplorePathsForColour(
boardCopy,
nextColour,
startCell(nextColour),
endCell(nextColour),
minDistance(nextColour),
uRemaining)
}
else
{
//No more colours remaining to draw
if(uRemaining == 0)
{
//No more uncoloured cells remaining
Report boardCopy as a result
}
}
}
}
现在只需要实现FindAllPaths(board, color, startCell, endCell, minDistance, maxDistance)方法。这里的难点在于,我们不仅要搜索最短路径,还要搜索符合minDistance和maxDistance范围内的任意路径。因此,我们不能只使用Dijkstra或A*算法,因为它们只记录到每个单元格的最短路径,而无法记录任何可能的绕路。
一种查找这些路径的方法是,在棋盘上使用多维数组,其中每个单元格都能够存储多个航点。一个航点被定义为一对(前一个航点,距离起点的距离)。需要前一个航点来重构整个路径,当我们到达目的地时,距离起点的距离则防止我们超出maxDistance。
通过从startCell开始向外进行类似洪水填充的探索,可以找到所有路径。对于给定的单元格,递归地探索每个未着色或与当前颜色相同的邻居,直到我们到达endCell或超过maxDistance(除了从起点到原点的路径)。
改进这种策略的一种方法是,我们不仅从startCell朝endCell外部探索,而且从startCell和endCell同时向外探索,使用Floor(maxDistance / 2)和Ceil(maxDistance / 2)作为各自的最大距离。对于较大的maxDistance值,这应该将探索的单元格数从2 * maxDistance^2降低到maxDistance^2。
.v<<.
v<...
v....
v....
首先,您需要翻转角落以填补边缘空间。
v<<<.
v....
v....
v....
v<<v.
v.^<.
v....
v....
v<<v.
>v^<.
v<...
v....
请注意,我所概述的方法并不能保证一定有解决方案,但如果存在解决方案,大多数情况下您应该能够找到一个解决方案。如果地图没有解决方案或算法无法找到解决方案,则可以放弃该地图并尝试其他地图 :)
你有两个选择:
我使用选项(2)生成Boggle类型的棋盘,非常成功。 如果您选择选项(2),请按照以下步骤操作:
所需工具:
解决:
10x10上的A*应在几百分之一秒内运行。 您可能可以每秒解决1k+个棋盘。 因此,10秒的运行时间应该能够得到几个“可用”的棋盘。
额外加分: