"""覆盖计划(CoveragePlan)与运行期校核验收(方案 §P0-2)。 要点: - 计划是"可离线搜索、可在运行期校验"的一等对象,运行期必须重新校核; - 校核必须能**拒绝**非法点集(删点即被检出),否则校核是装饰; - 两类覆盖语义不得混用:第三问 full_disk、第四问 forward_half_disk; - 第四问最小命中集必须在同一采样空间上与原 49 点表等价覆盖。 """ import unittest from mcm26b.config import ProblemConfig from mcm26b.coverage import coverage_points from mcm26b.guarantee import (plan_instance, plan_names, seven_plan, square_49_plan, verify_full_disk, verify_forward_half_disk, verify_plan) class CoveragePlanTests(unittest.TestCase): def test_q4_mincover_plan_is_certified(self): self.assertIn("q4_mincover_v1", plan_names()) plan = plan_instance("q4_mincover_v1") self.assertEqual(plan.problem, 4) self.assertEqual(plan.coverage, "forward_half_disk") certificate = verify_plan(plan) self.assertEqual(certificate.uncovered, 0, certificate.as_dict()) self.assertTrue(certificate.valid) # 最小命中集必须显著小于旧的充分条件表 self.assertLess(len(plan.points), len(square_49_plan().points)) self.assertLess(certificate.nn_tour_m, 25_000.0) def test_hardened_plan_passes_fine_grid(self): """S2:现役计划必须在 15 m/72 档(325.6 万个 (p,h))零缺口。 75 m/24 档的"零缺口"曾是采样过拟合(v1 在 25 m/48 档有 125 个缺口)。 """ self.assertIn("q4_mincover_v2", plan_names()) plan = plan_instance("q4_mincover_v2") certificate = plan.certificate self.assertIsNotNone(certificate) self.assertEqual(certificate.target_step_m, 15.0) self.assertEqual(certificate.headings, 72) verified = verify_plan(plan) self.assertEqual(verified.uncovered, 0, verified.as_dict()) self.assertLess(verified.worst_hit_distance_m, 1000.0) self.assertTrue(verified.valid) def test_legacy_overfit_plan_fails_fine_grid(self): """负例:旧的 31 点表在细网格上有缺口,硬化不是装饰。""" legacy = plan_instance("q4_mincover_v1", verify=False) certificate = verify_forward_half_disk(list(legacy.points), 25, 48) self.assertGreater(certificate.uncovered, 0) def test_square49_and_mincover_cover_the_same_space(self): for name in ("square49", "q4_mincover_v1"): certificate = verify_forward_half_disk(plan_instance(name).points) self.assertEqual(certificate.uncovered, 0, name) def test_verifier_rejects_a_broken_plan(self): """删掉关键点必须被检出——否则运行期校核没有意义。""" points = list(plan_instance("q4_mincover_v1").points) broken = points[:-1] certificate = verify_forward_half_disk(broken) self.assertGreater(certificate.uncovered, 0) self.assertFalse(certificate.valid) def test_hardened_plan_rejects_a_removed_point(self): """硬化计划同样必须能拒绝删点(在证书分辨率上复核)。""" plan = plan_instance("q4_mincover_v2") points = list(plan.points) certificate = verify_forward_half_disk( points[:-1], plan.certificate.target_step_m, plan.certificate.headings) self.assertGreater(certificate.uncovered, 0) def test_seven_plan_full_disk_semantics(self): plan = seven_plan(1200.0) certificate = verify_full_disk(plan.points) self.assertEqual(certificate.kind, "full_disk") self.assertEqual(certificate.uncovered, 0) def test_seven_plan_too_small_ring_is_rejected(self): """环半径过小则 Ω 边缘覆盖不全,full_disk 校核必须报缺口。""" certificate = verify_full_disk(seven_plan(800.0).points) self.assertGreater(certificate.uncovered, 0) def test_square49_order_matches_legacy_ledger(self): """历史基线 grid49_immediate 必须逐位可复现(点集与顺序都一致)。""" self.assertEqual(list(square_49_plan().points), list(coverage_points(ProblemConfig()))) def test_coverage_semantics_are_not_interchangeable(self): """第三问七站点集不满足第四问"前向半盘命中"语义(朝向遮挡)。""" certificate = verify_forward_half_disk(seven_plan(1200.0).points) self.assertGreater(certificate.uncovered, 0) if __name__ == "__main__": unittest.main()