【问题标题】:C# - A* algorithm gives wrong results [closed]C# - A* 算法给出错误的结果 [关闭]
【发布时间】:2016-04-02 11:36:24
【问题描述】:

我正在尝试在 C# 中制作 4 向 A-star 寻路算法,但无法使其正常工作。在 Main 方法中,我有一个示例 5x5 int 数组映射来设置可以访问哪些字段,哪些不可以访问(0 - 清除字段,1 - 障碍物)。例如,我将 START 点设置在 (1, 3) 并将 TARGET 点设置在 (4, 4),所以我希望路径可以避开墙壁并继续等,但它没有。我什至让我的算法显示每个父母及其继任者(邻居)。就我而言,尽管在字段(地图)中被标记为障碍物,但它仍将 (2,4) 显示为节点之一,它是如何发生的? GetSuccessors 方法中有明确的声明,只包括标记为 0 的那些,但它仍然包括 (2, 4) 点。我不确定 GetSuccessorsFindPath(主算法)是否有问题。

示例地图:

int[,] fields = new int[5, 5] //MAP, 1 - OBSTACLE
        {
            { 0, 0, 0, 0, 0 },
            { 0, 1, 1, 1, 0 },
            { 0, 1, 1, 1, 0 },
            { 0, 0, 1, 0, 0 },
            { 0, 0, 1, 0, 0 }
        };

示例点:

Node start = new Node(1, 3); //START LOCATION ON MAP
Node target = new Node(4, 4); //TARGET LOCATION ON MAP

生成的路径(从 TARGET 到 START):

4, 4
3, 4
2, 4
1, 3

完整代码(FindPath 和 GetSuccessors 是主要的,但我仍然可以用其他方法出错):

using System;
using System.Collections.Generic;
using System.Linq;

namespace FoxLib
{
    public class Node
    {
        public Node(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public int x, y; //POSITION ON MAP
        public Node parent; //NEEDED TO RETRIEVE PATH LATER
        public int G, H, F; 
        //G - COST FROM START NODE TO THIS NODE
        //H - COST FROM THIS NODE TO TARGET (HEURISTIC)
        //F - G + H; NODE COST
    }

    public class Pathfinding
    {
        public static void FindPath(int startX, int startY, int targetX, int targetY, int[,] fields) //MAIN ALGORITHM
        {
            bool pathFound=false; //IS PATH AVAILABLE?
            Node start = new Node(startX, startY); //STARTING NODE
            Node target = new Node(targetX, targetY); //TARGET NODE
            start.parent = start;
            start.G = 0;
            start.H = Math.Abs(target.x - start.x) + Math.Abs(target.y - start.y);
            start.F = start.G + start.H;
            Node current = new Node(0, 0);
            List<Node> openList = new List<Node>(); //NODES WAITING TO BE CHECKED
            List<Node> closedList = new List<Node>(); //ALREADY CHECKED NODES
            openList.Add(start);

            while(openList.Count>0) //DO AS LONG AS THERE ARE STILL NODES TO DISCOVER
            {
                current = MinFNode(openList); //FIND NODE WITH LOWEST F COST (LOWER=BETTER PATH)
                openList.Remove(current); //REMOVE CURRENT NODE FROM QUEUE

                if ((current.x==target.x)&&(current.y==target.y)) //TARGET FOUND
                {
                    Console.WriteLine("PATH FOUND");
                    pathFound = true;
                    //DISPLAY PATH (FROM TARGET TO START) BY CHECKING PARENTS
                    Console.WriteLine("[FULL PATH]");
                    do
                    {
                        Console.WriteLine(current.x + ", " + current.y);
                        current = current.parent;
                    } while ((current.x != start.x) && (current.y != start.y));
                    Console.WriteLine(start.x + ", " + start.y);
                    break;
                }

                List<Node> successors = GetSuccessors(current.x, current.y, fields); //GET SUCCESSORS
                foreach (Node node in successors)
                { 
                    node.parent = current; //SET CURRENT NODE AS PARENT TO RETRIEVE PATH LATER
                    node.G = current.G + 10; //10 - DISTANCE FROM CURRENT TO ITS SUCCESSOR, current.G DISTANCE FROM CURRENT TO START
                    node.H = Math.Abs(target.x - node.x) + Math.Abs(target.y - node.y); //HEURISTIC DISTANCE TO TARGET
                    node.F = node.G + node.H; //NODE FINAL COST

                    Console.WriteLine(current.x + ", " + current.y + " PARENT | SUCCESSOR: " + node.x + ", " + node.y);  //TEST DISPLAY TO CHECK SUCCESSORS CURRENT NODE PRODUCED

                    if (closedList.FirstOrDefault(l => l.x == node.x && l.y == node.y) != null) //IF SUCCESSOR ALREADY EXISTS ON CLOSED LIST
                    {
                        Node temp = closedList.FirstOrDefault(l => l.x == node.x && l.y == node.y);
                        if (node.F < temp.F) //IF NEW PATH TO THIS NODE IS BETTER? (LOWER F = SHORTER PATH)
                        {
                            closedList.Remove(temp); //REMOVE OLD NODE
                            temp.parent = node.parent;
                            temp.F = node.F;
                            closedList.Add(temp); //ADD UPDATED NODE
                        }

                    }
                    else
                    if(openList.FirstOrDefault(l => l.x == node.x && l.y == node.y) != null) //IF SUCCESSOR ALREADY EXISTS ON OPEN LIST
                    {
                        Node temp = openList.FirstOrDefault(l => l.x == node.x && l.y == node.y);
                        if (node.F < temp.F) //IF NEW PATH TO THIS NODE IS BETTER? (LOWER F = SHORTER PATH)
                        {
                            openList.Remove(temp); //REMOVE OLD NODE
                            temp.parent = node.parent;
                            temp.F = node.F;
                            openList.Add(temp); //ADD UPDATED NODE
                        }
                    }
                    else
                    {
                        openList.Add(node); //ADD SUCCESSOR TO OPEN LIST
                    }
                }
                closedList.Add(current); //MARK CURRENT NODE AS CHECKED (NO NEED TO CHECK IT UNTIL BETTER PATH TO THIS NODE IS FOUND)
            }
            if(!pathFound)
            {
                Console.WriteLine("PATH NOT FOUND");
            }
        }

