在具有2D网格地图的游戏中,我面临以下情况:
我需要找到围绕玩家位置(红点)的最大边界正方形。或者至少 最大尺寸的边界正方形,因为可能会有更多。注意:方,而不是矩形。
在这种情况下,很容易看到最大的正方形是8x8:
如果我在地图中添加障碍物,现在最大的可能性是5x5:
我正在寻找一种快速有效的方式来查找包含玩家位置的(或a)最大方格。
我目前正在做的是一种蛮力:
它有效,它非常简单,但感觉非常低效。显然我在这里做了很多冗余检查,我想知道是否有更聪明/更快的方法来做到这一点。有谁知道有效地做到这一点的算法?
(编辑)重要补充:除了玩家四处移动外,动态添加或移动障碍物或墙壁,因此这里的缓存或预先优化很难实现。
答案 0 :(得分:5)
我认为您可以通过在每个阶段检查现有“最大广场”的有效边界来改进您的算法。这可能更容易以图解方式解释......但基本上。你应该做的只是
**Growth Algorithm**
repeat
search the bounding sub-squares on the valid sides of the largest valid square found so far
increase size of square on 'valid' sides and mark newly blocked sides as invalid
until all sides invalid
then check if largest valid square can be translated 1 unit diagonally in any of 4 directions
if so repeat the growth algorithm on each new square until none get bigger
通过这种方式,您只需要测试一次最终有效方块的每个子方块。因此,如果square为n,则为n ^ 2进程。 IO不认为你可以做得更好,因为你需要检查每个子广场的有效性。
答案 1 :(得分:1)
答案 2 :(得分:1)
我认为一部视频价值一千张图像。 Algorithm demonstration。
在视频中,您可以看到该算法如何找到周围的正方形,不幸的是,我没有绘制整个过程,它仅绘制了良好的匹配项,但我认为您会发现有趣的事情,并且您可以注意到整个过程,只是观看了良好的匹配项
您可以通过处理3自己运行视频示例,我将整个代码放在this gist中。
最相关的代码(用python编写)是
def checkOutBoundaries(areaX1, areaY1, areaX2, areaY2):
return areaX1 < 0 or areaY1 < 0 or areaX2 >= gridSize or areaY2 >= gridSize
def isAreaCompatible(type, oldAreaX1, oldAreaY1, oldAreaX2, oldAreaY2, areaX1, areaY1, areaX2, areaY2):
global grid
for y in range(areaY1, areaY2+1):
for x in range(areaX1, areaX2+1):
print "checking point (%s,%s) old area is (%s,%s) (%s,%s) new area is (%s,%s) (%s,%s)" % (x,y,oldAreaX1, oldAreaY1, oldAreaX2, oldAreaY2, areaX1,areaY1,areaX2,areaY2)
if x >= oldAreaX1 and x <= oldAreaX2 and y >= oldAreaY1 and y <= oldAreaY2:
print "This area belongs to previous area, won't check"
else:
if grid[y][x].type != type: print "false"; print "This area have a different color/type so it's not compatible"; return False;
return True;
def explore(type, x1, y1, x2, y2):
#Right and bottom
print "----- Right and bottom ------"
areaX1 = x1;
areaY1 = y1;
areaX2 = x2+1;
areaY2 = y2+1;
if not checkOutBoundaries(areaX1, areaY1, areaX2, areaY2):
if isAreaCompatible(type, x1, y1, x2, y2, areaX1, areaY1, areaX2, areaY2):
addAnim(areaX1, areaY1, areaX2, areaY2);
addMatch(areaX1, areaY1, areaX2, areaY2);
explore(type, areaX1, areaY1, areaX2, areaY2);
#Bottom and left
print "----- Bottom and left ------"
areaX1 = x1-1;
areaY1 = y1;
areaX2 = x2;
areaY2 = y2+1;
if not checkOutBoundaries(areaX1, areaY1, areaX2, areaY2):
if isAreaCompatible(type, x1, y1, x2, y2, areaX1, areaY1, areaX2, areaY2):
addAnim(areaX1, areaY1, areaX2, areaY2);
addMatch(areaX1, areaY1, areaX2, areaY2);
explore(type, areaX1, areaY1, areaX2, areaY2);
#Left and top
print "----- Left and top ------"
areaX1 = x1-1;
areaY1 = y1-1;
areaX2 = x2;
areaY2 = y2;
if not checkOutBoundaries(areaX1, areaY1, areaX2, areaY2):
if isAreaCompatible(type, x1, y1, x2, y2, areaX1, areaY1, areaX2, areaY2):
addAnim(areaX1, areaY1, areaX2, areaY2);
addMatch(areaX1, areaY1, areaX2, areaY2);
explore(type, areaX1, areaY1, areaX2, areaY2);
#Top and right
print "----- Top and right ------"
areaX1 = x1;
areaY1 = y1-1;
areaX2 = x2+1;
areaY2 = y2;
if not checkOutBoundaries(areaX1, areaY1, areaX2, areaY2):
if isAreaCompatible(type, x1, y1, x2, y2, areaX1, areaY1, areaX2, areaY2):
addAnim(areaX1, areaY1, areaX2, areaY2);
addMatch(areaX1, areaY1, areaX2, areaY2);
explore(type, areaX1, areaY1, areaX2, areaY2);
答案 3 :(得分:0)
这是一种完全不同的方法:
你持有一个大小为2d的矩阵[长度,2]长度是地图的大小(我假设对称的宽度/高度,但如果不是只选择一个轴)。 我将调用第一级(长度)列(您可以将整个算法旋转90度并调用此行)。 每列保留2个值 - Min Position&amp;列中有效范围的最大位置。
使用以下算法填充此矩阵:
从点列开始,找到&amp;存储min&amp;连续范围的最大位置高于&amp;点下方。如果dot位于x,y:
int colMin, colMax;
for(colMin = y; colMin > 0;) {
if(Matrix[x, colMin - 1].IsValid)
colMin--;
else break;
}
for(colMax = y; colMax < maxHeight;) {
if(Matrix[x, colMax + 1].IsValid)
colMax++;
else break;
}
MinMaxValid[x, 0] = colMin;
MinMaxValid[x, 1] = colMax;
您可以使用以下算法单独进行两个方向:
int minCol = 0, maxCol = maxWidth; // These hold the min/max range of valid columns
for(int col = x - 1; col >= 0; col--) {
for(colMin = y; colMin > MinMaxValid[col + 1, 0];) { // Cell is only valid if it overlaps to the next range
if(Matrix[col, colMin - 1].IsValid)
colMin--;
else break;
}
for(colMax = y; colMax < MinMaxValid[col + 1, 1];) { // Cell is only valid if it overlaps to the next range
if(Matrix[col, colMax + 1].IsValid)
colMax++;
else break;
}
if((colMax - colMin) >= (x - col)) { // if the found range is smaller than the distance for x, it can't be a part of the square
MinMaxValid[col, 0] = colMin;
MinMaxValid[col, 1] = colMax;
}
else {
minCol = col + 1;
break; // We're done on this side
}
}
for(int col = x + 1; col < maxWidth; col++) {
for(colMin = y; colMin > MinMaxValid[col - 1, 0];) { // Cell is only valid if it overlaps to the previous range
if(Matrix[col, colMin - 1].IsValid)
colMin--;
else break;
}
for(colMax = y; colMax < MinMaxValid[col - 1, 1];) { // Cell is only valid if it overlaps to the previous range
if(Matrix[col, colMax + 1].IsValid)
colMax++;
else break;
}
if((colMax - colMin) >= (col - x)) { // if the found range is smaller than the distance for x, it can't be a part of the square
MinMaxValid[col, 0] = colMin;
MinMaxValid[col, 1] = colMax;
}
else {
maxCol = col - 1;
break; // We're done on this side
}
}
您现在已经填写了MinMaxValid。下一部分是遍历行并找到最大的正方形:
int maxSquareSize = 0, maxSquareCol = minCol;
for(int col = minCol; (MinMaxValid[col, 1] - MinMaxValid[col, 0]) >= (maxCol - col); col++) {
for(int squareSize = MinMaxValid[col, 1] - MinMaxValid[col, 0] + 1; squareSize > maxSquareSize; squareSize--) {
if((Min(MinMaxValid[col, 1], MinMaxValid[col + squareSize - 1, 1]) - Max(MinMaxValid[col, 0], MinMaxValid[col + squareSize - 1, 0])) >= (squareSize) {
maxSquareSize = squareSize;
maxSquareCol = col;
break;
}
}
}
最后一部分是这样的:
任何列都可以参与仅与其高度一样大的正方形。为此,当前列范围和col + squareSize - 1
范围的并集必须至少为squareSize高。
现在是最好的部分!每当场景发生变化时(方形变为无效,点移动等等),您只能使MinMaxValid矩阵的某个范围无效并根据需要重新进行评估。我没有包含这个逻辑,因为这个答案足够长,但这很容易(基本上,你减少了列范围以适应新的情况,并重新扫描两个方向)。
我必须说我还没有试过这个,但即使它不起作用(请告诉我所以我可以删除这个评论:-),它必须接近有效的解决方案。