Provinces.ExtraExits throws when a boss zone has no exit-path link to keep instead of silently keeping every link (cut one-exit, finding silent-keep-all-fallback)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9fd0fe96e7
commit
97aa371872
2 changed files with 19 additions and 15 deletions
|
|
@ -299,17 +299,22 @@ public static class Provinces
|
|||
}
|
||||
|
||||
// The crossing links to prune so each boss zone keeps one exit: all of them but the link to the boss zone's successor on
|
||||
// exitPath (the zones from the entrance to the exit), which is the link the run goes through. A boss zone that is not on
|
||||
// exitPath, or whose successor is not one of its crossing links, keeps all of its links: it is never left with none.
|
||||
public static (int Boss, int Far)[] ExtraExits(int[][] adjacency, IReadOnlyCollection<int> bossZones, int exit, int[] exitPath) =>
|
||||
CrossingLinks(adjacency, bossZones, exit).GroupBy(link => link.Boss)
|
||||
.SelectMany(links =>
|
||||
{
|
||||
var at = Array.IndexOf(exitPath, links.Key);
|
||||
var kept = at >= 0 && at + 1 < exitPath.Length ? exitPath[at + 1] : -1;
|
||||
return links.Any(link => link.Far == kept) ? links.Where(link => link.Far != kept) : Enumerable.Empty<(int Boss, int Far)>();
|
||||
})
|
||||
.ToArray();
|
||||
// exitPath (the zones from the entrance to the exit), which is the link the run goes through. The generator's exit path
|
||||
// passes through every boss zone by one of its crossing links; a boss zone off exitPath, last on it, or followed by a zone
|
||||
// it has no crossing link to breaks that, and throws naming the boss zone rather than guessing which link to keep.
|
||||
public static (int Boss, int Far)[] ExtraExits(int[][] adjacency, IReadOnlyCollection<int> bossZones, int exit, int[] exitPath)
|
||||
{
|
||||
var extra = new List<(int Boss, int Far)>();
|
||||
foreach (var links in CrossingLinks(adjacency, bossZones, exit).GroupBy(link => link.Boss))
|
||||
{
|
||||
var at = Array.IndexOf(exitPath, links.Key);
|
||||
var kept = at >= 0 && at + 1 < exitPath.Length ? exitPath[at + 1] : -1;
|
||||
if (!links.Any(link => link.Far == kept))
|
||||
throw new InvalidOperationException($"boss zone {links.Key} has no crossing link to its successor on the exit path, so no exit to keep");
|
||||
extra.AddRange(links.Where(link => link.Far != kept));
|
||||
}
|
||||
return extra.ToArray();
|
||||
}
|
||||
|
||||
// Each zone's region, named by the region's least zone index. Cutting every boss zone's crossing links (CrossingLinks)
|
||||
// leaves the regions as the connected components. Every zone, boss zones included, is in exactly one.
|
||||
|
|
|
|||
|
|
@ -543,10 +543,9 @@ public sealed class OreGeographyTests : IDisposable
|
|||
Assert.Empty(Provinces.ExtraExits(pruned, new[] {4, 1}, 7, path));
|
||||
Assert.Equal(Provinces.Regions(chain, new[] {4, 1}, 7), Provinces.Regions(pruned, new[] {4, 1}, 7));
|
||||
|
||||
// A boss zone off the path, last on it, or followed by a zone it has no crossing link to is never left with fewer links.
|
||||
Assert.Empty(Provinces.ExtraExits(diamond, new[] {2}, 6, new[] {0, 1, 5, 6}));
|
||||
Assert.Empty(Provinces.ExtraExits(diamond, new[] {2}, 6, new[] {0, 1, 2}));
|
||||
Assert.Empty(Provinces.ExtraExits(diamond, new[] {2}, 6, new[] {0, 1, 2, 5, 6}));
|
||||
// A boss zone off the path, last on it, or followed by a zone it has no crossing link to has no exit to keep: it throws, naming the boss zone.
|
||||
foreach (var broken in new[] {new[] {0, 1, 5, 6}, new[] {0, 1, 2}, new[] {0, 1, 2, 5, 6}})
|
||||
Assert.Contains("boss zone 2", Assert.Throws<InvalidOperationException>(() => Provinces.ExtraExits(diamond, new[] {2}, 6, broken)).Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Reference in a new issue