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
+27 -5
View File
@@ -74,16 +74,16 @@ def environment(binary):
"logical_cpus": os.cpu_count(),
},
"workers": 1,
"search_policy": "single-threaded, deterministic, largest-fitting-first",
"search_policy": "single-threaded, deterministic, smallest-width valley",
"seed": None,
}
def run_once(binary, order, mode, timeout):
def run_once(binary, order, mode, candidate_order, search_route, timeout):
started = time.monotonic()
try:
process = subprocess.run(
[str(binary), str(order), mode],
[str(binary), str(order), mode, candidate_order, search_route],
check=False,
capture_output=True,
text=True,
@@ -216,6 +216,12 @@ def main():
parser.add_argument("--warmup", type=int, default=1)
parser.add_argument("--repetitions", type=int, default=5)
parser.add_argument("--timeout", type=float, default=600.0)
parser.add_argument(
"--candidate-order",
choices=("ascending", "descending"),
default="ascending",
)
parser.add_argument("--direct-search", action="store_true")
parser.add_argument(
"--measure-overhead",
action="store_true",
@@ -241,11 +247,25 @@ def main():
mode_results = {mode: {"runs": []} for mode in modes}
for trial in range(args.warmup):
for mode in trial_modes(modes, trial):
run_once(args.binary, order, mode, args.timeout)
run_once(
args.binary,
order,
mode,
args.candidate_order,
"direct" if args.direct_search else "public",
args.timeout,
)
for trial in range(args.repetitions):
for mode in trial_modes(modes, trial):
mode_results[mode]["runs"].append(
run_once(args.binary, order, mode, args.timeout)
run_once(
args.binary,
order,
mode,
args.candidate_order,
"direct" if args.direct_search else "public",
args.timeout,
)
)
for result in mode_results.values():
result["summary"] = summarize(result["runs"])
@@ -270,6 +290,8 @@ def main():
"warmup_runs": args.warmup,
"measured_repetitions": args.repetitions,
"per_run_timeout_seconds": args.timeout,
"candidate_order": args.candidate_order,
"search_route": "direct" if args.direct_search else "public",
"stdout": "captured; rendered grid suppressed by probe",
},
"cases": cases,