Files
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

179 lines
5.7 KiB
C#

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
}