From d9e2ad7ee6a8035528c8be4b4ba5f46c3af3f5fc Mon Sep 17 00:00:00 2001 From: Wickedness Date: Mon, 6 Jul 2026 17:14:19 +0900 Subject: [PATCH] Expand southern campaign release QA --- package.json | 4 +- scripts/qa-representative-battles.mjs | 169 ++++++++++++++++++++++++-- 2 files changed, 160 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index a453d9e..a9a8e6f 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "verify:public-deploy": "node scripts/verify-public-deploy.mjs", "qa:representative": "node scripts/qa-representative-battles.mjs", "qa:smoke": "node scripts/qa-representative-battles.mjs --set=smoke", - "qa:early": "node scripts/qa-representative-battles.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20", - "qa:early:repeat": "node scripts/qa-repeat.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20", + "qa:early": "node scripts/qa-representative-battles.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27", + "qa:early:repeat": "node scripts/qa-repeat.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27", "deploy:nas": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy-nas.ps1 -Build", "deploy:nas:dist": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy-nas.ps1", "ship:release": "node scripts/ship-release.mjs" diff --git a/scripts/qa-representative-battles.mjs b/scripts/qa-representative-battles.mjs index 34cccc2..e2b6e17 100644 --- a/scripts/qa-representative-battles.mjs +++ b/scripts/qa-representative-battles.mjs @@ -75,6 +75,152 @@ const northernTenthSortie = ['zhuge-liang', 'jiang-wei', 'wang-ping', 'huang-qua const northernEleventhSortie = ['zhuge-liang', 'jiang-wei', 'ma-dai', 'wang-ping', 'huang-quan', 'li-yan', 'wei-yan']; const northernFinalSortie = ['zhuge-liang', 'jiang-wei', 'wang-ping', 'ma-dai', 'huang-quan', 'li-yan', 'wei-yan']; +const redCliffVanguardTargets = [ + 'redcliff-vanguard-scout-a', + 'redcliff-vanguard-scout-b', + 'redcliff-vanguard-marine-a', + 'redcliff-vanguard-marine-b', + 'redcliff-vanguard-marine-c', + 'redcliff-vanguard-marine-d', + 'redcliff-vanguard-archer-a', + 'redcliff-vanguard-archer-b', + 'redcliff-vanguard-archer-c', + 'redcliff-vanguard-cavalry-a', + 'redcliff-vanguard-cavalry-b', + 'redcliff-vanguard-cavalry-c', + 'redcliff-vanguard-guard-a', + 'redcliff-vanguard-guard-b', + 'redcliff-vanguard-strategist-a', + 'redcliff-vanguard-strategist-b', + 'redcliff-vanguard-leader-cai-mao' +]; + +const redCliffFireTargets = [ + 'redcliff-fire-scout-a', + 'redcliff-fire-scout-b', + 'redcliff-fire-marine-a', + 'redcliff-fire-marine-b', + 'redcliff-fire-marine-c', + 'redcliff-fire-marine-d', + 'redcliff-fire-archer-a', + 'redcliff-fire-archer-b', + 'redcliff-fire-archer-c', + 'redcliff-fire-archer-d', + 'redcliff-fire-cavalry-a', + 'redcliff-fire-cavalry-b', + 'redcliff-fire-cavalry-c', + 'redcliff-fire-guard-a', + 'redcliff-fire-guard-b', + 'redcliff-fire-strategist-a', + 'redcliff-fire-strategist-b', + 'redcliff-fire-leader-cao-cao' +]; + +const jingSouthTargets = [ + 'jing-south-scout-a', + 'jing-south-scout-b', + 'jing-south-infantry-a', + 'jing-south-infantry-b', + 'jing-south-infantry-c', + 'jing-south-infantry-d', + 'jing-south-archer-a', + 'jing-south-archer-b', + 'jing-south-archer-c', + 'jing-south-cavalry-a', + 'jing-south-cavalry-b', + 'jing-south-cavalry-c', + 'jing-south-guard-a', + 'jing-south-guard-b', + 'jing-south-strategist-a', + 'jing-south-strategist-b', + 'jing-south-leader-xing-daorong' +]; + +const guiyangTargets = [ + 'guiyang-scout-a', + 'guiyang-scout-b', + 'guiyang-infantry-a', + 'guiyang-infantry-b', + 'guiyang-infantry-c', + 'guiyang-infantry-d', + 'guiyang-archer-a', + 'guiyang-archer-b', + 'guiyang-archer-c', + 'guiyang-cavalry-a', + 'guiyang-cavalry-b', + 'guiyang-cavalry-c', + 'guiyang-guard-a', + 'guiyang-guard-b', + 'guiyang-strategist-a', + 'guiyang-strategist-b', + 'guiyang-leader-zhao-fan' +]; + +const wulingTargets = [ + 'wuling-scout-a', + 'wuling-scout-b', + 'wuling-scout-c', + 'wuling-bandit-a', + 'wuling-bandit-b', + 'wuling-bandit-c', + 'wuling-infantry-a', + 'wuling-infantry-b', + 'wuling-infantry-c', + 'wuling-infantry-d', + 'wuling-archer-a', + 'wuling-archer-b', + 'wuling-archer-c', + 'wuling-cavalry-a', + 'wuling-cavalry-b', + 'wuling-guard-a', + 'wuling-guard-b', + 'wuling-strategist-a', + 'wuling-leader-jin-xuan' +]; + +const changshaTargets = [ + 'changsha-scout-a', + 'changsha-scout-b', + 'changsha-infantry-a', + 'changsha-infantry-b', + 'changsha-infantry-c', + 'changsha-infantry-d', + 'changsha-archer-a', + 'changsha-archer-b', + 'changsha-archer-c', + 'changsha-cavalry-a', + 'changsha-cavalry-b', + 'changsha-guard-a', + 'changsha-guard-b', + 'changsha-strategist-a', + 'changsha-strategist-b', + 'changsha-veteran-huang-zhong', + 'changsha-officer-wei-yan', + 'changsha-leader-han-xuan' +]; + +const yizhouTargets = [ + 'yizhou-scout-a', + 'yizhou-scout-b', + 'yizhou-infantry-a', + 'yizhou-infantry-b', + 'yizhou-infantry-c', + 'yizhou-infantry-d', + 'yizhou-archer-a', + 'yizhou-archer-b', + 'yizhou-archer-c', + 'yizhou-cavalry-a', + 'yizhou-cavalry-b', + 'yizhou-cavalry-c', + 'yizhou-guard-a', + 'yizhou-guard-b', + 'yizhou-strategist-a', + 'yizhou-strategist-b', + 'yizhou-ambusher-a', + 'yizhou-ambusher-b', + 'yizhou-leader-yang-huai' +]; + const earlyCampaignBattles = [ { no: 1, id: 'first-battle-zhuo-commandery', selected: [], targets: ['rebel-archer-b', 'rebel-leader'], protected: ['liu-bei'] }, { @@ -371,49 +517,49 @@ const southernCampaignBattles = [ no: 21, id: 'twenty-first-battle-red-cliffs-vanguard', selected: redCliffVanguardSortie, - targets: ['redcliff-vanguard-leader-cai-mao'], + targets: redCliffVanguardTargets, protected: ['liu-bei', 'zhuge-liang'] }, { no: 22, id: 'twenty-second-battle-red-cliffs-fire', selected: redCliffFireSortie, - targets: ['redcliff-fire-leader-cao-cao'], + targets: redCliffFireTargets, protected: ['liu-bei', 'zhuge-liang'] }, { no: 23, id: 'twenty-third-battle-jingzhou-south-entry', selected: jingSouthSortie, - targets: ['jing-south-leader-xing-daorong'], + targets: jingSouthTargets, protected: ['liu-bei', 'zhuge-liang'] }, { no: 24, id: 'twenty-fourth-battle-guiyang-persuasion', selected: guiyangSortie, - targets: ['guiyang-leader-zhao-fan'], + targets: guiyangTargets, protected: ['liu-bei', 'zhuge-liang', 'ma-liang'] }, { no: 25, id: 'twenty-fifth-battle-wuling-mountain-road', selected: wulingSortie, - targets: ['wuling-leader-jin-xuan'], + targets: wulingTargets, protected: ['liu-bei', 'zhuge-liang', 'ma-liang'] }, { no: 26, id: 'twenty-sixth-battle-changsha-veteran', selected: changshaSortie, - targets: ['changsha-leader-han-xuan'], + targets: changshaTargets, protected: ['liu-bei', 'zhuge-liang'] }, { no: 27, id: 'twenty-seventh-battle-yizhou-relief-road', selected: yizhouReliefSortie, - targets: ['yizhou-leader-yang-huai'], + targets: yizhouTargets, protected: ['liu-bei', 'zhuge-liang'] }, { @@ -1535,7 +1681,7 @@ async function playBattleWithoutDebugVictory(page, battle) { } function protectedAnchorTile(unit, tiles, pressProtected) { - if (!protectedIds.has(unit.id) || pressProtected || liveUnits('enemy').length <= 4) { + if (!protectedIds.has(unit.id) || pressProtected || isLastProtectedFighter(unit) || liveUnits('enemy').length <= 4) { return undefined; } @@ -1582,7 +1728,7 @@ async function playBattleWithoutDebugVictory(page, battle) { function advancePointTile(unit, tiles) { const point = scenarioAdvancePoint(unit); - if (!point || protectedIds.has(unit.id)) { + if (!point || (protectedIds.has(unit.id) && !isLastProtectedFighter(unit))) { return undefined; } const currentDistance = distance(unit, point); @@ -1595,6 +1741,7 @@ async function playBattleWithoutDebugVictory(page, battle) { function chooseMove(unit) { const tiles = [{ x: unit.x, y: unit.y, cost: 0 }, ...scene.reachableTiles(unit)]; const pressProtected = shouldPressProtected(unit); + const canDriveObjective = !protectedIds.has(unit.id) || pressProtected || isLastProtectedFighter(unit); const anchorTile = protectedAnchorTile(unit, tiles, pressProtected); if (anchorTile) { return anchorTile; @@ -1638,7 +1785,7 @@ async function playBattleWithoutDebugVictory(page, battle) { return scenarioTile; } - if (!protectedIds.has(unit.id) || pressProtected) { + if (canDriveObjective) { const focus = focusTarget(unit); if (focus) { const currentDistance = distance(unit, focus); @@ -1661,7 +1808,7 @@ async function playBattleWithoutDebugVictory(page, battle) { } const objective = currentSecureObjective(); - if (objective && (!protectedIds.has(unit.id) || pressProtected)) { + if (objective && canDriveObjective) { const currentObjectiveDistance = objectiveDistance(unit, objective); const objectivePressureTile = tiles .filter((tile) => tile.x !== unit.x || tile.y !== unit.y)