AStart.cs 7.2 KB
using log4net;
using System;
using System.Collections.Generic;
using System.Linq;

namespace CtuDeviceLib
{
    public class AStart
    {
        static ILog log = Mushiny.Common.LOGGER;
        List<AStartPoint> CloseList;
        List<AStartPoint> OpenList;
        public const int OBLIQUE = 14;
        public const int STEP = 10;

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

            return OpenList.Get(end);
        }

        private void FoundPoint(AStartPoint tempStart, AStartPoint point)
        {
            var G = CalcG(tempStart, point);
            if (G < point.G)
            {
                point.ParentPoint = tempStart;
                point.G = G;
                point.CalcF();
            }
            log.Debug($"寻路算法-在开始列表:{point.Id}");
        }

        private void NotFoundPoint(AStartPoint tempStart, AStartPoint end, AStartPoint point)
        {
            point.ParentPoint = tempStart;
            point.G = CalcG(tempStart, point);
            point.H = CalcH(end, point);
            point.CalcF();
            OpenList.Add(point);
            log.Debug($"寻路算法-{tempStart.Id}->{point.Id}不在开始列表,{point.Id} G=【{point.G}】");
        }

        private int CalcG(AStartPoint start, AStartPoint point)
        {
            var dis = start.Landmark.GetDis(point.Id);
           // 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 dis + parentG;
        }

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

        /// <summary>
        /// 获取某个点周围可以到达的点
        /// </summary>
        /// <param name="point"></param>
        /// <param name="barrierPoints">不可走的点</param>
        /// <returns></returns>
        public List<AStartPoint> SurrroundPoints(AStartPoint point, List<AStartPoint> barrierPoints)
        {
            var surroundPoints = new List<AStartPoint>();

            if (point.Landmark.Above != null)
            {
                var p = new AStartPoint(pathPlan.GetPointCode(point.Landmark.Above.Id));
                if (!barrierPoints.HasBarrier(p) && pathPlan.IsTwoWay(point.Id, point.Landmark.Above.Id))
                    surroundPoints.Add(p);
            }
            if (point.Landmark.Right != null)
            {
                var p = new AStartPoint(pathPlan.GetPointCode(point.Landmark.Right.Id));
                if (!barrierPoints.HasBarrier(p) && pathPlan.IsTwoWay(point.Id, point.Landmark.Right.Id))
                    surroundPoints.Add(p);
            }
            if (point.Landmark.Below != null)
            {
                var p = new AStartPoint(pathPlan.GetPointCode(point.Landmark.Below.Id));
                if (!barrierPoints.HasBarrier(p) && pathPlan.IsTwoWay(point.Id, point.Landmark.Below.Id))
                    surroundPoints.Add(p);
            }
            if (point.Landmark.Left != null)
            {
                var p = new AStartPoint(pathPlan.GetPointCode(point.Landmark.Left.Id));
                if (!barrierPoints.HasBarrier(p) && pathPlan.IsTwoWay(point.Id, point.Landmark.Left.Id))
                    surroundPoints.Add(p);
            }
            return surroundPoints;
        }
    }

    public class AStartPoint
    {
        public uint Id { get { return Landmark.Id; } }
        public AStartPoint 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 { return Landmark.X; } }
        public int Y { get { return Landmark.Y; } }
        public PointCode Landmark { get; set; }
        public AStartPoint(PointCode landmark)
        {
            Landmark = landmark;
        }
        public void CalcF()
        {
            this.F = this.G + this.H;
        }
    }

    //对 List<Point> 的一些扩展方法
    public static class ListHelper
    {
        public static bool Exists(this List<AStartPoint> points, AStartPoint point)
        {
            if (points == null || points.Count == 0) return false;
            foreach (AStartPoint p in points)
                if ((p.X == point.X) && (p.Y == point.Y))
                    return true;
            return false;
        }
        /// <summary>
        /// 是否有障碍
        /// </summary>
        /// <param name="points"></param>
        /// <param name="point"></param>
        /// <returns></returns>
        public static bool HasBarrier(this List<AStartPoint> points, AStartPoint point)
        {
            if (point == null || point.Landmark == null)
            {
                return true;
            }

            if (points == null || points.Count == 0) return false;
            foreach (AStartPoint p in points)
                if (p.Landmark != null && p.Id == point.Landmark.Id)
                    return true;

            return false;
        }

        public static AStartPoint MinPoint(this List<AStartPoint> points)
        {
            points = points.OrderBy(p => p.F).ToList();
            return points[0];
        }
        public static AStartPoint Get(this List<AStartPoint> points, AStartPoint point)
        {
            foreach (AStartPoint p in points)
                if ((p.X == point.X) && (p.Y == point.Y))
                    return p;
            return null;
        }

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