C# A*算法

自己用Unity实现了一下
https://gitee.com/cqmyg13/Astar
主要关键词有开表,闭表,优先级队列,广度优先搜索,排序,启发式搜索,麦哈顿距离
引用: https://www.redblobgames.com/pathfinding/a-star/implementation.html#csharp
上面链接的代码不完整,我对其进行了修改完善,效果如下:

输出("P"是路径, “#” 是墙, "*"是树):

这里写图片描述

using System;
using System.Collections.Generic;


// A* 寻路不一定是网格,可以是其他类型,这里是网格

// A到B的权重图接口
public interface WeightedGraph<L>
{
    double Cost(Location a, Location b);
    IEnumerable<Location> Neighbors(Location id);
}

// 地理坐标 (x, y)
public struct Location
{
    // Implementation notes: I am using the default Equals but it can
    // be slow. You'll probably want to override both Equals and
    // GetHashCode in a real project.

    public readonly int x, y;
    public Location(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    // 重写 equals
    public override bool Equals(Object obj)
    {
        Location p = (Location)obj;
        return (this.x == p.x) && (this.y == p.y);
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }

}


public class SquareGrid : WeightedGraph<Location>
{
    //  真实项目最好设为private
    public static readonly Location[] DIRS = new[]
        {
            new Location(1, 0),
            new Location(0, -1),
            new Location(-1, 0),
            new Location(0, 1)
        };

    public int width, height;
    public HashSet<Location> walls = new HashSet<Location>();
    public HashSet<Location> forests = new HashSet<Location>();

    public SquareGrid(int width, int height)
    {
        this.width = width;
        this.height = height;
    }

    public bool InBounds(Location id)
    {
        return 0 <= id.x && id.x < width
            && 0 <= id.y && id.y < height;
    }

    public bool Passable(Location id)
    {
        return !walls.Contains(id);
    }

    public double Cost(Location a, Location b)
    {
        return forests.Contains(b) ? 5 : 1;
    }

    public IEnumerable<Location> Neighbors(Location id)
    {
        foreach (var dir in DIRS)
        {
            Location next = new Location(id.x + dir.x, id.y + dir.y);
            if (InBounds(next) && Passable(next))
            {
                yield return next;
            }
        }
    }
}


public class PriorityQueue<T>
{
    // 这里可以用二叉堆优化
    // I'm using an unsorted array for this example, but ideally this
    // would be a binary heap. There's an open issue for adding a binary
    // heap to the standard C# library: https://github.com/dotnet/corefx/issues/574
    //
    // Until then, find a binary heap class:
    // * https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
    // * http://visualstudiomagazine.com/articles/2012/11/01/priority-queues-with-c.aspx
    // * http://xfleury.github.io/graphsearch.html
    // * http://stackoverflow.com/questions/102398/priority-queue-in-net

    private List<Tuple<T, double>> elements = new List<Tuple<T, double>>();

    public int Count
    {
        get { return elements.Count; }
    }

    public void Enqueue(T item, double priority)
    {
        elements.Add(Tuple.Create(item, priority));
    }

    public T Dequeue()
    {
        int bestIndex = 0;

        for (int i = 0; i < elements.Count; i++)
        {
            if (elements[i].Item2 < elements[bestIndex].Item2)
            {
                bestIndex = i;
            }
        }

        T bestItem = elements[bestIndex].Item1;
        elements.RemoveAt(bestIndex);
        return bestItem;
    }
}


/* NOTE about types: in the main article, in the Python code I just
 * use numbers for costs, heuristics, and priorities. In the C++ code
 * I use a typedef for this, because you might want int or double or
 * another type. In this C# code I use double for costs, heuristics,
 * and priorities. You can use an int if you know your values are
 * always integers, and you can use a smaller size number if you know
 * the values are always small. */

public class AStarSearch
{
    public Dictionary<Location, Location> cameFrom
        = new Dictionary<Location, Location>();
    public Dictionary<Location, double> costSoFar
        = new Dictionary<Location, double>();

    // Note: a generic version of A* would abstract over Location and
    // also Heuristic
    // 启发函数 曼哈顿距离 出租车几何
    static public double Heuristic(Location a, Location b)
    {
        return Math.Abs(a.x - b.x) + Math.Abs(a.y - b.y);
    }

    public AStarSearch(WeightedGraph<Location> graph, Location start, Location goal)
    {
        var frontier = new PriorityQueue<Location>();
        frontier.Enqueue(start, 0);

        cameFrom[start] = start;
        costSoFar[start] = 0;

        while (frontier.Count > 0)
        {
            var current = frontier.Dequeue();

            if (current.Equals(goal))
            {
                break;
            }

            foreach (var next in graph.Neighbors(current))
            {
                double newCost = costSoFar[current]
                    + graph.Cost(current, next);
                if (!costSoFar.ContainsKey(next)
                    || newCost < costSoFar[next])
                {
                    costSoFar[next] = newCost;
                    double priority = newCost + Heuristic(next, goal);
                    frontier.Enqueue(next, priority);
                    cameFrom[next] = current;
                }
            }
        }
    }
}

public class Test
{
    // 初始化
    static void DrawGridPlant(SquareGrid grid, AStarSearch astar, List<Location> path)
    {
        Console.WriteLine("A* Init:");
        // Print out the cameFrom array
        for (var y = 0; y < 10; y++)
        {
            for (var x = 0; x < 10; x++)
            {
                Location id = new Location(x, y);
                Location ptr = id;
                if (!astar.cameFrom.TryGetValue(id, out ptr))
                {
                    ptr = id;
                }
                if (grid.walls.Contains(id)) { Console.Write("##"); }
                else if (grid.forests.Contains(id)) { Console.Write("* "); }
                else { Console.Write(". "); }
            }
            Console.WriteLine();
        }
    }

    // 全部路径
    static void DrawGrid(SquareGrid grid, AStarSearch astar, List<Location> path)
    {
        Console.WriteLine("All path:");
        // Print out the cameFrom array
        for (var y = 0; y < 10; y++)
        {
            for (var x = 0; x < 10; x++)
            {
                Location id = new Location(x, y);
                Location ptr = id;
                if (!astar.cameFrom.TryGetValue(id, out ptr))
                {
                    ptr = id;
                }
                if (grid.walls.Contains(id)) { Console.Write("##"); }
                else if (ptr.x == x + 1) { Console.Write("\u2192"); }
                else if (ptr.x == x - 1) { Console.Write("\u2190"); }
                else if (ptr.y == y + 1) { Console.Write("\u2193"); }
                else if (ptr.y == y - 1) { Console.Write("\u2191"); }
                else if (grid.forests.Contains(id)) { Console.Write("* "); }
                else { Console.Write(". "); }
            }
            Console.WriteLine();
        }
    }

    // 最短路径
    // 一个汉字占两个字节 一个ascii 占一个字节 特殊字符也是两个字节
    static void DrawGridPath(SquareGrid grid, AStarSearch astar, List<Location> path)
    {
        Console.WriteLine("Shorter path:");
        // Print out the cameFrom array
        for (var y = 0; y < 10; y++)
        {
            for (var x = 0; x < 10; x++)
            {
                Location id = new Location(x, y);
                Location ptr = id;
                if (!astar.cameFrom.TryGetValue(id, out ptr))
                {
                    ptr = id;
                }
                if (grid.walls.Contains(id)) { Console.Write("##"); }       // 墙
                else if (path.Contains(id)) { Console.Write("P "); }
                else if (grid.forests.Contains(id)) { Console.Write("* "); } // 森林
                else if (ptr.x == x + 1) { Console.Write(". "); }           // 地面
                else if (ptr.x == x - 1) { Console.Write(". "); }
                else if (ptr.y == y + 1) { Console.Write(". "); }
                else if (ptr.y == y - 1) { Console.Write(". "); }
                else { Console.Write(". "); };                    
            }
            Console.WriteLine();
        }
    }

    static void Main()
    {
        // Make "diagram 4" from main article
        var grid = new SquareGrid(10, 10);
        for (var x = 1; x < 4; x++)
        {
            for (var y = 7; y < 9; y++)
            {
                grid.walls.Add(new Location(x, y));
            }
        }

        // 森林其实就相当于 粘稠的可通过区域 walkable
        grid.forests = new HashSet<Location>
            {
                new Location(3, 4), new Location(3, 5),
                new Location(4, 1), new Location(4, 2),
                new Location(4, 3), new Location(4, 4),
                new Location(4, 5), new Location(4, 6),
                new Location(4, 7), new Location(4, 8),
                new Location(5, 1), new Location(5, 2),
                new Location(5, 3), new Location(5, 4),
                new Location(5, 5), new Location(5, 6),
                new Location(5, 7), new Location(5, 8),
                new Location(6, 2), new Location(6, 3),
                new Location(6, 4), new Location(6, 5),
                new Location(6, 6), new Location(6, 7),
                new Location(7, 3), new Location(7, 4),
                new Location(7, 5)
            };

        // Run A*

        Location start = new Location(1, 4);
        Location goal = new Location(9, 4);

        var astar = new AStarSearch(grid, start, goal);

        Location current = goal;

        // 寻路路径
        List<Location> path = new List<Location>();
        while (!current.Equals(start))
        {
            path.Add(current);
            current = astar.cameFrom[current];
        }
        path.Add(start);

        // 打印路径
        //foreach (Location l in path) {
        //    Console.WriteLine(l.x + " "+l.y );
        //}

        DrawGridPlant(grid, astar, path);
        Console.WriteLine("");
        DrawGrid(grid, astar, path);
        Console.WriteLine("");
        DrawGridPath(grid, astar, path);

        Console.ReadLine();
    }
}

函数

 // 启发式算法
    public float Heuristic(Node a, Node b)
    {
        //return (a.transform.position - b.transform.position).magnitude;
        return Mathf.Abs(a.transform.position.x - b.transform.position.x) + Mathf.Abs(a.transform.position.z - b.transform.position.z);
    }


    // Djsktra最短路径
    public float CostDjsktra(Node node, float newCost)
    {
        return newCost;
    }

    // 贪婪
    public float CostBestFirst(Node node, float newCost)
    {
        return Heuristic(node, m_GoalNode);
    }

    // A*
    public float CostAStar(Node node, float newCost)
    {
        return newCost + Heuristic(node, m_GoalNode);
    }
此为用C#写的A*算法源代码 using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace EtSoft.AStarLib { public class AStar { private StarNodeCollection openList = new StarNodeCollection(); private StarNodeCollection closeList = new StarNodeCollection(); public StarNode currentNode = null; #region 构造函数 /// <summary> /// 使用指定的地图对象、起点和终点初始化A星算法 /// </summary> /// <param name="map">地图对象</param> public AStar(Map map) { this.map = map; } /// <summary> /// /// </summary> /// <param name="map">地图对象</param> /// <param name="start">起点坐标</param> /// <param name="end">终点坐标</param> public AStar(Map map, Point start, Point end) : this(map) { this.start = new StarNode(start); this.end = new StarNode(end); openList.Add(new StarNode(start)); //AddStartNodeToOpenList(this.start); } /// <summary> /// /// </summary> /// <param name="map">地图对象</param> /// <param name="startX">起点X坐标</param> /// <param name="startY">起点Y坐标</param> /// <param name="endX">终点X坐标</param> /// <param name="endY">终点Y坐标</param> public AStar(Map map, int startX, int startY, int endX, int endY) : this(map, new Point(startX, startY), new Point(endX, endY)) { } #endregion #region 属性 protected Map map; /// <summary> /// 地图数据 /// </summary> public Map Map { get { return map; } set { map = value; } } private StarNode start = null; /// <summary> /// 起点坐标,如果没有设置起点,返回null /// </summary> public StarNode Start { get { return start; } set { start = value; openList.Clear(); openList.Add(start); //AddNodeToOpenList(start); } } private StarNode end = null; /// <summary> /// 终点坐标,如果没有设置终点,返回null /// </summary> public StarNode End { get { return end; } set { end = value; } } private StarNodeCollection path; /// <summary> /// 返回路径节点集合,没有路径则返回null /// </summary> public StarNodeCollection Path { get { return path; } } private bool allDirection = false; /// <summary> /// true,允许八方向寻路 /// </summary> public bool AllDirection { get { return allDirection; } set { allDirection = value; } } #endregion /// <summary> /// 开始寻路 /// </summary> public void StartSearchPath() { if (start == null) throw new InvalidNodeException(InvalidNodeTypes.NoStart); if (end == null) throw new InvalidNodeException(InvalidNodeTypes.NoEnd); path = null; openList.Clear(); closeList.Clear(); currentNode = start; SearchPath(currentNode); } //寻路递归,受保护的虚方法,允许在子类重写寻路算法 protected virtual void SearchPath(StarNode starNode) { //currentNode = starNode; openList.Remove(starNode); closeList.Add(starNode); AddNodeToOpenList(); if (closeList.Contains(end)) { //如果终点在关闭列表中,找到路径 StarNode node=starNode.Parent; path = new StarNodeCollection(); do { path.Add(node); node = node.Parent; } while (node != null && !node.Equals(start)); path.Reverse(); return; } else if (openList.Count == 0) { //终点不在关闭列表,开放列表已空,没有可通行的路径 return; } currentNode = GetNextNode(); //迭代过程 SearchPath(currentNode); } /// <summary> /// 获得当前节点的下一个最佳节点 /// </summary> /// <returns></returns> public StarNode GetNextNode() { openList.SortByF(); return openList[0]; } /// <summary> /// 把当前点周围可通过的点加入到开放列表中 /// </summary>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值