feat: 3
This commit is contained in:
+26
-21
@@ -1,8 +1,9 @@
|
||||
"""自检场景生成器(测试端专用)。
|
||||
|
||||
真值只进入测试与 CLI ``--self-test`` 入口,策略端仅见模拟器响应;
|
||||
场景覆盖题面强调的边界情形:近圆域边界源、最小/最大接收半径两档,
|
||||
以及问题4的定向源(含朝向背离圆心的朝外边界源)。
|
||||
默认完全按题面生成:源总数 10~16 随机、位置在目标圆域内均匀分布、
|
||||
频道互异(1..20 抽样)、有效接收半径 [1000,1500] 均匀。
|
||||
边界情形(近圆域边界源、接收半径两档、定向朝外源)由专门测试显式构造。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -10,25 +11,29 @@ from __future__ import annotations
|
||||
import math
|
||||
from random import Random
|
||||
|
||||
from ..config import ProblemConfig
|
||||
from .simulator import Source
|
||||
|
||||
|
||||
def random_omni_scene(seed: int = 20260910, count: int = 10) -> list[Source]:
|
||||
"""全向源场景:频道互异,含近圆域边界点,接收半径 1000/1500 交替。"""
|
||||
if not 0 <= count <= 20:
|
||||
raise ValueError("源数量须在 0..20")
|
||||
def random_omni_scene(seed: int = 20260910, count: int | None = None,
|
||||
config: ProblemConfig = ProblemConfig()) -> list[Source]:
|
||||
"""全向源场景 [题面]:count=None 时源总数在 10..16 均匀随机。
|
||||
|
||||
位置在半径 omega_radius_m 圆域内按面积均匀(r = R*sqrt(u)),
|
||||
有效接收半径在 [r_eff_min, r_eff_max] 均匀,频道从 1..20 无重复抽样。
|
||||
"""
|
||||
field = config.field
|
||||
rng = Random(seed)
|
||||
channels = rng.sample(range(1, 21), count)
|
||||
if count is None:
|
||||
count = rng.randint(field.source_count_min, field.source_count_max)
|
||||
if not 0 <= count <= field.channel_max:
|
||||
raise ValueError(f"源数量须在 0..{field.channel_max}")
|
||||
channels = rng.sample(range(field.channel_min, field.channel_max + 1), count)
|
||||
sources = []
|
||||
for index, channel in enumerate(channels):
|
||||
if index == 0:
|
||||
radius = rng.uniform(1780.0, 1799.0) # 近圆域边界
|
||||
elif index == 1 and count > 1:
|
||||
radius = rng.uniform(1780.0, 1799.0)
|
||||
else:
|
||||
radius = rng.uniform(0.0, 1795.0)
|
||||
for channel in channels:
|
||||
radius = field.omega_radius_m * math.sqrt(rng.random())
|
||||
angle = rng.uniform(0.0, 2 * math.pi)
|
||||
r_eff = 1000.0 if index % 2 == 0 else 1500.0
|
||||
r_eff = rng.uniform(field.r_eff_min_m, field.r_eff_max_m)
|
||||
sources.append(Source(channel=channel,
|
||||
x=radius * math.cos(angle),
|
||||
y=radius * math.sin(angle),
|
||||
@@ -36,21 +41,21 @@ def random_omni_scene(seed: int = 20260910, count: int = 10) -> list[Source]:
|
||||
return sources
|
||||
|
||||
|
||||
def mixed_scene(seed: int = 20260910, count: int = 10,
|
||||
directional: int = 3) -> list[Source]:
|
||||
def mixed_scene(seed: int = 20260910, count: int | None = None,
|
||||
directional: int = 3,
|
||||
config: ProblemConfig = ProblemConfig()) -> list[Source]:
|
||||
"""问题4混合场景:count 个源中 directional 个为定向源。
|
||||
|
||||
改造 random_omni_scene 的前 directional 个源:近圆域边界的前两个定向源
|
||||
朝向取"背离圆心"(发射半平面基本朝向域外,覆盖网格仍保证可发现,
|
||||
即"朝外边界源不漏"验收情形),其余定向源朝向随机。
|
||||
"""
|
||||
if not 0 <= directional <= count:
|
||||
raise ValueError("定向源数量须在 0..count")
|
||||
sources = list(random_omni_scene(seed=seed, count=count))
|
||||
sources = list(random_omni_scene(seed=seed, count=count, config=config))
|
||||
directional = min(directional, len(sources))
|
||||
rng = Random(seed + 1)
|
||||
for index in range(directional):
|
||||
source = sources[index]
|
||||
if index < 2 and count > 1 and math.hypot(source.x, source.y) > 1700.0:
|
||||
if index < 2 and len(sources) > 1 and math.hypot(source.x, source.y) > 1700.0:
|
||||
heading = math.degrees(math.atan2(source.y, source.x)) % 360.0
|
||||
else:
|
||||
heading = rng.uniform(0.0, 360.0)
|
||||
|
||||
@@ -127,6 +127,12 @@ class LocalSimulator:
|
||||
})
|
||||
if not self.entered or self.exited:
|
||||
return 200, self._not_accepted()
|
||||
if path in ("/measure", "/clear") \
|
||||
and self.virtual_time_s >= self.field.max_virtual_duration_s:
|
||||
# 虚拟世界限时 360000s 防死循环 [题面/附件2 §4.5:达限结束测试]。
|
||||
# 官方在达限时关闭接口;本地以 accepted=false 模拟,作为计划层
|
||||
# 虚拟安全余量(search.virtual_reserve_s)失效时的兜底。
|
||||
return 200, self._not_accepted()
|
||||
if path == "/exit":
|
||||
self.exited = True
|
||||
return 200, self._ok({"exit_reason": EXIT_USER})
|
||||
|
||||
Reference in New Issue
Block a user