Files
Kg.SeaTime/Service/Application.Domain/Tool/MapPath/MapRunTool.cs
2026-05-25 18:41:27 +08:00

692 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace Application.Domain;
// ===================== 核心定义:全局坐标体系 =====================
/// <summary>
/// 全局坐标结构体(所有城市共享唯一坐标)
/// </summary>
public struct Point
{
public int X; // 全局X坐标
public int Y; // 全局Y坐标
public Point(int x, int y)
{
X = x;
Y = y;
}
public override bool Equals(object obj)
{
return obj is Point point && X == point.X && Y == point.Y;
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
public static bool operator ==(Point a, Point b) => a.X == b.X && a.Y == b.Y;
public static bool operator !=(Point a, Point b) => !(a == b);
public override string ToString() => $"({X},{Y})";
}
/// <summary>
/// 城市节点(全局坐标管辖范围)
/// </summary>
public class CityNode
{
public int CityId { get; set; } // 城市ID
public string CityName { get; set; } // 城市名称
public Point CoreAnchor { get; set; } // 核心锚点(全局坐标)
public string CoreAnchorName { get; set; } // 核心锚点名称
public int MinX { get; set; } // 城市管辖范围-最小X全局
public int MaxX { get; set; } // 城市管辖范围-最大X全局
public int MinY { get; set; } // 城市管辖范围-最小Y全局
public int MaxY { get; set; } // 城市管辖范围-最大Y全局
public List<InterCityPortal> Portals { get; set; } // 所属传送点(全局坐标)
public CityNode(int cityId, string cityName, Point coreAnchor, string coreAnchorName,
int minX, int maxX, int minY, int maxY)
{
CityId = cityId;
CityName = cityName;
CoreAnchor = coreAnchor;
CoreAnchorName = coreAnchorName;
MinX = minX;
MaxX = maxX;
MinY = minY;
MaxY = maxY;
Portals = new List<InterCityPortal>();
}
/// <summary>
/// 校验全局坐标是否属于当前城市管辖范围
/// </summary>
public bool IsPointInCity(Point point)
{
return point.X >= MinX && point.X <= MaxX && point.Y >= MinY && point.Y <= MaxY;
}
public void AddPortal(InterCityPortal portal)
{
if (!Portals.Any(p => p.PortalId == portal.PortalId))
Portals.Add(portal);
}
}
/// <summary>
/// 城际传送点全局坐标PortalId改为字符串
/// </summary>
public class InterCityPortal
{
public string PortalId { get; set; } // 1. 改为字符串类型
public int BelongCityId { get; set; } // 所属城市ID
public Point Position { get; set; } // 传送点全局坐标
public int TargetCityId { get; set; } // 目标城市ID
public string PortalName { get; set; } // 传送点名称
public int Priority { get; set; } = 1; // 优先级
public string? ReversePortalId { get; set; } // 反向传送点ID字符串
public InterCityPortal(string portalId, int belongCityId, Point pos, int targetCityId, string name,
int priority = 1, string? reversePortalId = null)
{
PortalId = portalId;
BelongCityId = belongCityId;
Position = pos;
TargetCityId = targetCityId;
PortalName = name;
Priority = priority;
ReversePortalId = reversePortalId;
}
}
/// <summary>
/// 全局路网(基于全局坐标管理城市+传送点)
/// </summary>
public class GlobalRoadNetwork
{
public Dictionary<int, CityNode> CityDict { get; private set; }
public Dictionary<string, InterCityPortal> PortalDict { get; private set; } // 适配字符串PortalId
public GlobalRoadNetwork()
{
CityDict = new Dictionary<int, CityNode>();
PortalDict = new Dictionary<string, InterCityPortal>(); // 改为字符串Key
}
public void AddCity(CityNode city)
{
if (!CityDict.ContainsKey(city.CityId))
CityDict.Add(city.CityId, city);
}
public void AddPortal(InterCityPortal portal)
{
if (!PortalDict.ContainsKey(portal.PortalId))
{
PortalDict.Add(portal.PortalId, portal);
if (CityDict.ContainsKey(portal.BelongCityId))
CityDict[portal.BelongCityId].AddPortal(portal);
}
}
/// <summary>
/// 根据全局坐标获取所属城市
/// </summary>
public CityNode GetCityByGlobalPoint(Point point)
{
return CityDict.Values.FirstOrDefault(city => city.IsPointInCity(point));
}
/// <summary>
/// 获取最优传送点(修复反向寻路筛选逻辑)
/// </summary>
public InterCityPortal GetOptimalPortal(int startCityId, int targetCityId, Point startPos,
Dictionary<int, CityRoadNetwork> cityNetworks, bool isReverse = false)
{
if (!CityDict.ContainsKey(startCityId) || !cityNetworks.ContainsKey(startCityId))
return null;
var startCity = CityDict[startCityId];
var startCityNetwork = cityNetworks[startCityId];
// 修复反向寻路筛选逻辑:
// - 正向:当前城市传送点 → 目标城市
// - 反向:目标城市传送点 → 当前城市(但从当前城市视角找)
var candidatePortals = isReverse
? CityDict.Values
.Where(c => c.CityId == targetCityId)
.SelectMany(c => c.Portals)
.Where(p => p.TargetCityId == startCityId)
.Where(p => startCityNetwork.IsPointWalkable(p.Position))
.Where(p => startCityNetwork.FindPathInCity(startPos, p.Position) != null)
: startCity.Portals
.Where(p => p.TargetCityId == targetCityId)
.Where(p => startCityNetwork.IsPointWalkable(p.Position))
.Where(p => startCityNetwork.FindPathInCity(startPos, p.Position) != null);
// 计算距离并排序
var sortedPortals = candidatePortals
.Select(p => new
{
Portal = p,
Distance = GetManhattanDistance(startPos, p.Position)
})
.OrderBy(p => p.Portal.Priority)
.ThenBy(p => p.Distance);
return sortedPortals.FirstOrDefault()?.Portal;
}
/// <summary>
/// 获取反向传送点适配字符串PortalId
/// </summary>
public InterCityPortal GetReversePortal(InterCityPortal portal)
{
if (!string.IsNullOrEmpty(portal.ReversePortalId) && PortalDict.ContainsKey(portal.ReversePortalId))
return PortalDict[portal.ReversePortalId];
return null;
}
/// <summary>
/// 校验全局坐标合法性(属于某城市+该城市内可通行)
/// </summary>
public bool IsPointValid(Point point, Dictionary<int, CityRoadNetwork> cityNetworks)
{
var city = GetCityByGlobalPoint(point);
if (city == null || !cityNetworks.ContainsKey(city.CityId))
return false;
return cityNetworks[city.CityId].IsPointWalkable(point);
}
/// <summary>
/// 修复使用BFS获取所有连通的城市链支持多中转
/// </summary>
public List<int> GetConnectedCityChain(int startCityId, int endCityId)
{
if (startCityId == endCityId)
return new List<int> { startCityId };
// BFS队列存储(当前城市ID, 路径)
var queue = new Queue<Tuple<int, List<int>>>();
var visited = new HashSet<int>();
queue.Enqueue(Tuple.Create(startCityId, new List<int> { startCityId }));
visited.Add(startCityId);
while (queue.Count > 0)
{
var current = queue.Dequeue();
int currCityId = current.Item1;
List<int> path = current.Item2;
// 获取当前城市所有可达的传送点目标城市
var reachableCities = CityDict[currCityId].Portals
.Select(p => p.TargetCityId)
.Distinct()
.Where(c => !visited.Contains(c));
foreach (int nextCityId in reachableCities)
{
var newPath = new List<int>(path) { nextCityId };
// 找到终点,返回路径
if (nextCityId == endCityId)
return newPath;
visited.Add(nextCityId);
queue.Enqueue(Tuple.Create(nextCityId, newPath));
}
}
return null; // 无可达路线
}
private int GetManhattanDistance(Point a, Point b)
{
return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y);
}
}
public class AStarNode : IComparable<AStarNode>
{
public Point Position { get; set; }
public AStarNode Parent { get; set; }
public int G { get; set; } // 起点到当前节点的代价
public int H { get; set; } // 当前节点到终点的预估代价
public int F => G + H; // 总代价
// 实现IComparable接口解决SortedSet重复元素问题
public int CompareTo(AStarNode other)
{
if (other == null) return 1;
// 先按F值排序再按H值最后按坐标避免重复
int fCompare = F.CompareTo(other.F);
if (fCompare != 0) return fCompare;
int hCompare = H.CompareTo(other.H);
if (hCompare != 0) return hCompare;
int xCompare = Position.X.CompareTo(other.Position.X);
return xCompare != 0 ? xCompare : Position.Y.CompareTo(other.Position.Y);
}
}
/// <summary>
/// 单城市路网(基于全局坐标管理可通行区域)
/// </summary>
public class CityRoadNetwork
{
public int CityId { get; set; }
public Dictionary<Point, bool> WalkablePoints { get; private set; } // 全局坐标可通行列表
public CityRoadNetwork(int cityId)
{
CityId = cityId;
WalkablePoints = new Dictionary<Point, bool>();
}
/// <summary>
/// 添加全局坐标可通行点
/// </summary>
public void AddWalkablePoint(int x, int y, bool walkable = true)
{
var point = new Point(x, y);
WalkablePoints[point] = walkable;
}
/// <summary>
/// 批量添加全局坐标可通行区域
/// </summary>
public void AddWalkableRect(int minX, int maxX, int minY, int maxY, bool walkable = true)
{
if (minX > maxX || minY > maxY)
throw new ArgumentException("矩形范围不合法min不能大于max");
for (int x = minX; x <= maxX; x++)
for (int y = minY; y <= maxY; y++)
AddWalkablePoint(x, y, walkable);
}
/// <summary>
/// 校验全局坐标是否可走
/// </summary>
public bool IsPointWalkable(Point point)
{
return WalkablePoints.TryGetValue(point, out bool walkable) && walkable;
}
/// <summary>
/// 修复后的A*寻路(基于全局坐标)
/// </summary>
public List<Point> FindPathInCity(Point start, Point end)
{
// 基础校验
if (!WalkablePoints.ContainsKey(start) || !WalkablePoints.ContainsKey(end))
return null;
if (!IsPointWalkable(start) || !IsPointWalkable(end))
return null;
if (start == end)
return new List<Point> { start };
// 开放列表(排序集合)和关闭列表
var openSet = new SortedSet<AStarNode>();
var openDict = new Dictionary<Point, AStarNode>(); // 快速查找
var closeDict = new Dictionary<Point, AStarNode>();
// 初始化起点
var startNode = new AStarNode
{
Position = start,
Parent = null,
G = 0,
H = GetManhattanDistance(start, end)
};
openSet.Add(startNode);
openDict[start] = startNode;
// 四方向移动
var dirs = new[] { new Point(0, 1), new Point(0, -1), new Point(1, 0), new Point(-1, 0) };
while (openSet.Count > 0)
{
// 获取F值最小的节点
var currentNode = openSet.Min;
openSet.Remove(currentNode);
openDict.Remove(currentNode.Position);
closeDict[currentNode.Position] = currentNode;
// 到达终点,回溯路径
if (currentNode.Position == end)
{
var path = new List<Point>();
var node = currentNode;
while (node != null)
{
path.Add(node.Position);
node = node.Parent;
}
path.Reverse();
return path;
}
// 遍历邻接节点
foreach (var dir in dirs)
{
var neighborPos = new Point(currentNode.Position.X + dir.X, currentNode.Position.Y + dir.Y);
// 跳过不可通行或已关闭的节点
if (!IsPointWalkable(neighborPos) || closeDict.ContainsKey(neighborPos))
continue;
// 计算新的G值
int newG = currentNode.G + 1;
// 邻接节点不存在或找到更优路径
if (!openDict.TryGetValue(neighborPos, out var neighborNode) || newG < neighborNode.G)
{
// 如果节点已存在,先移除
if (neighborNode != null)
{
openSet.Remove(neighborNode);
openDict.Remove(neighborPos);
}
// 创建/更新节点
neighborNode = new AStarNode
{
Position = neighborPos,
Parent = currentNode,
G = newG,
H = GetManhattanDistance(neighborPos, end)
};
openSet.Add(neighborNode);
openDict[neighborPos] = neighborNode;
}
}
}
// 无路径
return null;
}
private int GetManhattanDistance(Point a, Point b)
{
return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y);
}
}
// ===================== 路径步骤定义 =====================
public enum PathStepType
{
Move,
Teleport
}
public class PathStep
{
public int CityId { get; set; }
public Point Position { get; set; } // 全局坐标
public PathStepType StepType { get; set; }
public int? TargetCityId { get; set; }
public Point? TargetAnchor { get; set; } // 目标锚点全局坐标
public string? TargetAnchorName { get; set; }
public string ToWapDesc(GlobalRoadNetwork globalNetwork)
{
var cityName = globalNetwork?.CityDict?.ContainsKey(CityId) == true
? globalNetwork.CityDict[CityId].CityName
: "未知城市";
return StepType switch
{
PathStepType.Move => $"【{cityName}】移动到全局坐标{Position}",
PathStepType.Teleport => GetTeleportDesc(globalNetwork, cityName),
_ => string.Empty
};
}
private string GetTeleportDesc(GlobalRoadNetwork globalNetwork, string currentCityName)
{
if (globalNetwork == null || !TargetCityId.HasValue)
return $"【{currentCityName}】通过传送阵→【未知城市】未知锚点";
var targetCityName = globalNetwork.CityDict.ContainsKey(TargetCityId.Value)
? globalNetwork.CityDict[TargetCityId.Value].CityName
: "未知城市";
var anchorName = string.IsNullOrEmpty(TargetAnchorName) ? "未知锚点" : TargetAnchorName;
var anchorPos = TargetAnchor.HasValue ? $"(全局坐标{TargetAnchor}" : "";
return $"【{currentCityName}】通过传送阵→【{targetCityName}】{anchorName}{anchorPos}";
}
}
// ===================== 跨城寻路核心服务(全自动适配) =====================
public class CrossCityPathFinder
{
private readonly GlobalRoadNetwork _globalNetwork;
private readonly Dictionary<int, CityRoadNetwork> _cityNetworks;
public CrossCityPathFinder(GlobalRoadNetwork globalNetwork)
{
_globalNetwork = globalNetwork;
_cityNetworks = new Dictionary<int, CityRoadNetwork>();
}
public void AddCityRoadNetwork(CityRoadNetwork cityNetwork)
{
if (!_cityNetworks.ContainsKey(cityNetwork.CityId))
_cityNetworks.Add(cityNetwork.CityId, cityNetwork);
}
/// <summary>
/// 核心方法:输入起止点,全自动判定同城/跨城/多城市,自动适配正向/反向
/// </summary>
public List<PathStep> FindCrossCityPath(Point startPos, Point endPos)
{
// 1. 基础校验:识别起止点所属城市
var startCity = _globalNetwork.GetCityByGlobalPoint(startPos);
var endCity = _globalNetwork.GetCityByGlobalPoint(endPos);
if (startCity == null || endCity == null)
{
return null;
}
// 2. 同城寻路
if (startCity.CityId == endCity.CityId)
{
return FindSameCityPath(startCity.CityId, startPos, endPos);
}
// 3. 跨城/多城市寻路使用修复后的BFS路径链
var cityChain = _globalNetwork.GetConnectedCityChain(startCity.CityId, endCity.CityId);
if (cityChain == null || cityChain.Count < 2)
{
return null;
}
// 自动适配正向/反向
var cityStartPoints = new Dictionary<int, Point>
{
{ startCity.CityId, startPos },
{ endCity.CityId, endPos }
};
// 自动判断是否需要反向寻路
var isReverse = IsNeedReverseRoute(startCity.CityId, endCity.CityId);
return FindMultiCityPath(cityChain, cityStartPoints, isReverse);
}
/// <summary>
/// 同城寻路
/// </summary>
private List<PathStep> FindSameCityPath(int cityId, Point startPos, Point endPos)
{
if (!_globalNetwork.IsPointValid(startPos, _cityNetworks) ||
!_globalNetwork.IsPointValid(endPos, _cityNetworks))
{
return null;
}
var cityPath = _cityNetworks[cityId].FindPathInCity(startPos, endPos);
return cityPath?.Select(p => new PathStep
{
CityId = cityId,
Position = p,
StepType = PathStepType.Move
}).ToList();
}
/// <summary>
/// 多城市寻路(自动适配正向/反向)
/// </summary>
private List<PathStep> FindMultiCityPath(List<int> cityChain, Dictionary<int, Point> cityStartPoints,
bool isReverse)
{
var finalPath = new List<PathStep>();
var actualChain = isReverse ? cityChain.AsEnumerable().Reverse().ToList() : cityChain;
for (int i = 0; i < actualChain.Count - 1; i++)
{
int currCityId = actualChain[i];
int nextCityId = actualChain[i + 1];
Point currStartPoint = cityStartPoints.ContainsKey(currCityId)
? cityStartPoints[currCityId]
: _globalNetwork.CityDict[currCityId].CoreAnchor;
// 校验起点坐标归属
var currCity = _globalNetwork.CityDict[currCityId];
if (!currCity.IsPointInCity(currStartPoint))
{
return null;
}
if (!_globalNetwork.IsPointValid(currStartPoint, _cityNetworks))
return null;
// 获取最优传送点(自动传入反向标识)
var optimalPortal =
_globalNetwork.GetOptimalPortal(currCityId, nextCityId, currStartPoint, _cityNetworks, isReverse);
if (optimalPortal == null)
{
return null;
}
// 当前城市内:起点 → 传送点
var currToPortal = _cityNetworks[currCityId].FindPathInCity(currStartPoint, optimalPortal.Position);
if (currToPortal == null)
return null;
finalPath.AddRange(currToPortal.Select(p => new PathStep
{
CityId = currCityId,
Position = p,
StepType = PathStepType.Move
}));
// 跨城传送
var nextCity = _globalNetwork.CityDict[nextCityId];
finalPath.Add(new PathStep
{
CityId = currCityId,
Position = optimalPortal.Position,
StepType = PathStepType.Teleport,
TargetCityId = nextCityId,
TargetAnchor = nextCity.CoreAnchor,
TargetAnchorName = nextCity.CoreAnchorName
});
// 更新下一个城市的起点
if (!cityStartPoints.ContainsKey(nextCityId))
cityStartPoints.Add(nextCityId, nextCity.CoreAnchor);
// 最后一段:核心锚点→终点
if (i == actualChain.Count - 2)
{
Point nextEndPoint = cityStartPoints[nextCityId];
var anchorToEnd = _cityNetworks[nextCityId].FindPathInCity(nextCity.CoreAnchor, nextEndPoint);
if (anchorToEnd == null)
return null;
finalPath.AddRange(anchorToEnd.Select(p => new PathStep
{
CityId = nextCityId,
Position = p,
StepType = PathStepType.Move
}));
}
}
return finalPath;
}
/// <summary>
/// 自动判断是否需要反向寻路
/// </summary>
private bool IsNeedReverseRoute(int startCityId, int endCityId)
{
// 检查正向传送点
var startCity = _globalNetwork.CityDict[startCityId];
var hasForward = startCity.Portals.Any(p => p.TargetCityId == endCityId);
// 正向无则反向
if (!hasForward)
{
var endCity = _globalNetwork.CityDict[endCityId];
var hasReverse = endCity.Portals.Any(p => p.TargetCityId == startCityId);
return hasReverse;
}
return false;
}
/// <summary>
/// 辅助方法:获取城市名称
/// </summary>
private string GetCityName(CityNode city)
{
return city?.CityName ?? "未知城市";
}
/// <summary>
/// 简化路径描述(用于展示)
/// </summary>
public List<string> SimplifyPathForWap(List<PathStep> fullPath)
{
if (fullPath == null || fullPath.Count == 0)
return new List<string>();
var simplified = new List<string>();
PathStep? lastTeleport = null;
foreach (var step in fullPath)
{
if (step.StepType == PathStepType.Teleport)
{
simplified.Add(step.ToWapDesc(_globalNetwork));
lastTeleport = step;
}
else if (lastTeleport == null && step == fullPath.First())
{
var cityName = _globalNetwork.CityDict.ContainsKey(step.CityId)
? _globalNetwork.CityDict[step.CityId].CityName
: "未知城市";
simplified.Add($"【{cityName}】从起点(全局坐标{step.Position})出发");
}
else if (step == fullPath.Last())
{
var cityName = _globalNetwork.CityDict.ContainsKey(step.CityId)
? _globalNetwork.CityDict[step.CityId].CityName
: "未知城市";
simplified.Add($"【{cityName}】到达终点(全局坐标{step.Position}");
}
}
return simplified;
}
}