Frame/Assets/Scripts/BFS/BFS.cs

271 lines
7.8 KiB
C#
Raw Normal View History

2024-04-04 23:51:14 +08:00
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;
namespace Game.Pathfinding;
public class Node<T>
{
public int index { get; private set; }
public T data { get; private set; }
2024-04-04 23:51:14 +08:00
public Node(int index, T data)
{
this.index = index;
this.data = data;
}
2024-04-04 23:51:14 +08:00
}
class Edge<T>
2024-04-04 23:51:14 +08:00
{
public Node<T> from { get; private set; }
public Node<T> to { get; private set; }
public bool canTranslate { get; private set; }
2024-04-04 23:51:14 +08:00
public Edge(Node<T> from, Node<T> to, bool canTranslate = true)
2024-04-04 23:51:14 +08:00
{
this.from = from;
this.to = to;
this.canTranslate = canTranslate;
2024-04-04 23:51:14 +08:00
}
public Node<T> GetAnotherNode(Node<T> node)
2024-04-04 23:51:14 +08:00
{
return node == this.from ? to : to == node ? from : throw new InvalidOperationException();
2024-04-04 23:51:14 +08:00
}
}
2024-04-04 23:51:14 +08:00
public class WayPointGraph : Graph<WayPoint>
{
readonly struct UniqueWayPoint : IEquatable<UniqueWayPoint>
2024-04-04 23:51:14 +08:00
{
private readonly WayPoint _wayPoint;
2024-04-04 23:51:14 +08:00
public UniqueWayPoint(WayPoint wayPoint)
{
this._wayPoint = wayPoint;
}
2024-04-04 23:51:14 +08:00
public bool Equals(UniqueWayPoint other)
{
return _wayPoint.position == other._wayPoint.position;
}
2024-04-04 23:51:14 +08:00
public override bool Equals(object obj)
{
return obj is UniqueWayPoint other && Equals(other);
}
public override int GetHashCode()
{
return _wayPoint.position.GetHashCode();
}
public static implicit operator WayPoint(UniqueWayPoint uniqueWayPoint)
{
return uniqueWayPoint._wayPoint;
}
public static implicit operator UniqueWayPoint(WayPoint wayPoint)
{
return new UniqueWayPoint(wayPoint);
}
2024-04-04 23:51:14 +08:00
}
public void Initialize(GameGlobalConfig.NodeMap[] nodeMaps)
2024-04-04 23:51:14 +08:00
{
HashSet<UniqueWayPoint> wayPoints = new HashSet<UniqueWayPoint>();
foreach (var nodeMap in nodeMaps)
{
wayPoints.Add(new WayPoint(nodeMap.node1));
wayPoints.Add(new WayPoint(nodeMap.node2));
}
Dictionary<Vector3, Node<WayPoint>> position2NodeMap = new Dictionary<Vector3, Node<WayPoint>>();
_nodes = new Node<WayPoint>[wayPoints.Count];
using var enumerator = wayPoints.GetEnumerator();
int index = 0;
while (enumerator.MoveNext())
2024-04-04 23:51:14 +08:00
{
var enumeratorCurrent = (WayPoint)enumerator.Current;
var node = new Node<WayPoint>(index, enumeratorCurrent);
this._nodes[index] = node;
position2NodeMap[enumeratorCurrent.position] = node;
index++;
}
_edges = new List<Edge<WayPoint>>(nodeMaps.Length);
this._connection = new Dictionary<Node<WayPoint>, List<Edge<WayPoint>>>(_edges.Count);
foreach (var nodeMap in nodeMaps)
{
if (position2NodeMap.TryGetValue(nodeMap.node1, out var node1))
2024-04-04 23:51:14 +08:00
{
if (position2NodeMap.TryGetValue(nodeMap.node2, out var node2))
2024-04-04 23:51:14 +08:00
{
var edge = new Edge<WayPoint>(node1, node2);
_edges.Add(edge);
if (!this._connection.TryGetValue(node1, out var list1))
{
this._connection[node1] = list1 = new List<Edge<WayPoint>>();
}
list1.Add(edge);
if (!this._connection.TryGetValue(node2, out var list2))
{
this._connection[node2] = list2 = new List<Edge<WayPoint>>();
}
list2.Add(edge);
2024-04-04 23:51:14 +08:00
}
}
}
}
}
public class Graph<T>
2024-04-04 23:51:14 +08:00
{
private protected Node<T>[] _nodes;
private protected List<Edge<T>> _edges;
private protected Dictionary<Node<T>, List<Edge<T>>> _connection;
2024-04-04 23:51:14 +08:00
internal Node<T> GetNode(int index)
2024-04-04 23:51:14 +08:00
{
if (index < 0 || index >= this._nodes.Length - 1)
2024-04-04 23:51:14 +08:00
{
throw new IndexOutOfRangeException($"Index is {index}");
2024-04-04 23:51:14 +08:00
}
return this._nodes[index];
}
2024-04-04 23:51:14 +08:00
[CanBeNull]
internal Node<T> GetNode(Predicate<Node<T>> predicate)
{
foreach (var node in this._nodes)
{
if (predicate(node))
{
return node;
}
}
2024-04-04 23:51:14 +08:00
return null;
}
2024-04-04 23:51:14 +08:00
internal IReadOnlyList<Edge<T>> GetNeighbours(Node<T> node)
{
this._connection.TryGetValue(node, out var edges);
return edges;
}
2024-04-04 23:51:14 +08:00
}
public class BFS<T>
2024-04-04 23:51:14 +08:00
{
private Graph<T> _graph;
private List<int> _closeList = new List<int>();
private Queue<int> _openList = new Queue<int>();
private Dictionary<Node<T>, Node<T>> _nodeMap = new Dictionary<Node<T>, Node<T>>();
2024-04-04 23:51:14 +08:00
public BFS(Graph<T> graph)
2024-04-04 23:51:14 +08:00
{
this._graph = graph;
2024-04-04 23:51:14 +08:00
}
[CanBeNull]
public Node<T> GetNode(Predicate<Node<T>> predicate)
2024-04-04 23:51:14 +08:00
{
return _graph.GetNode(predicate);
2024-04-04 23:51:14 +08:00
}
public Path<T> FindPath(Node<T> begin, Node<T> end)
2024-04-04 23:51:14 +08:00
{
_openList.Clear();
_closeList.Clear();
_openList.Enqueue(begin.index);
while (this._openList.Count > 0)
2024-04-04 23:51:14 +08:00
{
var nodeIndex = this._openList.Dequeue();
var currNode = this._graph.GetNode(nodeIndex);
if (currNode.index == end.index)
break;
_closeList.Add(currNode.index);
Search(currNode);
2024-04-04 23:51:14 +08:00
}
Path<T> path = new Path<T>();
Node<T> curr = end;
path.AddNode(new PathNode<T>(curr.index, curr.data));
while (_nodeMap.TryGetValue(curr, out curr))
2024-04-04 23:51:14 +08:00
{
path.AddNode(new PathNode<T>(curr.index, curr.data));
if (curr == begin)
break;
2024-04-04 23:51:14 +08:00
}
path.Reverse();
return path;
2024-04-04 23:51:14 +08:00
}
private void Search(Node<T> node)
{
var readOnlyList = this._graph.GetNeighbours(node);
foreach (var edge in readOnlyList)
{
if (!edge.canTranslate)
continue;
Node<T> another = edge.GetAnotherNode(node);
if (!this._nodeMap.ContainsKey(another))
{
_nodeMap[another] = node;
}
2024-04-04 23:51:14 +08:00
if (!this._closeList.Contains(another.index))
{
_openList.Enqueue(another.index);
}
}
}
2024-04-04 23:51:14 +08:00
}
public class Path<T>
2024-04-04 23:51:14 +08:00
{
private readonly List<PathNode<T>> _pathNodes = new List<PathNode<T>>();
2024-04-04 23:51:14 +08:00
public void Reverse()
2024-04-04 23:51:14 +08:00
{
this._pathNodes.Reverse();
}
public void AddNode(PathNode<T> node)
{
this._pathNodes.Add(node);
2024-04-04 23:51:14 +08:00
}
public IReadOnlyList<PathNode<T>> GetNodes()
{
return this._pathNodes;
}
2024-04-04 23:51:14 +08:00
public void GetDatas(IList<T> result)
{
foreach (var pathNode in this._pathNodes)
{
result.Add(pathNode.data);
}
}
2024-04-04 23:51:14 +08:00
}
public class PathNode<T>
{
public int index { get; private set; }
public T data { get; private set; }
public PathNode(int index, T data)
{
this.index = index;
this.data = data;
}
}