(对不起,我把它放在GameDev.Stackexchange上并且它没有获得太多的流量,所以我想我也会在这里尝试。)
我正在编写一个简单的基于拼图的益智游戏,而且我一直试图找出一个寻路算法。
以下是游戏的制定方式:
董事会看起来像这样:
(反应堆是圆形;其他瓷砖没有特殊属性。)
我需要做的是:从反应堆开始,追踪沿着与反应堆相同颜色的相邻瓷砖的最长路径。像这样:
蓝色反应堆很简单(ish),因为它的路径不分支。但是,正如您从绿色反应堆的起始位置可以看到的那样,它的路径可以在开始时(向上或向下)分支两种方式,并在中途绕行。
我正在寻找的路径是最长的路径,因此它是屏幕抓取中突出显示的路径(第一条路径只穿过两个磁贴,中途绕行导致分拣机路径)。
当满足某些条件时,反应堆将使最长路径中的所有瓷砖(图中箭头所在的位置)消失并替换为新的。所有其他瓷砖将保留在原位,包括与绿色反应堆路径相邻的外来绿色瓷砖。
瓷砖存储在2D阵列的近似值中(Swift目前还没有强大的原生实现,所以我使用this tutorial中描述的那个)。他们使用tile[column, row]
检索。
在朋友的帮助下,我已经编写了一个递归函数,应该返回最长的路径。它正确地循环,但它没有从longestPath
数组中修剪较短的分支(例如,最长的路径将包括反应堆下方的2瓦分支,以及单个分支在拱顶处绕行。)
任何人都可以在此代码中看到我出错的地方吗?
这里是递归函数:
func pathfinder(startingTile: Tile, pathToThisPoint: Chain, var iteration: Int? = 1) -> Chain
{
var longestPath: Chain? = nil
var availableTiles = getNeighbouringTiles(startingTile)
for var nextTile = 0; nextTile < availableTiles.count; nextTile++
{
let column = availableTiles[nextTile].column
let row = availableTiles[nextTile].row
if tiles[column, row]!.tileType == startingTile.tileType && (tiles[column, row]!.isReactor == false || startingTile.isReactor)
{
// if we haven't been here before
if !pathToThisPoint.tiles.contains(tiles[column, row]!)
{
print(iteration)
iteration = iteration! + 1
// add this tile to the pathtothispoint
// go to the next unexplored tile (recurse this function)
pathToThisPointaddTile(tiles[column, row]!)
let tempPath = pathfinder(tiles[column, row]!, pathToThisPoint: pathToThisPoint)
// if the resulting path is longer...
if tempPath.length > longestPath.length
{
// then tempPath is now the longest path
for var i:Int = 0; i < tempPath.length; i++
{
let tile = Tile(column: pathToThisPoint.tiles[i].column, row: pathToThisPoint.tiles[i].row, tileType: pathToThisPoint.tiles[i].tileType)
longestPath?.addTile(tile)
}
}
}
}
if longestPath != nil
{
return longestPath!
}
else
{
return pathToThisPoint
}
}
它依赖于getNeighboringTiles函数(如下所示),该函数返回相同类型的有效tile数组,不包括反应器:
func getNeighbouringTiles(tile: Tile, previousTile: Tile? = nil) -> Array<Tile>
{
var validNeighbouringTiles = Array<Tile>()
var neighbourTile: Tile
// check top, right, bottom, left
if tile.row < NumRows - 1
{
neighbourTile = tiles[tile.column, tile.row + 1]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
if tile.column < NumColumns - 1
{
neighbourTile = tiles[tile.column + 1, tile.row]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
if tile.row > 0
{
neighbourTile = tiles[tile.column, tile.row - 1]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
if tile.column > 0
{
neighbourTile = tiles[tile.column - 1, tile.row]!
if neighbourTile.tileType == tile.tileType && !neighbourTile.isReactor && (previousTile == nil || previousTile != neighbourTile)
{
validNeighbouringTiles.append(neighbourTile)
}
}
// if we get this far, they have no neighbour
return validNeighbouringTiles
}
Tile类看起来像这样(为简洁起见省略了方法):
class Tile: CustomStringConvertible, Hashable
{
var column:Int
var row:Int
var tileType: TileType // enum, 1 - 4, mapping to colors
var isReactor: Bool = false
// if the tile is a reactor, we can store is longest available path here
var reactorPath: Chain! = Chain()
}
最后,链类看起来像这样(为简洁起见,省略了方法):
class Chain {
// The tiles that are part of this chain.
var tiles = [Tile]()
func addTile(tile: Tile) {
tiles.append(tile)
}
func firstTile() -> Tile {
return tiles[0]
}
func lastTile() -> Tile {
return tiles[tiles.count - 1]
}
var length: Int {
return tiles.count
}
}
-----------------------编辑:替换追踪者----------------- -------
我试图将User2464424代码转换为Swift。这就是我所拥有的:
func calculatePathsFromReactor(reactor: Tile) -> Chain?
{
func countDirections(neighbours: [Bool]) -> Int
{
var count: Int = 0
for var i:Int = 0; i < neighbours.count; i++
{
if neighbours[i] == true
{
count++
}
}
return count
}
var longestChain: Chain? = nil
longestChain = Chain()
var temp: Chain = Chain()
var lastBranch: Tile = reactor
var lastMove: Int? = reactor.neighbours.indexOf(true)
func looper(var currentTile: Tile)
{
if currentTile != reactor
{
if countDirections(currentTile.neighbours) > 2 //is branch
{
lastBranch = currentTile
}
if countDirections(currentTile.neighbours) == 1 //is endpoint
{
lastBranch.neighbours[lastMove!] = false // block move out of the last branch found
if longestChain.length < temp.length
{
longestChain = temp
}
currentTile = reactor // return to reactor and redo
lastVisitedTile = reactor
temp = Chain() //reset to empty array
lastBranch = reactor
lastMove = reactor.neighbours.indexOf(true)
looper(currentTile)
}
}
//let tempTile: Tile = Tile(column: currentTile.column, row: currentTile.row, tileType: currentTile.tileType, isReactor: currentTile.isReactor, movesRemaining: currentTile.movesRemaining)
//tempTile.neighbours = currentTile.neighbours
if currentTile.neighbours[0] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 0
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column, currentTile.row + 1]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
if currentTile.neighbours[1] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 1
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column + 1, currentTile.row]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
if currentTile.neighbours[2] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 2
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column, currentTile.row - 1]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
if currentTile.neighbours[3] == true
{
if !temp.tiles.contains(currentTile)
{
temp.addTile(currentTile)
}
if countDirections(currentTile.neighbours) > 2
{
lastMove = 3
}
lastVisitedTile = currentTile
currentTile = tiles[currentTile.column - 1, currentTile.row]! //must avoid going backwards
if !temp.tiles.contains(currentTile)
{
looper(currentTile)
}
}
}
// trigger the function for the reactor tile
looper(reactor)
return longestChain
}
(neighbours
属性是struct
,包含四个命名变量:above
,right
,below
和left
,每个都初始化为false
然后通过在路径查找器之前运行的函数设置为true
。)
我现在发现了几个问题。代码按原样循环,但停在拱顶,在单个瓷砖绕行下 - 返回的路径只有4个瓷砖长(包括反应堆)。
我遇到的另一个问题 - 当我返回正确的路径时我会担心 - 是我在第三列中移动磁贴时遇到内存访问错误一个人。我认为当有一块瓷砖(2x2或更高)而不是只有一块瓷砖宽的路径时,它会感到困惑。
答案 0 :(得分:1)
我无法修复您的代码,但我对一个不需要递归的系统有所了解。 您可以尝试从反应堆中执行所有可能的路径,并通过了解遇到分支时所做的移动来阻止已经遍历的路径。
在tile类中,添加另一个由4个整数初始化为0的数组(例如,名为&#34; dir&#34;)。
伪代码。 首先执行预处理循环:
foreach tiles:
if tileHasNORTHNeighbor: tile.dir[0] = 1;
if tileHasEASTNeighbor: tile.dir[1] = 1;
if tileHasSOUTHNeighbor: tile.dir[2] = 1;
if tileHasWESTNeighbor: tile.dir[3] = 1;
然后做:
tile currentTile = reactor;
array longest;
array temp;
tile lastBranch = reactor;
int lastMove = any key of "reactor.dir" with "1" as value;
function int countOnes(array dir):
int count = 0;
int t;
for (t=0;t<4;t++):
if dir[t]==1:
count++;
return count;
:start
if currentTile != reactor:
if countOnes(currentTile.dir) > 2: //is branch
lastBranch = currentTile;
if countOnes(currentTile.dir) == 1: //is endpoint
lastBranch.dir[lastMove] = 0; // block move out of the last branch found
if longest.length < temp.length:
longest = temp;
currentTile = reactor; // return to reactor and redo
array temp = []; //reset to empty array
lastBranch = reactor;
lastMove = next "reactor.dir" key with "1" as value;
goto start;
if currentTile.dir[0] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 0;
currentTile = getTileAtNORTH; //must avoid going backwards
goto start;
if currentTile.dir[1] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 1;
currentTile = getTileAtEAST; //must avoid going backwards
goto start;
if currentTile.dir[2] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 2;
currentTile = getTileAtSOUTH; //must avoid going backwards
goto start;
if currentTile.dir[3] == 1:
temp.append(currentTile);
if countOnes(currentTile.dir) > 2:
lastMove = 3;
currentTile = getTileAtWEST; //must avoid going backwards
goto start;
答案 1 :(得分:1)
您可以使用BFS Algorithm并轻松修改它,为您提供最长的路径。
您已获得实施示例here。或者您已经获得了至少SwiftStructures和SwiftGraph个github存储库,其图形和搜索算法已经在swift中实现。