feat(B06): 구조물 잔토를 사토에 더하는 통로 신설 — 공제와 짝이 되는 자리
데스크탑 메인이 구조물 터파기·되메우기·잔토를 인계에 실었는데, **잔토 126.625㎥ 가
어디에도 안 실렸다**. 사토로 실어 내는 몫이라 유토곡선이 세야 겹치지 않는다.
구조물 잔토는 사토에 한 번만 더한다.
B08 은 소요량(structure_spoil_m3, ㎥ 양수)을 내기만 하고,
더하는 자리는 유토곡선의 사토뿐이다.
- ⚠ **잔량 하나하나를 늘린다** — 총량만 늘리면 사토는 늘고 운반이 안 는다(공제 때와 같은 자리).
- ⚠ 나누는 법: 지금은 총량 하나만 오므로 **남은 사토 잔량 크기에 비례**해 나눈다.
한 곳에 몰면 운반거리가 틀린다. 측점별 값이 오면 이 함수만 고치면 된다.
- ⚠ 자연방토·지반유형 안분은 안 건드린다 — 실어 내는 흙이고, 어느 지반에서 나온지 모른다.
- `null`(아직 안 옴) ≠ `0`(없음), 받은 값과 **실제로 더한 값**을 따로 싣는다.
시험 5건(더해짐·잔량 동반 증가·자연방토 불변·토취 불변·공제와 함께 와도 한 번씩).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -199,6 +199,13 @@ export interface HaulPlan {
|
||||
collected_stone_deduction_m3: number | null;
|
||||
/** 실제로 사토에서 뺀 양(㎥). 사토가 모자라면 받은 값보다 작을 수 있다. */
|
||||
collected_stone_deducted_m3: number;
|
||||
/**
|
||||
* 구조물 터파기가 남긴 잔토(㎥, 양수) — **사토에 더한다**. `null` 은 「아직 안 옴」이고
|
||||
* `0` 은 「없음」이다(공제와 같은 태도).
|
||||
*/
|
||||
structure_spoil_m3: number | null;
|
||||
/** 실제로 사토에 더한 양(㎥). 받을 잔량이 없으면 받은 값보다 작을 수 있다. */
|
||||
structure_spoil_added_m3: number;
|
||||
/** 블록 안에서 옮기는 양(㎥). */
|
||||
hauled_m3: number;
|
||||
/** 떨어진 구간끼리 장거리로 옮기는 양(㎥). */
|
||||
@@ -367,10 +374,46 @@ function applyCollectedStoneDeduction(residuals: HaulResidual[], deduction: numb
|
||||
return deduction - Math.max(left, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 구조물 잔토 — **사토에 한 번만 더한다**(2026-09-09 네 창 합의).
|
||||
*
|
||||
* 구조물 잔토는 사토에 한 번만 더한다.
|
||||
* B08 은 소요량(structure_spoil_m3, ㎥ 양수)을 내기만 하고,
|
||||
* 더하는 자리는 유토곡선의 사토뿐이다.
|
||||
*
|
||||
* ⚠ **잔량 하나하나를 늘린다** — 총량만 늘리면 사토는 늘고 **운반이 안 는다**(공제 때와 같은 자리).
|
||||
* ⚠ **나누는 법** — B08 이 지금은 **총량 하나**만 준다. 어느 측점에서 나온 잔토인지 모르므로
|
||||
* **남은 사토 잔량의 크기에 비례**해 나눈다. 한 곳에 몰면 운반거리가 틀리기 때문이고,
|
||||
* 측점별 값이 오면 그때 그 자리에 얹을 것(그때는 이 함수만 고치면 된다).
|
||||
* ⚠ **자연방토(`natural_m3`)는 안 늘린다** — 구조물 잔토는 실어 내는 흙이다.
|
||||
* ⚠ **지반유형 안분(ea/rr/br)도 안 건드린다** — 어느 지반에서 나온 흙인지 모른다.
|
||||
* 합계(`volume_m3`)와 갈래 합이 어긋나는 것은 그 사실을 드러내는 표시다.
|
||||
*
|
||||
* 돌려주는 값은 **실제로 더한 양(㎥)**. 받을 사토 잔량이 하나도 없으면 0 이다.
|
||||
*/
|
||||
function applyStructureSpoil(residuals: HaulResidual[], amount: number | null): number {
|
||||
if (amount === null || !Number.isFinite(amount) || amount <= 0) return 0;
|
||||
const spoils = residuals.filter((residual) => residual.kind === "spoil");
|
||||
const total = spoils.reduce((sum, residual) => sum + residual.volume_m3, 0);
|
||||
if (!spoils.length || total <= EPSILON) return 0;
|
||||
let added = 0;
|
||||
spoils.forEach((residual, index) => {
|
||||
// 마지막 잔량은 나머지를 그대로 받아 반올림 오차가 새지 않게 한다.
|
||||
const share =
|
||||
index === spoils.length - 1 ? amount - added : amount * (residual.volume_m3 / total);
|
||||
residual.volume_m3 += share;
|
||||
added += share;
|
||||
});
|
||||
return added;
|
||||
}
|
||||
|
||||
export function computeHaulPlan(
|
||||
result: MassHaulResult,
|
||||
limits: HaulEquipmentLimit[] | undefined,
|
||||
options?: { collected_stone_deduction_m3?: number | null },
|
||||
options?: {
|
||||
collected_stone_deduction_m3?: number | null;
|
||||
structure_spoil_m3?: number | null;
|
||||
},
|
||||
): HaulPlan | null {
|
||||
const points = result.points;
|
||||
if (points.length < 2) return null;
|
||||
@@ -551,6 +594,8 @@ export function computeHaulPlan(
|
||||
|
||||
const deductionInput = options?.collected_stone_deduction_m3 ?? null;
|
||||
const deducted = applyCollectedStoneDeduction(settled, deductionInput);
|
||||
const structureSpoilInput = options?.structure_spoil_m3 ?? null;
|
||||
const structureSpoilAdded = applyStructureSpoil(settled, structureSpoilInput);
|
||||
const remaining = settled.filter((residual) => residual.volume_m3 > EPSILON);
|
||||
remaining.forEach((residual, index) => {
|
||||
residual.index = index + 1;
|
||||
@@ -577,6 +622,8 @@ export function computeHaulPlan(
|
||||
natural_spoil_m3: naturalSpoil,
|
||||
collected_stone_deduction_m3: deductionInput,
|
||||
collected_stone_deducted_m3: deducted,
|
||||
structure_spoil_m3: structureSpoilInput,
|
||||
structure_spoil_added_m3: structureSpoilAdded,
|
||||
hauled_m3: blocks.reduce((sum, block) => sum + block.volume_m3, 0),
|
||||
transferred_m3: transfers.reduce((sum, entry) => sum + entry.volume_m3, 0),
|
||||
fill_total_m3: fillTotal,
|
||||
|
||||
Reference in New Issue
Block a user