feat(offline): hotseat record schema + source (locks, host actions)
Extend the local game to offline pass-and-play (hotseat): - serialize.ts: Seat.pin, record hotseat + hostPin (all optional; old vs_ai records read hotseat as false). - source.ts: create() takes hotseat/hostPin + per-seat pins; stateView reveals the seat-to-move's rack and withholds it (locked) when that seat is PIN-locked, until unlockSeat; ephemeral per-turn unlock re-locks on advance; new unlockSeat / verifyHostPin / hostAction (skip/resign/terminate); gameView sets vsAi=!hotseat + the hotseat flag. - model.ts: GameView.hotseat, StateView.locked (offline-only, optional). Tests: source.hotseat (lock/unlock/re-lock, host skip/resign/terminate, master-PIN gate, natural-end persistence) + serialize hotseat shape.
This commit is contained in:
@@ -83,4 +83,20 @@ describe('local game serialize + replay', () => {
|
||||
expect(rec.journal.length).toBe(g.moveCount);
|
||||
expect(replayGame(rec, dawg).seed).toBe(9999999999n);
|
||||
});
|
||||
|
||||
it('serialises hotseat metadata: the mode, master PIN and per-seat PINs', async () => {
|
||||
const { newLock } = await import('../pin');
|
||||
const hostPin = await newLock('9999');
|
||||
const seatPin = await newLock('1234');
|
||||
const hotseatSeats: Seat[] = [
|
||||
{ kind: 'human', name: 'Ann', pin: seatPin },
|
||||
{ kind: 'human', name: 'Bob' },
|
||||
];
|
||||
const g = makeGame(5n);
|
||||
const rec = serializeGame(g, { id: 'h1', seats: hotseatSeats, hotseat: true, hostPin, createdAtUnix: 1, updatedAtUnix: 2, hintUnlockAtMs: 0 });
|
||||
expect(rec.hotseat).toBe(true);
|
||||
expect(rec.hostPin).toEqual(hostPin);
|
||||
expect(rec.seats[0].pin).toEqual(seatPin);
|
||||
expect(rec.seats[1].pin).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user