Files
StandardSence/StandardScene.MagCarSimulator/Map/MapPathFinder.cs
T

214 lines
6.1 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.
using System.Collections.Generic;
using System.Text;
namespace StandardScene.MagCarSimulator
{
public static class MapPathFinder
{
/// <summary>
/// 生成往返循环:起点→终点→起点(终点不重复,回到起点靠下标回绕)。
/// </summary>
public static List<int> BuildLoop(SimpleLiteMap map, int startSiteId, int endSiteId)
{
if (map == null)
{
throw new System.InvalidOperationException("尚未加载地图");
}
if (startSiteId == endSiteId)
{
throw new System.InvalidOperationException($"起点和终点不能相同(站点 {startSiteId}");
}
if (!map.Sites.ContainsKey(startSiteId))
{
throw new System.InvalidOperationException($"地图中没有起点 {startSiteId}");
}
if (!map.Sites.ContainsKey(endSiteId))
{
throw new System.InvalidOperationException($"地图中没有终点 {endSiteId}");
}
var forward = FindPath(map, startSiteId, endSiteId);
if (forward == null)
{
throw new System.InvalidOperationException($"找不到路径 {startSiteId} → {endSiteId}");
}
var back = FindPath(map, endSiteId, startSiteId);
if (back == null)
{
throw new System.InvalidOperationException($"找不到返回路径 {endSiteId} → {startSiteId}");
}
var loop = new List<int>(forward.Count + back.Count);
loop.AddRange(forward);
for (var i = 1; i < back.Count - 1; i++)
{
loop.Add(back[i]);
}
if (loop.Count < 2)
{
throw new System.InvalidOperationException("循环路径至少需要两个站点");
}
return loop;
}
public static List<int> NormalizeLoop(IEnumerable<int> siteIds)
{
var loop = new List<int>();
if (siteIds == null)
{
return loop;
}
foreach (var id in siteIds)
{
if (id <= 0)
{
continue;
}
if (loop.Count > 0 && loop[loop.Count - 1] == id)
{
continue;
}
loop.Add(id);
}
if (loop.Count >= 2 && loop[0] == loop[loop.Count - 1])
{
loop.RemoveAt(loop.Count - 1);
}
return loop;
}
public static List<int> ResolveLoop(SimpleLiteMap map, MagCarSimVehicleConfig config)
{
if (map == null)
{
throw new System.InvalidOperationException("尚未加载地图");
}
if (config?.LoopSiteIds != null && config.LoopSiteIds.Count >= 3)
{
var loop = NormalizeLoop(config.LoopSiteIds);
if (loop.Count < 3)
{
throw new System.InvalidOperationException($"{config.Name} 环线站点不足");
}
foreach (var id in loop)
{
if (!map.Sites.ContainsKey(id))
{
throw new System.InvalidOperationException($"{config.Name} 环线站点 {id} 不在地图中");
}
}
return loop;
}
return BuildLoop(map, config.StartSiteId, config.EndSiteId);
}
public static string FormatPath(IReadOnlyList<int> siteIds, int maxShow = 0)
{
if (siteIds == null || siteIds.Count == 0)
{
return "";
}
var show = siteIds.Count;
var truncated = false;
if (maxShow > 0 && siteIds.Count > maxShow)
{
show = maxShow;
truncated = true;
}
var sb = new StringBuilder();
for (var i = 0; i < show; i++)
{
if (i > 0)
{
sb.Append("→");
}
sb.Append(siteIds[i]);
}
if (truncated)
{
sb.Append("→…共").Append(siteIds.Count).Append("站");
}
else
{
sb.Append("→").Append(siteIds[0]);
}
return sb.ToString();
}
public static List<int> FindPath(SimpleLiteMap map, int fromSiteId, int toSiteId)
{
if (fromSiteId == toSiteId)
{
return new List<int> { fromSiteId };
}
var queue = new Queue<int>();
var prev = new Dictionary<int, int>();
var visited = new HashSet<int> { fromSiteId };
queue.Enqueue(fromSiteId);
while (queue.Count > 0)
{
var current = queue.Dequeue();
if (!map.Adjacency.TryGetValue(current, out var nexts))
{
continue;
}
foreach (var next in nexts)
{
if (!visited.Add(next))
{
continue;
}
prev[next] = current;
if (next == toSiteId)
{
return Reconstruct(prev, fromSiteId, toSiteId);
}
queue.Enqueue(next);
}
}
return null;
}
private static List<int> Reconstruct(Dictionary<int, int> prev, int fromSiteId, int toSiteId)
{
var path = new List<int>();
var current = toSiteId;
path.Add(current);
while (current != fromSiteId)
{
current = prev[current];
path.Add(current);
}
path.Reverse();
return path;
}
}
}