理解A*寻路算法具体过程


由A移动到B
假设垂直方向移动一个格子消耗为10,斜线方向移动一个格子消耗为14。
然后定义三个变量:
G:表示从起点A移动到当前方格的移动消耗
H:表示从当前方格移到到终点B的预计消耗
F = G + H
此时可以得到上图中的各个格子的信息。

实现思路:
1.加入开启列表。将A加入【开启列表】
2.遍历开启列表。找出F最小的,开始只有A
3.移除开启列表。从【开启列表】中移除A,加入【关闭列表】
4.遍历周围格子。获取当前表格周围的格子再遍历
5.周围点不在开启列表时。如果周围的格子不在开启列表,就将当前格子作为父节点,计算G,H,F,并将这个周围点加入到【开启列表】。
Tips:当起点为A时,上面已经完成了一次开启列表的遍历,下次遍历时,当前格子F最小就是格子1。
6.周围点在开启列表时判断G值。如果周围的格子在【开启列表】,那么就检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,更小的G值表示更好的路径,如果G更小,把它的父节点设置为当前格,重新计算G,F;如果没有更小的就不做处理。
Tips:此时如果走A12路径,2的G为20,G值>它原来的14,所以这里不做处理。此时1已经加入关闭列表,然后再继续遍历【开启列表】
Tips:当遍历到3时,此时如果走A123,那么当遍历到2_H时,此时当前的G=10+14=24结果是小于2_H当前的G值,那么此时2_H重新计算G,F最终变成上图3_H。
8.当把目标表格加入到【开启列表】。也就找到目标表格,如果没有找到目标表格,表示无路径
9.获取最终路径。从目标表格开始,获取每个格子的父节点,就是最终路径。
抽象过程

把起始格添加到 "开启列表" 
do 

       寻找开启列表中F值最低的格子, 我们称它为当前格. 
       把它切换到关闭列表. 
       对当前格相邻的8格中的每一个 
          if (它不可通过 || 已经在 "关闭列表" 中) 
          { 
                什么也不做. 
           } 
          if (它不在开启列表中) 
          { 
                把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的 FGH 
          if (它已经在开启列表中) 
          { 
                if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径) 
                    { 
                            把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值. 
                    } 
} while( 目标格已经在 "开启列表", 这时候路径被找到) 
如果开启列表已经空了, 说明路径不存在.

最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.

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


namespace Maze
{
    class Maze
    {
        public const int OBLIQUE = 14;
        public const int STEP = 10;
        public int[,] MazeArray { get; private set; }
        List<Point> CloseList;
        List<Point> OpenList;


        public Maze(int[,] maze)
        {
            this.MazeArray = maze;
            OpenList = new List<Point>(MazeArray.Length);
            CloseList = new List<Point>(MazeArray.Length);
        }


        public Point FindPath(Point start, Point end, bool IsIgnoreCorner)
        {
            OpenList.Add(start);
            while (OpenList.Count != 0)
            {
                //找出F值最小的点
                var tempStart = OpenList.MinPoint();
                OpenList.RemoveAt(0);
                CloseList.Add(tempStart);
                //找出它相邻的点
                var surroundPoints = SurrroundPoints(tempStart, IsIgnoreCorner);
                foreach (Point point in surroundPoints)
                {
                    if (OpenList.Exists(point))
                        //计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F
                        FoundPoint(tempStart, point);
                    else
                        //如果它们不在开始列表里, 就加入, 并设置父节点,并计算GHF
                        NotFoundPoint(tempStart, end, point);
                }
                if (OpenList.Get(end) != null)
                    return OpenList.Get(end);
            }
            return OpenList.Get(end);
        }


        private void FoundPoint(Point tempStart, Point point)
        {
            var G = CalcG(tempStart, point);
            if (G < point.G)
            {
                point.ParentPoint = tempStart;
                point.G = G;
                point.CalcF();
            }
        }


