Provide a separately installed OR-Tools model to compare a generic constraint solver with the native first-solution path without adding a production or default-test dependency. Use no-overlap, exact-fill, edge and equal-copy symmetry constraints, validate placements independently, and record model size, memory, worker count and timings for orders 8 and 9. Keep the tool only as a reference and defer DLX absent new evidence. Tests: Release CTest (9 passed) Tests: Python reference tests and compilation checks Tests: independently validated CP-SAT orders 8 and 9 Refs: #5
84 lines
2.7 KiB
Python
84 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Dependency-free tests for the CP-SAT reference support code."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "tools"))
|
|
from partridge_validator import validate # noqa: E402
|
|
|
|
|
|
KNOWN_ORDER_8 = {
|
|
"order": 8,
|
|
"board_side": 36,
|
|
"placements": [
|
|
{"x": x, "y": y, "side": side}
|
|
for x, y, side in [
|
|
(0, 0, 8), (8, 0, 8), (16, 0, 8), (24, 0, 8),
|
|
(32, 0, 4), (32, 4, 4), (0, 8, 8), (8, 8, 8),
|
|
(16, 8, 8), (24, 8, 6), (30, 8, 6), (24, 14, 5),
|
|
(29, 14, 7), (0, 16, 6), (6, 16, 3), (9, 16, 8),
|
|
(17, 16, 7), (6, 19, 3), (24, 19, 5), (29, 21, 7),
|
|
(0, 22, 7), (7, 22, 2), (17, 23, 1), (18, 23, 6),
|
|
(7, 24, 6), (13, 24, 5), (24, 24, 5), (29, 28, 3),
|
|
(32, 28, 4), (0, 29, 7), (13, 29, 7), (20, 29, 7),
|
|
(27, 29, 2), (7, 30, 6), (27, 31, 5), (32, 32, 4),
|
|
]
|
|
],
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
assert validate(KNOWN_ORDER_8) == []
|
|
|
|
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
|
invalid["placements"].pop()
|
|
diagnostics = validate(invalid)
|
|
assert any("side 4 has multiplicity 3; expected 4" in d for d in diagnostics)
|
|
assert any("not completely covered" in d for d in diagnostics)
|
|
|
|
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
|
invalid["placements"][0]["x"] = 36
|
|
diagnostics = validate(invalid)
|
|
assert any("outside the board" in diagnostic for diagnostic in diagnostics)
|
|
assert any("not completely covered" in diagnostic for diagnostic in diagnostics)
|
|
|
|
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
|
invalid["placements"][1]["x"] = invalid["placements"][0]["x"]
|
|
invalid["placements"][1]["y"] = invalid["placements"][0]["y"]
|
|
diagnostics = validate(invalid)
|
|
assert any("overlaps placement 0" in diagnostic for diagnostic in diagnostics)
|
|
|
|
invalid = json.loads(json.dumps(KNOWN_ORDER_8))
|
|
invalid["placements"][0]["side"] = 9
|
|
diagnostics = validate(invalid)
|
|
assert any("invalid side length" in diagnostic for diagnostic in diagnostics)
|
|
|
|
if importlib.util.find_spec("ortools") is not None:
|
|
process = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(ROOT / "tools" / "cp_sat_reference.py"),
|
|
"1",
|
|
"--workers",
|
|
"1",
|
|
],
|
|
text=True,
|
|
capture_output=True,
|
|
check=True,
|
|
)
|
|
report = json.loads(process.stdout)
|
|
assert report["result"]["valid"]
|
|
assert validate(report["solution"]) == []
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|