solver: replace cell DFS with skyline search

Represent partial placements as column heights and branch on the narrowest local valley. This removes the board-area cell state and makes first-solution search substantially smaller for feasible orders.

Expose direct-search and candidate-order benchmark controls so the skyline core can be measured independently of odd-order construction. Document the completeness argument and the 10/11 test-tier decision.

Tests: Release, Debug, ASan, and UBSan CTest (10 passed each)

Refs: #4
This commit was merged in pull request #23.
This commit is contained in:
Codex instance
2026-07-30 17:55:21 +01:00
parent 0a7ce1e49e
commit d751d1b13e
7 changed files with 296 additions and 198 deletions
+39
View File
@@ -343,6 +343,42 @@ namespace {
return failures;
}
auto test_skyline_search() -> int {
int failures = 0;
auto const narrowest =
smallest_valley(std::vector<std::uint64_t>{4, 2, 2, 4, 0, 0, 0, 4});
failures += expect(
narrowest.x == 1 && narrowest.height == 2 && narrowest.width == 2,
"skyline did not select the smallest-width valley");
auto const tie =
smallest_valley(std::vector<std::uint64_t>{4, 1, 4, 4, 2, 4});
failures += expect(tie.x == 1 && tie.height == 1 && tie.width == 1,
"skyline valley tie-break is not deterministic");
SearchCounters descending_counters;
auto const descending = search_solution_instrumented(
8, descending_counters, CandidateOrder::descending);
auto validation = validate(8, descending);
failures += expect(
validation.valid(),
"descending skyline search returned an invalid order-8 solution:\n" +
validation.text());
SearchCounters direct_nine_counters;
auto const direct_nine = search_solution_instrumented(
9, direct_nine_counters, CandidateOrder::ascending);
validation = validate(9, direct_nine);
failures += expect(
validation.valid(),
"direct skyline search returned an invalid order-9 solution:\n" +
validation.text());
failures += expect(
direct_nine_counters.search_nodes != descending_counters.search_nodes,
"direct order-9 coverage unexpectedly reused predecessor construction");
return failures;
}
auto test_solver_completion() -> int {
int failures = 0;
for (auto const order: std::array<std::uint64_t, 2>{1, 8}) {
@@ -385,6 +421,9 @@ int main(int argc, char **argv) {
if (test == "search-counters") {
return test_search_counters();
}
if (test == "skyline-search") {
return test_skyline_search();
}
std::cerr << "unknown test: " << test << '\n';
return 2;
}