using System.Collections.Generic; using System.Text; namespace StandardScene.MagCarSimulator { public static class MapPathFinder { /// /// 生成往返循环:起点→终点→起点(终点不重复,回到起点靠下标回绕)。 /// public static List 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(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 NormalizeLoop(IEnumerable siteIds) { var loop = new List(); 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 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 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 FindPath(SimpleLiteMap map, int fromSiteId, int toSiteId) { if (fromSiteId == toSiteId) { return new List { fromSiteId }; } var queue = new Queue(); var prev = new Dictionary(); var visited = new HashSet { 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 Reconstruct(Dictionary prev, int fromSiteId, int toSiteId) { var path = new List(); var current = toSiteId; path.Add(current); while (current != fromSiteId) { current = prev[current]; path.Add(current); } path.Reverse(); return path; } } }