初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace PCL.Core.App.Configuration;
|
||||
|
||||
public delegate void ConfigMigrationHandler(string from, string to);
|
||||
|
||||
/// <summary>
|
||||
/// 配置文件迁移模型与工具。
|
||||
/// </summary>
|
||||
public class ConfigMigration
|
||||
{
|
||||
/// <summary>
|
||||
/// 来源路径。
|
||||
/// </summary>
|
||||
public required string From { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标路径。
|
||||
/// </summary>
|
||||
public required string To { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 优先级 (或称"权重"),数字越大越容易被使用。
|
||||
/// </summary>
|
||||
public int Priority { get; init; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 迁移实现。
|
||||
/// </summary>
|
||||
public required ConfigMigrationHandler OnMigration { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 执行配置文件迁移。
|
||||
/// </summary>
|
||||
/// <param name="target">最终目标路径</param>
|
||||
/// <param name="migrations">可用的迁移过程</param>
|
||||
/// <returns>若找到最简方案并迁移成功,则为 <c>true</c>,否则为 <c>false</c></returns>
|
||||
public static bool Migrate(string target, IEnumerable<ConfigMigration> migrations)
|
||||
{
|
||||
migrations = migrations as ConfigMigration[] ?? migrations.ToArray();
|
||||
IEnumerable<ConfigMigration>? solution = null;
|
||||
var found = (
|
||||
from migration in migrations.Reverse()
|
||||
let path = migration.From
|
||||
where File.Exists(path) && _TryFindShortestPath(path, target, migrations, out solution)
|
||||
select path
|
||||
).Any();
|
||||
if (!found) return false;
|
||||
foreach (var migration in solution!) migration.OnMigration(migration.From, migration.To);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 寻找最短路径
|
||||
// Partly generated by gpt-5 (20250904)
|
||||
// ReSharper disable InvertIf, ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
private static bool _TryFindShortestPath(string start, string end,
|
||||
IEnumerable<ConfigMigration> paths, [NotNullWhen(true)] out IEnumerable<ConfigMigration>? result)
|
||||
{
|
||||
// 起点即终点:最短过程为 0 条边
|
||||
if (start == end)
|
||||
{
|
||||
result = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
// 构建邻接表(有向图)
|
||||
var adj = new Dictionary<string, List<ConfigMigration>>(StringComparer.Ordinal);
|
||||
foreach (var p in paths)
|
||||
{
|
||||
if (!adj.TryGetValue(p.From, out var list))
|
||||
{
|
||||
list = [];
|
||||
adj[p.From] = list;
|
||||
}
|
||||
list.Add(p);
|
||||
}
|
||||
|
||||
// 第一阶段:BFS 计算从 start 到各点的最短边数 dist
|
||||
var dist = new Dictionary<string, int>(StringComparer.Ordinal);
|
||||
var queue = new Queue<string>();
|
||||
dist[start] = 0;
|
||||
queue.Enqueue(start);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var u = queue.Dequeue();
|
||||
if (!adj.TryGetValue(u, out var outgoing))
|
||||
continue;
|
||||
|
||||
var du = dist[u];
|
||||
foreach (var e in outgoing)
|
||||
{
|
||||
var v = e.To;
|
||||
if (!dist.ContainsKey(v))
|
||||
{
|
||||
dist[v] = du + 1;
|
||||
queue.Enqueue(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 不可达
|
||||
if (!dist.ContainsKey(end))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将节点按深度分层,便于第二阶段的 DP
|
||||
var nodesByDepth = new Dictionary<int, List<string>>();
|
||||
foreach (var kv in dist)
|
||||
{
|
||||
if (!nodesByDepth.TryGetValue(kv.Value, out var list))
|
||||
{
|
||||
list = [];
|
||||
nodesByDepth[kv.Value] = list;
|
||||
}
|
||||
list.Add(kv.Key);
|
||||
}
|
||||
|
||||
// 第二阶段:在所有最短路径子图上,选择累计 Priority 之和最大的路径
|
||||
var bestPriority = new Dictionary<string, long>(StringComparer.Ordinal); // 累计优先级和
|
||||
var prevNode = new Dictionary<string, string>(StringComparer.Ordinal); // 重建路径
|
||||
var prevEdge = new Dictionary<string, ConfigMigration>(StringComparer.Ordinal);
|
||||
|
||||
bestPriority[start] = 0;
|
||||
var maxDepth = dist[end];
|
||||
|
||||
for (var d = 0; d < maxDepth; d++)
|
||||
{
|
||||
if (!nodesByDepth.TryGetValue(d, out var layer))
|
||||
continue;
|
||||
|
||||
foreach (var u in layer)
|
||||
{
|
||||
if (!bestPriority.ContainsKey(u)) continue;
|
||||
|
||||
if (!adj.TryGetValue(u, out var outgoing)) continue;
|
||||
|
||||
foreach (var e in outgoing)
|
||||
{
|
||||
if (!dist.TryGetValue(e.To, out var dv) || dv != d + 1)
|
||||
continue; // 只考虑保持最短性的边
|
||||
|
||||
var candidate = bestPriority[u] + e.Priority;
|
||||
if (!bestPriority.TryGetValue(e.To, out var cur) || candidate > cur)
|
||||
{
|
||||
bestPriority[e.To] = candidate;
|
||||
prevNode[e.To] = u;
|
||||
prevEdge[e.To] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重建从 start 到 end 的路径(边序列)
|
||||
if (!prevEdge.ContainsKey(end)) { // 理论上不应发生
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
var path = new List<ConfigMigration>();
|
||||
var curr = end;
|
||||
while (curr != start)
|
||||
{
|
||||
var edge = prevEdge[curr];
|
||||
path.Add(edge);
|
||||
curr = prevNode[curr];
|
||||
}
|
||||
path.Reverse();
|
||||
result = path;
|
||||
return true;
|
||||
}
|
||||
// ReSharper restore InvertIf, ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
}
|
||||
Reference in New Issue
Block a user