        private void NotFoundPoint(Point tempStart, Point end, Point point)
        {
            point.ParentPoint = tempStart;
            point.G = CalcG(tempStart, point);
            point.H = CalcH(end, point);
            point.CalcF();
            OpenList.Add(point);
        }


        private int CalcG(Point start, Point point)
        {
            int G = (Math.Abs(point.X - start.X) + Math.Abs(point.Y - start.Y)) == 2 ? STEP : OBLIQUE;
            int parentG = point.ParentPoint != null ? point.ParentPoint.G : 0;
            return G + parentG;
        }


        private int CalcH(Point end, Point point)
        {
            int step = Math.Abs(point.X - end.X) + Math.Abs(point.Y - end.Y);
            return step * STEP;
        }


        //获取某个点周围可以到达的点
        public List<Point> SurrroundPoints(Point point, bool IsIgnoreCorner)
        {
            var surroundPoints = new List<Point>(9);


            for(int x = point.X -1; x <= point.X+1;x++)
                for (int y = point.Y - 1; y <= point.Y + 1; y++)
                {
                    if (CanReach(point,x, y,IsIgnoreCorner))
                        surroundPoints.Add(x, y);
                }
            return surroundPoints;
        }


        //在二维数组对应的位置不为障碍物
        private bool CanReach(int x, int y)
        {
            return MazeArray[x, y] == 0;
        }


        public bool CanReach(Point start, int x, int y, bool IsIgnoreCorner)
        {
            if (!CanReach(x, y) || CloseList.Exists(x, y))
                return false;
            else
            {
                if (Math.Abs(x - start.X) + Math.Abs(y - start.Y) == 1)
                    return true;
                //如果是斜方向移动, 判断是否 "拌脚"
                else
                {
                    if (CanReach(Math.Abs(x - 1), y) && CanReach(x, Math.Abs(y - 1)))
                        return true;
                    else
                        return IsIgnoreCorner;
                }
            }
        }
    }


    //Point 类型
    public class Point
    {
        public Point ParentPoint { get; set; }
        public int F { get; set; }  //F=G+H
        public int G { get; set; }
        public int H { get; set; }
        public int X { get; set; }
        public int Y { get; set; }


        public Point(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
        public void CalcF()
        {
            this.F = this.G + this.H;
        }
    }


    //对 List<Point> 的一些扩展方法
    public static class ListHelper
    {
        public static bool Exists(this List<Point> points, Point point)
        {
            foreach (Point p in points)
                if ((p.X == point.X) && (p.Y == point.Y))
                    return true;
            return false;
        }


        public static bool Exists(this List<Point> points, int x, int y)
        {
            foreach (Point p in points)
                if ((p.X == x) && (p.Y == y))
                    return true;
            return false;
        }


        public static Point MinPoint(this List<Point> points)
        {
            points = points.OrderBy(p => p.F).ToList();
            return points[0];
        }
        public static void Add(this List<Point> points, int x, int y)
        {
            Point point = new Point(x, y);
            points.Add(point);
        }


        public static Point Get(this List<Point> points, Point point)
        {
            foreach (Point p in points)
                if ((p.X == point.X) && (p.Y == point.Y))
                    return p;
            return null;
        }


        public static void Remove(this List<Point> points, int x, int y)
        {
            foreach (Point point in points)
            {
                if (point.X == x && point.Y == y)
                    points.Remove(point);
            }
        }
    }
}




using System;


namespace Maze
{
    class Program
    {
        static void Main(string[] args)
        {
            int[,] array = {
                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
                           { 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1},
                           { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1},
                           { 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1},
                           { 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1},
                           { 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
                           { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1},
                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
                           };
            Maze maze = new Maze(array);
            Point start = new Point(1, 1);
            Point end = new Point(6, 10);
            var parent = maze.FindPath(start, end, false);


            Console.WriteLine("Print path:");
            while (parent != null)
            {
                Console.WriteLine(parent.X + ", " + parent.Y);
                parent = parent.ParentPoint;
            }
            Console.ReadLine();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值