test(turn): cover general pre-required turn boundaries
This commit is contained in:
@@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
현재 ref MariaDB와 core memory를 잇는 공통 runner, 성공 경로 55개,
|
현재 ref MariaDB와 core memory를 잇는 공통 runner, 성공 경로 55개,
|
||||||
실행 중 확률 실패 9개, full constraint fallback 12개와 모략 확률 clamp
|
실행 중 확률 실패 9개, full constraint fallback 12개와 모략 확률 clamp
|
||||||
8개, 모략 결과값 경계 5개, 부상 경계 3개가 구현됐다.
|
8개, 모략 결과값 경계 5개, 부상 경계 3개, alternative 5개와 pre-required
|
||||||
|
turn 중간 경계 6개가 구현됐다.
|
||||||
실패 9개는 내정 critical
|
실패 9개는 내정 critical
|
||||||
`주민선정/정착장려/상업투자/기술연구/물자조달`과 모략
|
`주민선정/정착장려/상업투자/기술연구/물자조달`과 모략
|
||||||
`화계/선동/파괴/탈취`이며 RNG 전체 trace, semantic state delta와 실패
|
`화계/선동/파괴/탈취`이며 RNG 전체 trace, semantic state delta와 실패
|
||||||
@@ -23,9 +24,12 @@ state delta를 비교한다. 결과값 5개는 화계 농업·상업, 선동 치
|
|||||||
파괴 수비·성벽의 0 바닥과 탈취의 대상 국가 자원 상한·미보급 도시 최종
|
파괴 수비·성벽의 0 바닥과 탈취의 대상 국가 자원 상한·미보급 도시 최종
|
||||||
저장 상태를 비교한다. 부상 3개는 화계·선동·파괴의 대상 장수별 판정,
|
저장 상태를 비교한다. 부상 3개는 화계·선동·파괴의 대상 장수별 판정,
|
||||||
부상도 80 상한, 병력·훈련·사기 0.98 정수 저장과 대상 장수 로그를
|
부상도 80 상한, 병력·훈련·사기 0.98 정수 저장과 대상 장수 로그를
|
||||||
비교한다. 나머지 명령별 제약 실패·값 경계·alternative와 전체 core
|
비교한다. alternative 5개는 해산·랜덤임관·무작위건국·출병의 모든 대체
|
||||||
PostgreSQL 재조회가 완료 기준을 통과하기 전까지 55개 명령 전체의 동적
|
분기에서 최초 명령 RNG의 연속 소비와 최종 상태를 비교한다. pre-required
|
||||||
호환 상태를 `확인`으로 올리지 않는다.
|
turn 6개는 전투태세 1/2/3턴, 내정·전투 특기 초기화 1턴, 은퇴 1턴의
|
||||||
|
`last_turn`과 진행 로그, RNG 무소비를 비교한다. 나머지 명령별 제약
|
||||||
|
실패·값 경계와 전체 core PostgreSQL 재조회가 완료 기준을 통과하기 전까지
|
||||||
|
55개 명령 전체의 동적 호환 상태를 `확인`으로 올리지 않는다.
|
||||||
|
|
||||||
## 결정 요약
|
## 결정 요약
|
||||||
|
|
||||||
|
|||||||
@@ -199,8 +199,15 @@ of 80, integer persistence after multiplying crew/train/atmosphere by 0.98,
|
|||||||
and the target-general injury log for fire attack, agitation and destruction.
|
and the target-general injury log for fire attack, agitation and destruction.
|
||||||
They restore the legacy Korean particle in the log, add the two missing logs,
|
They restore the legacy Korean particle in the log, add the two missing logs,
|
||||||
and round rather than floor the integer fields.
|
and round rather than floor the integer fields.
|
||||||
|
Five general alternative cases cover disband and random-appointment fallback
|
||||||
|
to talent search, both random-founding alternatives, and sortie fallback to
|
||||||
|
movement. The alternative command reuses the original requested-action RNG
|
||||||
|
instance and current consumption position. Six pre-required-turn cases cover
|
||||||
|
battle-preparation terms 1 through 3 plus the first intermediate turn of both
|
||||||
|
trait resets and retirement. They compare the exact intermediate `lastTurn`,
|
||||||
|
progress log, zero command-RNG consumption and semantic state delta.
|
||||||
This is not yet a claim that every command-specific
|
This is not yet a claim that every command-specific
|
||||||
constraint, clamp, alternative and persistence boundary has been dynamically
|
constraint, clamp and persistence boundary has been dynamically
|
||||||
compared.
|
compared.
|
||||||
|
|
||||||
The fixture runner also reports whether the requested legacy command reached
|
The fixture runner also reports whether the requested legacy command reached
|
||||||
|
|||||||
@@ -828,6 +828,109 @@ integration('general command alternative matrix', () => {
|
|||||||
}, 120_000);
|
}, 120_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type GeneralPreReqBoundaryCase = {
|
||||||
|
name: string;
|
||||||
|
action: string;
|
||||||
|
actorPatch?: Record<string, unknown>;
|
||||||
|
expectedCommand: string;
|
||||||
|
expectedTerm: number;
|
||||||
|
expectedProgressText: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generalPreReqBoundaryCases: GeneralPreReqBoundaryCase[] = [
|
||||||
|
{
|
||||||
|
name: 'battle preparation starts at term 1',
|
||||||
|
action: 'che_전투태세',
|
||||||
|
expectedCommand: '전투태세',
|
||||||
|
expectedTerm: 1,
|
||||||
|
expectedProgressText: '전투태세 수행중... (1/4)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'battle preparation advances term 1 to 2',
|
||||||
|
action: 'che_전투태세',
|
||||||
|
actorPatch: { lastTurn: { command: '전투태세', term: 1 } },
|
||||||
|
expectedCommand: '전투태세',
|
||||||
|
expectedTerm: 2,
|
||||||
|
expectedProgressText: '전투태세 수행중... (2/4)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'battle preparation advances term 2 to 3',
|
||||||
|
action: 'che_전투태세',
|
||||||
|
actorPatch: { lastTurn: { command: '전투태세', term: 2 } },
|
||||||
|
expectedCommand: '전투태세',
|
||||||
|
expectedTerm: 3,
|
||||||
|
expectedProgressText: '전투태세 수행중... (3/4)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'domestic trait reset starts at term 1 after another command',
|
||||||
|
action: 'che_내정특기초기화',
|
||||||
|
actorPatch: {
|
||||||
|
specialDomestic: 'che_인덕',
|
||||||
|
lastTurn: { command: '전투태세', term: 3 },
|
||||||
|
},
|
||||||
|
expectedCommand: '내정 특기 초기화',
|
||||||
|
expectedTerm: 1,
|
||||||
|
expectedProgressText: '새로운 적성을 찾는 중... (1/2)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'war trait reset starts at term 1',
|
||||||
|
action: 'che_전투특기초기화',
|
||||||
|
actorPatch: { specialWar: 'che_귀병' },
|
||||||
|
expectedCommand: '전투 특기 초기화',
|
||||||
|
expectedTerm: 1,
|
||||||
|
expectedProgressText: '새로운 적성을 찾는 중... (1/2)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'retirement starts at term 1 without applying retirement',
|
||||||
|
action: 'che_은퇴',
|
||||||
|
actorPatch: { age: 60 },
|
||||||
|
expectedCommand: '은퇴',
|
||||||
|
expectedTerm: 1,
|
||||||
|
expectedProgressText: '은퇴 수행중... (1/2)',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
integration('general command pre-required turn boundary matrix', () => {
|
||||||
|
it.each(generalPreReqBoundaryCases)(
|
||||||
|
'$name matches legacy intermediate last-turn state without consuming command RNG',
|
||||||
|
async ({ action, actorPatch, expectedCommand, expectedTerm, expectedProgressText }) => {
|
||||||
|
const request = buildRequest(action, undefined, actorPatch);
|
||||||
|
request.setup!.world!.hiddenSeed = `general-prereq-${action}-${expectedTerm}`;
|
||||||
|
const reference = runReferenceTurnCommandTraceRequest(
|
||||||
|
workspaceRoot!,
|
||||||
|
request as unknown as Record<string, unknown>
|
||||||
|
);
|
||||||
|
const core = await runCoreTurnCommandTrace(request, reference.before);
|
||||||
|
const referenceActor = reference.after.generals.find((entry) => entry.id === 1);
|
||||||
|
const coreActor = core.after.generals.find((entry) => entry.id === 1);
|
||||||
|
|
||||||
|
expect(reference.execution.outcome).toMatchObject({ completed: false });
|
||||||
|
expect(core.execution.outcome).toMatchObject({
|
||||||
|
requestedAction: action,
|
||||||
|
actionKey: action,
|
||||||
|
usedFallback: false,
|
||||||
|
});
|
||||||
|
expect(referenceActor?.lastTurn).toMatchObject({
|
||||||
|
command: expectedCommand,
|
||||||
|
term: expectedTerm,
|
||||||
|
});
|
||||||
|
expect(coreActor?.lastTurn).toMatchObject({
|
||||||
|
command: expectedCommand,
|
||||||
|
term: expectedTerm,
|
||||||
|
});
|
||||||
|
expect(reference.after.logs.some((entry) => String(entry.text).includes(expectedProgressText))).toBe(true);
|
||||||
|
expect(core.after.logs.some((entry) => String(entry.text).includes(expectedProgressText))).toBe(true);
|
||||||
|
expect(core.rng).toEqual(reference.rng);
|
||||||
|
expect(
|
||||||
|
compareTurnSnapshotDeltas(reference.before, reference.after, core.before, core.after, {
|
||||||
|
ignoredPathPatterns: ignoredLifecyclePaths,
|
||||||
|
})
|
||||||
|
).toEqual([]);
|
||||||
|
},
|
||||||
|
120_000
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
type GeneralConstraintCase = {
|
type GeneralConstraintCase = {
|
||||||
name: string;
|
name: string;
|
||||||
action: string;
|
action: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user