solver: select skyline candidate policy

Make candidate ordering an explicit deterministic policy and benchmark ascending, descending, and exact-width-first choices. Keep ascending as the default because it produces the best measured time to first solution despite best-fit's slightly smaller tree.

Retain the benchmark v1 interface and document why randomized and duplicate-work portfolio policies are deferred.

Tests: Release and Debug CTest (10 passed each)

Refs: #12
This commit was merged in pull request #24.
This commit is contained in:
Codex instance
2026-07-30 18:07:29 +01:00
parent d751d1b13e
commit 74bde266d0
6 changed files with 141 additions and 49 deletions
+32 -17
View File
@@ -154,9 +154,12 @@ namespace {
size_t completed_tasks = 0;
};
enum class CandidateOrder {
/** Deterministic order in which a skyline node tries fitting squares. */
enum class SearchPolicy {
ascending,
descending,
/** Close the selected valley when possible, then try smaller sizes first. */
best_fit,
};
/** A maximal level skyline segment which is lower than its neighbours. */
@@ -199,7 +202,7 @@ namespace {
template<bool Instrument>
auto search_skyline(size_t const n, size_t const length,
CandidateOrder const candidate_order,
SearchPolicy const policy,
std::vector<size_t> &skyline, Avail &available,
std::vector<Square> &squares,
SearchCounters *const counters) noexcept -> bool {
@@ -231,7 +234,7 @@ namespace {
side, valley.height + side);
squares.emplace_back(valley.x + valley.height * length, side);
if (search_skyline<Instrument>(n, length, candidate_order, skyline,
if (search_skyline<Instrument>(n, length, policy, skyline,
available, squares, counters)) {
return true;
}
@@ -246,8 +249,15 @@ namespace {
return false;
};
if (candidate_order == CandidateOrder::ascending) {
if (policy == SearchPolicy::best_fit && largest == valley.width &&
try_side(largest)) {
return true;
}
if (policy != SearchPolicy::descending) {
for (size_t side = 1; side <= largest; ++side) {
if (policy == SearchPolicy::best_fit && side == valley.width) {
continue;
}
if (try_side(side)) {
return true;
}
@@ -272,7 +282,7 @@ namespace {
* O(board width + n^2) local work per node and the same total state.
*/
template<bool Instrument>
auto search_solution_impl(size_t const n, CandidateOrder const candidate_order,
auto search_solution_impl(size_t const n, SearchPolicy const policy,
SearchCounters *const counters) noexcept
-> Results {
auto const length = triangle_num(n);
@@ -284,25 +294,25 @@ namespace {
std::vector<Square> squares;
squares.reserve(length);
static_cast<void>(search_skyline<Instrument>(
n, length, candidate_order, skyline, available, squares, counters));
n, length, policy, skyline, available, squares, counters));
return {length, std::move(squares)};
}
auto search_solution(
size_t const n,
CandidateOrder const candidate_order = CandidateOrder::ascending) noexcept
SearchPolicy const policy = SearchPolicy::ascending) noexcept
-> Results {
return search_solution_impl<false>(n, candidate_order, nullptr);
return search_solution_impl<false>(n, policy, nullptr);
}
auto search_solution_instrumented(size_t const n,
SearchCounters &counters,
CandidateOrder const candidate_order =
CandidateOrder::ascending) noexcept
SearchPolicy const policy =
SearchPolicy::ascending) noexcept
-> Results {
counters = {};
return search_solution_impl<true>(n, candidate_order, &counters);
return search_solution_impl<true>(n, policy, &counters);
}
/** Construct an odd-order solution from its even-order predecessor. */
@@ -335,20 +345,25 @@ namespace {
return n >= 9 && n % 2 == 1;
}
auto find_solution(size_t const n) noexcept -> Results {
auto find_solution(
size_t const n,
SearchPolicy const policy = SearchPolicy::ascending) noexcept -> Results {
if (uses_odd_construction(n)) {
return construct_odd_solution(n, search_solution(n - 1));
return construct_odd_solution(n, search_solution(n - 1, policy));
}
return search_solution(n);
return search_solution(n, policy);
}
auto find_solution_instrumented(size_t const n,
SearchCounters &counters) noexcept -> Results {
SearchCounters &counters,
SearchPolicy const policy =
SearchPolicy::ascending) noexcept
-> Results {
if (uses_odd_construction(n)) {
return construct_odd_solution(
n, search_solution_instrumented(n - 1, counters));
n, search_solution_instrumented(n - 1, counters, policy));
}
return search_solution_instrumented(n, counters);
return search_solution_instrumented(n, counters, policy);
}
} // anon namespace