        //FIND NODE WITH LOWEST F COST
        public static Node MinFNode(List<Node> nodes)
        {
            Node result = nodes[0];
            foreach(Node node in nodes)
            {
                if (node.F < result.F)
                    result = node;
            }
            return result;
        }

        //GET SUCCESSORS OF CURRENT NODE (ONLY THE ONES WITH "0" VALUE)
        public static List<Node> GetSuccessors(int x, int y, int[,] fields) 
        {
            List<Node> Successors = new List<Node>();
            if ((x - 1 >= 0) && (fields[x - 1, y]==0))
                Successors.Add(new Node(x - 1, y)); //LEFT

            if ((x + 1 < fields.GetLength(0)) && (fields[x + 1, y]==0))
                Successors.Add(new Node(x + 1, y)); //RIGHT

            if ((y - 1 >= 0) && (fields[x, y - 1]==0))
                Successors.Add(new Node(x, y - 1)); //UP

            if ((y + 1 < fields.GetLength(1)) && (fields[x, y + 1]==0))
                Successors.Add(new Node(x, y + 1)); //DOWN

            return Successors; //RETURN LIST OF AVAILABLE SUCCESSORS
        }

        static void Main()
        {
            int[,] fields = new int[5, 5] //MAP, 1 - OBSTACLE
            {
                { 0, 0, 0, 0, 0 },
                { 0, 1, 1, 1, 0 },
                { 0, 1, 1, 1, 0 },
                { 0, 0, 1, 0, 0 },
                { 0, 0, 1, 0, 0 }
            };

            Node start = new Node(1, 3); //START LOCATION ON MAP
            Node target = new Node(4, 4); //TARGET LOCATION ON MAP

            FindPath(start.x,start.y,target.x,target.y,fields); //MAIN ALGORITHM

            Console.WriteLine("END");
            Console.ReadLine();
        }
    }


}

【问题讨论】:

  • 您的问题是“我写了一个程序,它不起作用,我不知道如何修复它”。这不是在您的程序中查找错误的服务。请阅读:ericlippert.com/2014/03/05/how-to-debug-small-programs 应用这些技巧,当您有具体问题时再回来。今天学习调试;这将比仅仅看着你的问题在 SO 上结束更有效。
  • 您可以先在代码中定义什么是x 和什么是y。看起来您正在考虑寻址为 (x, y)。但在 C# 二维数组中,索引为 array[y, x] 其中 0 &lt;= y &lt; array.GetLength(0), 0 &lt;= x &lt; array.GetLength(1)

标签: c# algorithm a-star


【解决方案1】:

正如 Ivan 指出的那样,您的工作是基于错误的假设。在二维数组中,第一个索引是行号,而不是列。这意味着fields[2,4] 是第二行,第四列......这是0。

简单修复...将所有fields 引用上的索引切换到fields[y, x],您应该更接近一步。

然而,Eric 的评论完全有效。您应该能够通过一些调试来解决这个问题。单步执行代码并查看实际发生的情况,您将对问题有更深入的了解。由于 VS2015 社区版是免费的,因此没有太多理由不这样做,如果您不是在 Windows 上开发,还有其他 C# IDE 可以让您逐步执行代码并检查程序运行时的状态。 MonoDevelop 是 Linux 和 Mac 上的首选。

【讨论】:

  • 你是对的,谢谢,这么小的错误造成这么多问题。实际上问题不是因为没有使用 [y, x] 格式,而主要是因为测试数组的声明方式不好。当然在算法中使用 [y, x] 格式会起作用,但我可以反转测试数组。无论如何,问题只存在于测试数组,因为我的真实输入数组(来自整个班级之外)也是在 [x, y] 中制作的,因此效果很好。我还设法修复了导致显示错误路径的秒数错误,但这只是与显示相关的问题,路径已按应有的方式计算,因此问题已解决,谢谢。
猜你喜欢
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 2013-01-26
  • 2016-08-29
  • 2018-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多