feat(replay+parser): log order+init, detect unexplained order shifts
replay-combat.js: - Turn line now dumps order=[Name:init,...] (both, not names only) - reorderParticipants call fixed: real drag (dragged→before target), correct signature (ids not array). Was broken (passed array, func wants ids, swallowed by try/catch silent no-op). analyze-turns.js: - Parse order=[Name:init,...] from turn lines - detectOrderShifts: compare order+init between consecutive turns. Flag shifts NOT explained by logged reorder, roster change (add/remove), or init change. Catches display/rotation divergence (invariant: display===turnOrderIds===nextTurn). - Report order shifts count + sample. CLEAN requires 0 shifts. Result: 100-round replay CLEAN (0 skips, 0 doubles, 0 shifts). Note: shift detector reads turnOrderIds dump. reorder still leaves turnOrderIds unchanged (BUG-6) — Path A (step 3) aligns display+rotation, then shift detector catches true divergence.
This commit is contained in:
+22
-10
@@ -178,7 +178,13 @@ async function main() {
|
||||
const actorName = firstActiveName(enc);
|
||||
const actor = currentParticipant(enc);
|
||||
|
||||
console.log(` turn ${totalTurns} (round ${enc.round}): ${actorName} | order=[${enc.turnOrderIds.map(id=>enc.participants.find(p=>p.id===id)?.name||id).join(',')}] cur=${enc.currentTurnParticipantId}`);
|
||||
// Dump turn line with order AND initiative (DM drag may reorder without
|
||||
// changing init — log both so parser can flag unexplained shifts).
|
||||
const ordStr = enc.turnOrderIds.map(id => {
|
||||
const p = enc.participants.find(x => x.id === id);
|
||||
return p ? `${p.name}:${p.initiative}` : id;
|
||||
}).join(',');
|
||||
console.log(` turn ${totalTurns} (round ${enc.round}): ${actorName} | order=[${ordStr}] cur=${enc.currentTurnParticipantId}`);
|
||||
|
||||
// 1. damage: actor hits a random living, active target.
|
||||
if (actor) {
|
||||
@@ -302,19 +308,25 @@ async function main() {
|
||||
lastPaused = true;
|
||||
}
|
||||
|
||||
// 10. reorderParticipants: every 8 turns, shuffle initiative slightly.
|
||||
// 10. reorderParticipants: every 8 turns, drag one past next (DM reorder).
|
||||
if (totalTurns % 8 === 0 && lastReorder !== totalTurns) {
|
||||
const living = enc.participants.filter(p => p.currentHp > 0 && p.isActive !== false);
|
||||
if (living.length >= 2) {
|
||||
// bump first participant's initiative by +1 (deterministic reorder).
|
||||
const tgt = living[0];
|
||||
const newInit = (tgt.initiative || 0) + 1;
|
||||
if (living.length >= 3) {
|
||||
// drag first past second (same-or-cross init, exercises reorder).
|
||||
const dragged = living[0];
|
||||
const target = living[1];
|
||||
try {
|
||||
const reordered = [...enc.participants].map(p => p.id === tgt.id ? { ...p, initiative: newInit } : p);
|
||||
const r = reorderParticipants(enc, reordered);
|
||||
enc = await patch(encounterPath, enc, r, `reorder (${tgt.name} init→${newInit})`);
|
||||
const r = reorderParticipants(enc, dragged.id, target.id);
|
||||
enc = await patch(encounterPath, enc, r, `reorder ${dragged.name}→before ${target.name}`);
|
||||
lastReorder = totalTurns;
|
||||
} catch (e) { /* ignore */ }
|
||||
} catch (e) { /* same-init only — try same-init pair */
|
||||
const sameInit = living.find(p => p !== dragged && p.initiative === dragged.initiative);
|
||||
if (sameInit) {
|
||||
const r = reorderParticipants(enc, dragged.id, sameInit.id);
|
||||
enc = await patch(encounterPath, enc, r, `reorder ${dragged.name}→before ${sameInit.name}`);
|
||||
lastReorder = totalTurns;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user