Merge branch 'development' into feature/offline-hint-gate
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m28s
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 3s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m28s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m42s
# Conflicts: # ui/src/lib/localgame/source.ts
This commit is contained in:
@@ -13,6 +13,7 @@ vi.mock('../dict', () => ({
|
||||
|
||||
import { LocalSource, isLocalGameId } from './source';
|
||||
import type { Seat } from './serialize';
|
||||
import { hasAlphabet, alphabetValues } from '../alphabet';
|
||||
|
||||
const seats: Seat[] = [
|
||||
{ kind: 'human', name: 'You' },
|
||||
@@ -42,6 +43,18 @@ describe('LocalSource', () => {
|
||||
expect(st.game.toMove).toBe(0);
|
||||
});
|
||||
|
||||
it('seeds the per-variant alphabet from the ruleset, so offline tiles show real weights not zeros', async () => {
|
||||
// Offline there is no server to send the alphabet table the rack/board render from; opening a
|
||||
// local game must seed it from the static ruleset. Without this a cold offline boot draws zeros.
|
||||
const src = await newSource('local:gAlpha', 42n);
|
||||
await src.gameState('local:gAlpha');
|
||||
expect(hasAlphabet('scrabble_en')).toBe(true);
|
||||
const values = alphabetValues('scrabble_en');
|
||||
expect(values.length).toBe(26);
|
||||
expect(values[0]).toBe(1); // A = 1 (pinned to rules.go / ruleset.ts), not 0
|
||||
expect(values.every((v) => v > 0)).toBe(true);
|
||||
});
|
||||
|
||||
it('replies to a human pass with a synchronous robot move, delivered via the event', async () => {
|
||||
const src = await newSource('local:g2', 999n);
|
||||
const events: PushEvent[] = [];
|
||||
|
||||
@@ -13,6 +13,7 @@ import { serializeGame, replayGame, type LocalGameRecord, type Seat as LocalSeat
|
||||
import { getLocalGame, saveLocalGame, listLocalGames, deleteLocalGame } from './store';
|
||||
import { RULESETS } from './ruleset';
|
||||
import { getDawg } from '../dict';
|
||||
import { setAlphabet, hasAlphabet } from '../alphabet';
|
||||
import { HINT_GATE_MS } from '../hints';
|
||||
import { LOCAL_ID_PREFIX, isLocalGameId } from './id';
|
||||
import { decide } from '../robot/strategy';
|
||||
@@ -362,8 +363,22 @@ export class LocalSource implements GameLoopSource {
|
||||
};
|
||||
}
|
||||
|
||||
// Offline there is no server to send the per-variant alphabet table (letters + tile values) the
|
||||
// rack and board render from (lib/alphabet); seed it from the static offline ruleset so a cold-
|
||||
// booted local game shows real tile weights instead of zeros. Idempotent and cheap — a no-op once
|
||||
// the table is cached (from here or a prior online session).
|
||||
private ensureAlphabet(variant: Variant): void {
|
||||
if (hasAlphabet(variant)) return;
|
||||
const rs = RULESETS[variant];
|
||||
setAlphabet(
|
||||
variant,
|
||||
rs.letters.map((letter, index) => ({ index, letter, value: rs.values[index] })),
|
||||
);
|
||||
}
|
||||
|
||||
private gameView(entry: Live): GameView {
|
||||
const { game, record } = entry;
|
||||
this.ensureAlphabet(record.variant);
|
||||
const seats: Seat[] = record.seats.map((s, i) => ({
|
||||
seat: i,
|
||||
accountId: s.accountId ?? `${LOCAL_ID_PREFIX}${s.kind}:${i}`,
|
||||
|
||||
Reference in New Issue
Block a user