From d2d8949d182372f259c6bd5faf396ff7fb519034 Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Wed, 15 Feb 2023 16:48:35 +0100 Subject: [PATCH 1/2] Clean up Milestone analyser a bit And add more (disabled) debug logs --- YAFCmodel/Analysis/Milestones.cs | 87 +++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/YAFCmodel/Analysis/Milestones.cs b/YAFCmodel/Analysis/Milestones.cs index aae5ef58..ea3535ff 100644 --- a/YAFCmodel/Analysis/Milestones.cs +++ b/YAFCmodel/Analysis/Milestones.cs @@ -9,8 +9,8 @@ namespace YAFC.Model public class Milestones : Analysis { public static readonly Milestones Instance = new Milestones(); - - public FactorioObject[] currentMilestones; + + public FactorioObject[] currentMilestones; public Mapping milestoneResult; public ulong lockedMask { get; private set; } private Project project; @@ -54,10 +54,10 @@ public FactorioObject GetHighest(FactorioObject target, bool all) ms &= lockedMask; if (ms == 0) return null; - var msb = MathUtils.HighestBitSet(ms)-1; + var msb = MathUtils.HighestBitSet(ms) - 1; return msb < 0 || msb >= currentMilestones.Length ? null : currentMilestones[msb]; } - + [Flags] private enum ProcessingFlags : byte { @@ -81,12 +81,12 @@ public void ComputeWithParameters(Project project, ErrorCollector warnings, Fact this.project = project; project.settings.changed += ProjectSettingsChanged; } - + var time = Stopwatch.StartNew(); var result = Database.objects.CreateMapping(); var processing = Database.objects.CreateMapping(); var processingQueue = new Queue(); - + foreach (var rootAccessbile in Database.rootAccessible) { result[rootAccessbile] = 1; @@ -101,13 +101,14 @@ public void ComputeWithParameters(Project project, ErrorCollector warnings, Fact result[obj] = 1; processingQueue.Enqueue(obj.id); processing[obj] = ProcessingFlags.Initial | ProcessingFlags.InQueue; - } else if (flag.HasFlag(ProjectPerItemFlags.MarkedInaccessible)) + } + else if (flag.HasFlag(ProjectPerItemFlags.MarkedInaccessible)) processing[obj] = ProcessingFlags.ForceInaccessible; } if (autoSort) { - // Adding default milestones AND special flag to auto-order them + // Set special flag to auto-order the milestones, keep currentMilestones empty, as the milestones needs to be added later so they get ordered according to their dependencies foreach (var milestone in milestones) processing[milestone] |= ProcessingFlags.MilestoneNeedOrdering; currentMilestones = new FactorioObject[milestones.Length]; @@ -126,14 +127,14 @@ public void ComputeWithParameters(Project project, ErrorCollector warnings, Fact var nextMilestoneMask = 0x2ul; var nextMilestoneIndex = 0; var accessibleObjects = 0; - + var flagMask = 0ul; for (var i = 0; i <= currentMilestones.Length; i++) { flagMask |= 1ul << i; if (i > 0) { - var milestone = currentMilestones[i-1]; + var milestone = currentMilestones[i - 1]; if (milestone == null) { milestonesNotReachable = new List(); @@ -148,45 +149,63 @@ public void ComputeWithParameters(Project project, ErrorCollector warnings, Fact Array.Resize(ref currentMilestones, nextMilestoneIndex); break; } - Console.WriteLine("Processing milestone "+milestone.locName); - processingQueue.Enqueue(milestone.id); - processing[milestone] = ProcessingFlags.Initial | ProcessingFlags.InQueue; + else + { + Console.WriteLine("Queuing milestone {0} ({1}) [{2}]", milestone.name, milestone.id, milestone.GetType().Name); + + processingQueue.Enqueue(milestone.id); + processing[milestone] = ProcessingFlags.Initial | ProcessingFlags.InQueue; + } } while (processingQueue.Count > 0) { var elem = processingQueue.Dequeue(); - var entry = dependencyList[elem]; + var elemDeps = dependencyList[elem]; var cur = result[elem]; var eflags = cur; var isInitial = (processing[elem] & ProcessingFlags.Initial) != 0; processing[elem] &= ProcessingFlags.MilestoneNeedOrdering; - foreach (var list in entry) + // Console.WriteLine("Processing {0} ({1}) [{2}] -> isInitial: {3}", Database.objects[elem].name, Database.objects[elem].id, Database.objects[elem].GetType().Name, isInitial); + + foreach (var list in elemDeps) { + // Console.WriteLine(" -> {0}: req all {1}", list.flags, list.flags & DependencyList.Flags.RequireEverything); if ((list.flags & DependencyList.Flags.RequireEverything) != 0) { foreach (var req in list.elements) { var reqFlags = result[req]; + // Console.WriteLine(" -> dep all {0} ({1}) [{2}]: reqflags {3}", Database.objects[req].name, Database.objects[req].id, Database.objects[req].GetType().Name, reqFlags); if (reqFlags == 0 && !isInitial) goto skip; - eflags |= result[req]; + eflags |= reqFlags; } } else { + if (list.elements.Length == 0) + { + Console.WriteLine("Unexpected: {0} ({1}) [{2}] - {3} group deps empty, will cause unreachable elements", Database.objects[elem].name, Database.objects[elem].id, Database.objects[elem].GetType().Name, list.flags); + } + + // Minimize group (dependency) cost var groupFlags = 0ul; foreach (var req in list.elements) { - var acc = result[req]; - if (acc == 0) + var reqFlags = result[req]; + // Console.WriteLine(" -> dep grp {0} ({1}) [{2}]: reqflags {3}, groupFlags {4}", Database.objects[req].name, Database.objects[req].id, Database.objects[req].GetType().Name, reqFlags, groupFlags); + if (reqFlags == 0) + // Dependency is not available/processed yet, so check next continue; - if (acc < groupFlags || groupFlags == 0ul) - groupFlags = acc; + + if (reqFlags < groupFlags || groupFlags == 0ul) + groupFlags = reqFlags; } + // Console.WriteLine(" -> group flags {0}", groupFlags); if (groupFlags == 0 && !isInitial) goto skip; eflags |= groupFlags; @@ -195,32 +214,40 @@ public void ComputeWithParameters(Project project, ErrorCollector warnings, Fact if (!isInitial) { if (eflags == cur || (eflags | flagMask) != flagMask) + { + // Console.WriteLine(" -> Skipping: eflags {0}, flagMask {1}, cur {2}", eflags, flagMask, cur); continue; + } } else eflags &= flagMask; accessibleObjects++; - //var obj = Database.objects[elem]; - //Console.WriteLine("Added object "+obj.locName+" ["+obj.GetType().Name+"] with mask "+eflags.ToString("X") + " (was "+cur.ToString("X")+")"); + // Console.WriteLine(" -> Added object {0} ({1}) [{2}] with eflags {3} (was {4})", Database.objects[elem].name, Database.objects[elem].id, Database.objects[elem].GetType().Name, eflags, cur); if (processing[elem] == ProcessingFlags.MilestoneNeedOrdering) { + // Auto-sorting, elem was not added to currentMilestones yet, so add now its dependencies are solved processing[elem] = 0; eflags |= nextMilestoneMask; nextMilestoneMask <<= 1; currentMilestones[nextMilestoneIndex++] = Database.objects[elem]; } - + result[elem] = eflags; + // Add reverse dependencies to feed the algorithm with new processable elements foreach (var revdep in reverseDependencies[elem]) { if ((processing[revdep] & ~ProcessingFlags.MilestoneNeedOrdering) != 0 || result[revdep] != 0) + // Already/About to be processed continue; + + // Console.WriteLine(" -> Queuing rev dep {0} ({1}) [{2}]", Database.objects[revdep].name, Database.objects[revdep].id, Database.objects[revdep].GetType().Name); + processing[revdep] |= ProcessingFlags.InQueue; processingQueue.Enqueue(revdep); } - - skip:; + + skip:; } } @@ -235,20 +262,20 @@ public void ComputeWithParameters(Project project, ErrorCollector warnings, Fact var hasAutomatableRocketLaunch = result[Database.objectsByTypeName["Special.launch"]] != 0; if (accessibleObjects < Database.objects.count / 2) { - warnings.Error("More than 50% of all in-game objects appear to be inaccessible in this project with your current mod list. This can have a variety of reasons like objects being accessible via scripts," + + warnings.Error("More than 50% of all in-game objects appear to be inaccessible in this project with your current mod list. This can have a variety of reasons like objects being accessible via scripts," + MaybeBug + MilestoneAnalysisIsImportant + UseDependencyExplorer, ErrorSeverity.AnalysisWarning); - } + } else if (!hasAutomatableRocketLaunch) { - warnings.Error("Rocket launch appear to be inaccessible. This means that rocket may not be launched in this mod pack, or it requires mod script to spawn or unlock some items," + + warnings.Error("Rocket launch appear to be inaccessible. This means that rocket may not be launched in this mod pack, or it requires mod script to spawn or unlock some items," + MaybeBug + MilestoneAnalysisIsImportant + UseDependencyExplorer, ErrorSeverity.AnalysisWarning); - } + } else if (milestonesNotReachable != null) { warnings.Error("There are some milestones that are not accessible: " + string.Join(", ", milestonesNotReachable.Select(x => x.locName)) + ". You may remove these from milestone list," + MaybeBug + MilestoneAnalysisIsImportant + UseDependencyExplorer, ErrorSeverity.AnalysisWarning); } - Console.WriteLine("Milestones calculation finished in "+time.ElapsedMilliseconds+" ms."); + Console.WriteLine("Milestones calculation finished in {0} ms.", time.ElapsedMilliseconds); milestoneResult = result; } From 8eb150f5b82a59d162adeac5684164b60a21d11a Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Wed, 15 Feb 2023 16:56:10 +0100 Subject: [PATCH 2/2] Fix dependency collector to not collect empty lists --- YAFCmodel/Analysis/Dependencies.cs | 18 +++++++++++------- YAFCmodel/Analysis/Milestones.cs | 5 ++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/YAFCmodel/Analysis/Dependencies.cs b/YAFCmodel/Analysis/Dependencies.cs index b61eb976..9e4a0a3a 100644 --- a/YAFCmodel/Analysis/Dependencies.cs +++ b/YAFCmodel/Analysis/Dependencies.cs @@ -65,17 +65,21 @@ private class DependencyCollector : IDependencyCollector { private readonly List list = new List(); - public void Add(FactorioId[] raw, DependencyList.Flags flags) + public void Add(FactorioId[] elements, DependencyList.Flags flags) { - list.Add(new DependencyList {elements = raw, flags = flags}); + // Only add lists that actually contain elements, lists that are used to hide objects, or lists to unlock technologies (because of the lack of unlocking dependencies those should be unavailable) + if (elements.Length > 0 || flags == DependencyList.Flags.Hidden || flags == DependencyList.Flags.TechnologyUnlock) + { + list.Add(new DependencyList { elements = elements, flags = flags }); + } } - public void Add(IReadOnlyList raw, DependencyList.Flags flags) + public void Add(IReadOnlyList readOnlyList, DependencyList.Flags flags) { - var elems = new FactorioId[raw.Count]; - for (var i = 0; i < raw.Count; i++) - elems[i] = raw[i].id; - list.Add(new DependencyList {elements = elems, flags = flags}); + var elems = new FactorioId[readOnlyList.Count]; + for (var i = 0; i < readOnlyList.Count; i++) + elems[i] = readOnlyList[i].id; + Add(elems, flags); } public DependencyList[] Pack() diff --git a/YAFCmodel/Analysis/Milestones.cs b/YAFCmodel/Analysis/Milestones.cs index ea3535ff..f4ff5076 100644 --- a/YAFCmodel/Analysis/Milestones.cs +++ b/YAFCmodel/Analysis/Milestones.cs @@ -188,7 +188,10 @@ public void ComputeWithParameters(Project project, ErrorCollector warnings, Fact { if (list.elements.Length == 0) { - Console.WriteLine("Unexpected: {0} ({1}) [{2}] - {3} group deps empty, will cause unreachable elements", Database.objects[elem].name, Database.objects[elem].id, Database.objects[elem].GetType().Name, list.flags); + if ((list.flags & DependencyList.Flags.Hidden) != DependencyList.Flags.Hidden && (list.flags & DependencyList.Flags.TechnologyUnlock) != DependencyList.Flags.TechnologyUnlock) + { + Console.WriteLine("Unexpected: {0} ({1}) [{2}] - {3} group deps empty, will cause unreachable elements", Database.objects[elem].name, Database.objects[elem].id, Database.objects[elem].GetType().Name, list.flags); + } } // Minimize group (dependency) cost