简体   繁体   English

C#A *算法StackOverflowException

[英]C# A* Algorithm StackOverflowException

I am having a bit of trouble perfecting my A* algorithm. 我在完善A *算法时遇到了一些麻烦。 I works pretty well but needs some fine tuning. 我工作得很好,但需要一些微调。 I am getting a StackOverflowException while checking to see if my tile is valid. 检查我的图块是否有效时,我得到了StackOverflowException。 The Exception occurs either at the aStar() method or the isValid() function. 异常发生在aStar()方法或isValid()函数上。 Here is my code: 这是我的代码:

    private void aStar(int x, int y) { //Problem
        Point[] refPoints = { new Point(x - 1, y), new Point(x, y - 1), new Point(x, y + 1), new Point(x + 1, y) };
        Point lowPoint = new Point(x, y);
        int lowCost = 1000;

        for (int i = 0; i < refPoints.Length; i++) {
            if (isValid(refPoints[i], true)) { //Problem
                map[refPoints[i].X, refPoints[i].Y].H = Math.Abs(refPoints[i].X - player.X) + Math.Abs(refPoints[i].Y - player.Y);
                map[refPoints[i].X, refPoints[i].Y].G = map[x, y].G + 10;
                map[refPoints[i].X, refPoints[i].Y].F = map[refPoints[i].X, refPoints[i].Y].H + map[refPoints[i].X, refPoints[i].Y].G;

                if (map[refPoints[i].X, refPoints[i].Y].F < lowCost) {
                    lowCost = map[refPoints[i].X, refPoints[i].Y].F;
                    lowPoint = refPoints[i];
                }

                map[refPoints[i].X, refPoints[i].Y].AType = AType.OPEN;
            }
        }

        if (!(lowPoint.Equals(player))) {
            map[lowPoint.X, lowPoint.Y].AType = AType.CLOSED;
            path.Add(lowPoint);
            aStar(lowPoint);
        }
    }

    private void aStar(Point p) {
        aStar(p.X, p.Y); //Problem
    }

    private bool isValid(int x, int y, bool aStar) { //Problem
        if (aStar) {
            return (x >= 0 && x < tileX && y >= 0 && y < tileY && !map[x, y].TileType.Equals(TileType.BLOCK) && 
                !map[x, y].AType.Equals(AType.CLOSED) && !map[x, y].AType.Equals(AType.OPEN));
        } else {
            return (x >= 0 && x < tileX && y >= 0 && y < tileY && !map[x, y].TileType.Equals(TileType.BLOCK));
        }
    }

    private bool isValid(Point p, bool aStar) {
        return isValid(p.X, p.Y, aStar); //Problem
    }

I cannot seem to trace the origin of the problem but it only happens sometimes (usually when there is an obstacle in the way, but not always). 我似乎无法追踪问题的根源,但它仅在某些情况下发生(通常是在路上有障碍,但并非总是如此)。

Instead of an open and closed list in my code I have an enum (AType) in each Tile of my map (BLANK, OPEN, CLOSED). 在我的地图的每个图块(空白,打开,关闭)中都有一个枚举(AType),而不是代码中的打开和关闭列表。 The OPEN enum really doesn't affect anything except mark tiles that have been tested and are not the best move. OPEN枚举确实不会影响任何东西,除了经过测试且不是最佳移动的标记图块。 The CLOSED enum is just applied to all Tiles that are identified as the best move thus eventually building a path. CLOSED枚举仅应用于所有被确定为最佳移动的图块,从而最终构建路径。 The BLANK enum is just the default state of the Tile (neither in the open or closed list). BLANK枚举只是Tile的默认状态(不在打开或关闭列表中)。

A* does not have a recursive step. A *没有递归步骤。 You should be pulling work items out of a priority queue. 您应该将工作项从优先级队列中拉出。

Your code is tail recursive. 您的代码是尾递归的。 There is no need for recursion here. 此处无需递归。 Just wrap the entire method in a while (true) loop: 只需将整个方法包装在while (true)循环中:

private void aStar(int x, int y) {
    while (true) {
    //...
    if (!(lowPoint.Equals(player))) {
        map[lowPoint.X, lowPoint.Y].AType = AType.CLOSED;
        path.Add(lowPoint);
        continue;
    }
    else break;
}
}

I suspect the StackOverflowEx is gone after making this change but the algorithm will not work because I don't see a priority queue anywhere. 我怀疑进行了此更改后,StackOverflowEx消失了,但是该算法无法正常工作,因为我在任何地方都看不到优先级队列。

What you're basically doing: 您基本上在做什么:

void foo(Point a)
{
   ...
   foo(a.X, a.Y);
}
void foo(int x, int y)
{
   foo(new Point(x, y));
}

Now, recursion can be very handy, but in this case it isn't, because it isn't necessary and it'll simply get called too much, causing a StackOverFlow . 现在,递归可以非常方便,但是在这种情况下,它不是必需的,因为它不是必需的,它只会被调用太多,从而导致StackOverFlow So you need to redesign the function so you it doesn't call itself, but instead having a while loop with a certain condition. 因此,您需要重新设计该函数,以便它不会调用自身,而要while特定条件下进行while循环。

Something like this: 像这样:

void aStar(int x, int y)
{
   // just declarations to show you
   Path path;
   Point currentPoint;
   while (true)
   {
      // Your modified function goal. Instead of calling the function, just let the loop continue
      // if node is goal, break
      if (!(lowPoint.Equals(player)))
      {
        map[lowPoint.X, lowPoint.Y].AType = AType.CLOSED;
        path.Add(lowPoint);
        aStar(lowPoint);
      }
      else
      {
        break;
      }
   }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM