feat(offline): randomise the hotseat seating (random first mover)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m7s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m42s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m7s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m42s
The 2-4 hotseat players now start in a random seating order (so who goes first, and the turn order, is random) — matching the online games. The engine starts at seat 0, so shuffling the seats at creation picks the starter; the shuffle is seed-driven (a distinct derivation from the tile bag), so replay reproduces it. Scoped to hotseat — vs_ai stays human-first. - lib/roster.ts: shuffleSeeded (unit-tested); NewGame shuffles buildSeats with the game seed before create. - e2e: pin a seed that keeps the roster order so the lock assertions stay deterministic.
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
import { t, type MessageKey } from '../lib/i18n/index.svelte';
|
||||
import { validDisplayName } from '../lib/profileValidation';
|
||||
import { verifyPin, type PinLock } from '../lib/pin';
|
||||
import { commitName, rosterReady, buildSeats, type RosterRow } from '../lib/roster';
|
||||
import { commitName, rosterReady, buildSeats, shuffleSeeded, type RosterRow } from '../lib/roster';
|
||||
import type { AccountRef, Variant } from '../lib/model';
|
||||
import {
|
||||
availableVariants,
|
||||
@@ -255,16 +255,19 @@
|
||||
return;
|
||||
}
|
||||
const id = newLocalGameId();
|
||||
const seed = randomSeed();
|
||||
// Randomise the seating so the FIRST mover is random (the engine starts at seat 0, so shuffling
|
||||
// the seats picks a random starter and turn order). Seed-driven, so replay is consistent.
|
||||
// Snapshot the roster + PINs to plain objects: the rows/pins are $state proxies, and a proxy
|
||||
// fails structured-clone when the record is persisted to IndexedDB (silently, since the store
|
||||
// is best-effort) — so the game would vanish on reload. See lib/localgame/store.
|
||||
// fails structured-clone when the record is persisted to IndexedDB (silently, best-effort store)
|
||||
// — so the game would vanish on reload. See lib/localgame/store.
|
||||
await localSource.create({
|
||||
id,
|
||||
variant: inviteVariant,
|
||||
dictVersion: version,
|
||||
seed: randomSeed(),
|
||||
seed,
|
||||
multipleWords: multipleWordsForRequest(inviteVariant, multipleWords),
|
||||
seats: $state.snapshot(buildSeats(rows)),
|
||||
seats: $state.snapshot(shuffleSeeded(buildSeats(rows), seed)),
|
||||
hotseat: true,
|
||||
hostPin: hostPin ? $state.snapshot(hostPin) : undefined,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user