- 64-1: 추천 창 「종단 반영」 체크(기본 켬) — 요청 with_profile 로 서버가 가름 · 끄면 원지반만(63-4 전 점수) · 묶음 캐시에 남은 종단은 걷어 냄 · 값은 편집 창 안에서만(저장 안 함) - 64-2: 설명 칸을 갈래 한 쪽씩 ◀ ▶ + 「n / 8」 · 고른 갈래 쪽부터 · 토공 최소 · 공사비 어림 최소 쪽에 종단 반영 설명 추가 - 시험: test_64_1_profile_toggle.py · ui_checks 64-1 · 64-2 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
74 lines
3.1 KiB
Python
74 lines
3.1 KiB
Python
"""추천 「종단 반영」 켜기 · 끄기(PLAN 64-1) — 점수 분기 시험.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_64_1_profile_toggle.py -q
|
|
|
|
켬(기본) = 후보마다 균형 종단을 세워 토공 점수를 그것으로(63-4) · 끔 = 종단 없이 원지반 가늠
|
|
(절 + 성). 끈 요청은 켠 요청이 묶음 캐시에 남긴 종단을 걷어 내고 처음부터 끈 것과 같아야 한다.
|
|
"""
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from B05_Profile.B05_Profile_Engine_RouteInitial import Terrain, generate_initial_route
|
|
from B05_Profile.B05_Profile_Engine_RouteInitial_Metrics import COST_EARTH_M3
|
|
from B05_Profile.B05_Profile_Engine_RouteInitial_Pool import GOALS
|
|
from B05_Profile.B05_Profile_Engine_RouteInitial_Profile import profile_earth
|
|
from B05_Profile.B05_Profile_Router_Replan import RouteInitialRequest
|
|
|
|
GRADE = 0.14
|
|
RADIUS = 12.0
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def hill() -> Terrain:
|
|
x = np.arange(0.0, 802.0, 2.0)
|
|
y = np.arange(0.0, 402.0, 2.0)
|
|
xx, yy = np.meshgrid(x, y)
|
|
z = 40.0 * np.exp(-((xx - 400.0) ** 2 + (yy - 200.0) ** 2) / 90.0**2)
|
|
return Terrain(x, y, z, np.ones(xx.shape, dtype=bool))
|
|
|
|
|
|
def _run(terrain: Terrain, mode: str, with_profile: bool) -> dict:
|
|
expected = [(float(x), 200.0) for x in np.arange(50.0, 752.0, 4.0)]
|
|
return generate_initial_route(
|
|
expected,
|
|
terrain,
|
|
[],
|
|
mode=mode,
|
|
weights=None,
|
|
max_grade=GRADE,
|
|
min_radius_m=RADIUS,
|
|
with_profile=with_profile,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("mode", ["earthwork", "cost", "follow"])
|
|
def test_켬은_종단_끔은_원지반(hill: Terrain, mode: str) -> None:
|
|
on = _run(hill, mode, True)
|
|
off = _run(hill, mode, False)
|
|
assert on["with_profile"] is True and off["with_profile"] is False
|
|
assert "error" not in on["metrics"]["profile"]
|
|
assert "profile" not in off["metrics"] and "earth_estimate_m3" not in off["metrics"]
|
|
# 끔 점수 = 절 + 성 · 켬 점수 = 균형 종단 토공
|
|
assert GOALS["earthwork"](off["metrics"]) == (
|
|
off["metrics"]["cut_m3"] + off["metrics"]["fill_m3"],
|
|
)
|
|
assert GOALS["earthwork"](on["metrics"]) == (profile_earth(on["metrics"]["profile"]),)
|
|
# 공사비 어림 = 켬은 토공 몫을 종단 점수로 바꾼 값(고른 노선이 달라도 그 노선의 처음 값 기준)
|
|
metrics = on["metrics"]
|
|
shift = COST_EARTH_M3 * (profile_earth(metrics["profile"]) - metrics["earth_estimate_m3"])
|
|
assert metrics["cost_index"] == pytest.approx(metrics["cost_index_base"] + shift, abs=1.0)
|
|
|
|
|
|
def test_끈_요청은_묶음_캐시의_종단을_걷어_낸다(hill: Terrain) -> None:
|
|
first = _run(hill, "cost", False)
|
|
_run(hill, "cost", True) # 같은 묶음 캐시에 종단을 붙임
|
|
again = _run(hill, "cost", False)
|
|
assert again["metrics"] == first["metrics"] and again["planned"] == first["planned"]
|
|
assert "profile" not in again["metrics"]
|
|
|
|
|
|
def test_요청_기본은_켬이고_끔을_받는다() -> None:
|
|
assert RouteInitialRequest(mode="earthwork").with_profile is True
|
|
assert RouteInitialRequest(mode="earthwork", with_profile=False).with_profile is False
|