fix(diplomacy): 전쟁 기간을 양방향으로 동기화한다

This commit is contained in:
2026-08-22 10:23:06 +00:00
parent 86aa515619
commit 300a3c93e9
4 changed files with 117 additions and 16 deletions
+14 -4
View File
@@ -92,10 +92,12 @@ export const processDiplomacyMonth = (
entry.term = clamp(entry.term + termIncrease, 0, MAX_WAR_TERM);
}
// 전쟁 종료 판정: 양방 term이 1 이하이면 통상으로 전환.
// 전쟁은 양방향 diplomacy row로 저장되지만 기간은 하나의 관계가 소유한다.
// Ref처럼 방향별 사상자 연장을 먼저 계산하되, 더 긴 기간을 양쪽에 적용하여
// 한쪽만 0개월 전쟁으로 남는 비대칭 상태를 다음 월까지 노출하지 않는다.
const processedPairs = new Set<string>();
for (const entry of next) {
if (entry.state !== DIPLOMACY_STATE.WAR || entry.term > 1) {
if (entry.state !== DIPLOMACY_STATE.WAR) {
continue;
}
const pairKey = buildDiplomacyKey(
@@ -106,12 +108,20 @@ export const processDiplomacyMonth = (
continue;
}
const opposite = byKey.get(buildDiplomacyKey(entry.toNationId, entry.fromNationId));
if (opposite && opposite.state === DIPLOMACY_STATE.WAR && opposite.term <= 1) {
if (!opposite || opposite.state !== DIPLOMACY_STATE.WAR) {
continue;
}
const sharedTerm = Math.max(entry.term, opposite.term);
entry.term = sharedTerm;
opposite.term = sharedTerm;
processedPairs.add(pairKey);
if (sharedTerm <= 1) {
entry.state = DIPLOMACY_STATE.TRADE;
entry.term = 0;
opposite.state = DIPLOMACY_STATE.TRADE;
opposite.term = 0;
processedPairs.add(pairKey);
}
}
+27 -1
View File
@@ -61,6 +61,32 @@ describe('diplomacy month processing', () => {
}
});
it('keeps an asymmetric war on one shared countdown and ends it once', () => {
let entries = [buildEntry(1, 2, DIPLOMACY_STATE.WAR, 1), buildEntry(2, 1, DIPLOMACY_STATE.WAR, 3)];
const generalCounts = new Map([
[1, 1],
[2, 1],
]);
entries = processDiplomacyMonth(entries, generalCounts);
expect(entries.map(({ state, term }) => ({ state, term }))).toEqual([
{ state: DIPLOMACY_STATE.WAR, term: 2 },
{ state: DIPLOMACY_STATE.WAR, term: 2 },
]);
entries = processDiplomacyMonth(entries, generalCounts);
expect(entries.map(({ state, term }) => ({ state, term }))).toEqual([
{ state: DIPLOMACY_STATE.WAR, term: 1 },
{ state: DIPLOMACY_STATE.WAR, term: 1 },
]);
entries = processDiplomacyMonth(entries, generalCounts);
expect(entries.map(({ state, term }) => ({ state, term }))).toEqual([
{ state: DIPLOMACY_STATE.TRADE, term: 0 },
{ state: DIPLOMACY_STATE.TRADE, term: 0 },
]);
});
it('extends war term based on accumulated casualties', () => {
const entries = [buildEntry(1, 2, DIPLOMACY_STATE.WAR, 3, 400), buildEntry(2, 1, DIPLOMACY_STATE.WAR, 3, 0)];
const result = processDiplomacyMonth(
@@ -75,7 +101,7 @@ describe('diplomacy month processing', () => {
const reverse = result.find((entry) => entry.fromNationId === 2 && entry.toNationId === 1);
expect(forward?.term).toBe(4);
expect(forward?.dead).toBe(0);
expect(reverse?.term).toBe(2);
expect(reverse?.term).toBe(4);
});
it('expires non-aggression pact into trade', () => {