diff --git a/src/Blocktavius.Core/Generators/Hills/AdamantCliffBuilder.cs b/src/Blocktavius.Core/Generators/Hills/AdamantCliffBuilder.cs index aca1ecd..7aecbb9 100644 --- a/src/Blocktavius.Core/Generators/Hills/AdamantCliffBuilder.cs +++ b/src/Blocktavius.Core/Generators/Hills/AdamantCliffBuilder.cs @@ -697,9 +697,7 @@ public Span GetWritableBuffer(int xStart, int runLength) /// /// Holds the state needed to generate a single run. - /// If we succeed, we recurse. - /// If we cannot, we backtrack to an earlier state and rely on the mutable state - /// of the shared PRNG to explore other possible recursions. + /// Uses exhaustive backtracking instead of random sampling with arbitrary retry limits. /// record struct LayerGenerator(Shared shared, int xStart, int y) { @@ -710,65 +708,74 @@ public bool Execute() return true; } - var runLengths = shared.legalRunLengths; + // Get all valid run lengths that can end at valid positions + // Copy to local variables to avoid struct member access in lambda + var backstop = shared.backstop; + var currentXStart = xStart; + var validRunLengths = shared.legalRunLengths + .Where(len => backstop.CanEndRunAt(currentXStart + len)) + .ToList(); - for (int i = 0; i < 5; i++) + if (validRunLengths.Count == 0) { - if (!ChooseRandomRunLength(ref runLengths, out int runLength)) - { - return false; // no valid run lengths remain - } - if (GenerateRunRecursive(runLength)) - { - return true; - } + return false; } - return false; - } + // Shuffle to maintain randomness in appearance while still being exhaustive + shared.prng.Shuffle(validRunLengths); - private bool ChooseRandomRunLength(ref IImmutableSet runLengths, out int runLength) - { - while (runLengths.Any()) + // Try each valid run length exhaustively + foreach (var runLength in validRunLengths) { - runLength = shared.prng.RandomChoice(runLengths.ToArray()); - if (shared.backstop.CanEndRunAt(xStart + runLength)) + // For the first position, there's no Y adjustment + if (xStart == 0) { - return true; + if (TryGenerateRunWithY(runLength, this.y)) + { + return true; + } } else { - runLengths = runLengths.Remove(runLength); + // Try both Y+1 and Y-1 systematically instead of randomly + int[] yChoices = shared.prng.NextInt32(2) == 0 + ? new[] { this.y + 1, this.y - 1 } + : new[] { this.y - 1, this.y + 1 }; + + foreach (var yChoice in yChoices) + { + if (TryGenerateRunWithY(runLength, yChoice)) + { + return true; + } + } } } - runLength = int.MinValue; return false; } - private bool GenerateRunRecursive(int runLength) + private bool TryGenerateRunWithY(int runLength, int y) { - int y = this.y; - if (xStart > 0) - { - y += shared.prng.RandomChoice(-1, 1); - } - var buffer = shared.GetWritableBuffer(xStart, runLength); + int currentY = y; + + // Try to generate all cells for this run for (int i = 0; i < runLength; i++) { int x = xStart + i; - if (shared.backstop.GetCellForNextLayer(shared.config, x, ref y, shared.LayerId, out var cell)) + if (shared.backstop.GetCellForNextLayer(shared.config, x, ref currentY, shared.LayerId, out var cell)) { buffer[i] = cell; } else { - return false; + return false; // This run doesn't work with this Y } } - var next = new LayerGenerator(shared, xStart + runLength, y); + // Successfully generated this run, try to generate the rest + var next = new LayerGenerator(shared, xStart + runLength, currentY); return next.Execute(); } } diff --git a/src/Blocktavius.Tests/Test1.cs b/src/Blocktavius.Tests/Test1.cs index 39f47a1..3da5822 100644 --- a/src/Blocktavius.Tests/Test1.cs +++ b/src/Blocktavius.Tests/Test1.cs @@ -60,23 +60,14 @@ public void ExerciseAdamantCliff() } [TestMethod] - public void AdamantCliffKnownBug() + public void AdamantCliffKnownBug() // this particular seed is now fixed { var prng = PRNG.Deserialize("3309110861-16864670-3535033132-2513591414-720943084-1714556781"); - try - { - for (int i = 0; i < 1000; i++) - { - var cliff = Core.Generators.Hills.AdamantCliffBuilder.Generate(prng, 100, 60); - Assert.IsNotNull(cliff); - } - } - catch (Exception) + for (int i = 0; i < 1000; i++) { - Assert.Inconclusive("The known issue in adamant cliff is still present... Fix is not urgent."); - return; + var cliff = Core.Generators.Hills.AdamantCliffBuilder.Generate(prng, 100, 60); + Assert.IsNotNull(cliff); } - Assert.Fail("ATTENTION: Is the bug in Adamant Cliff fixed? Or is it just fixed for this particular seed?"); } [TestMethod]