// Game variants offered on New Game, and the gating of that choice by the // languages the sign-in service supports. Kept out of the .svelte screen so the // gating is unit-testable (the project's node-env Vitest layer). import type { MessageKey } from './i18n/index.svelte'; import type { Variant } from './model'; // VariantOption is a selectable game variant with its i18n label key. export interface VariantOption { id: Variant; label: MessageKey; } // ALL_VARIANTS lists every variant in display order (Erudit first — the default // preference and the product's primary variant). The labels are display names keyed by // the game's alphabet, not the interface language: the English-alphabet game is always // "Scrabble" and the Russian-alphabet Scrabble always "Скрэббл" (both unlocalized, so the // two never collide whatever the UI language); Erudit is localized "Erudite"/"Эрудит". export const ALL_VARIANTS: VariantOption[] = [ { id: 'erudit_ru', label: 'new.erudit' }, { id: 'scrabble_ru', label: 'new.russian' }, { id: 'scrabble_en', label: 'new.english' }, ]; // variantNameKey returns the i18n key for a variant's display name (used by the in-game // title and the lobby cards). export function variantNameKey(v: Variant): MessageKey { return ALL_VARIANTS.find((o) => o.id === v)?.label ?? 'new.english'; } // VARIANT_RULES is the i18n key for each variant's one-line rules summary on the New Game // buttons (bag size, the ё rule, bonus differences), sourced from the engine rulesets. export const VARIANT_RULES: Record = { scrabble_en: 'new.rulesEnglish', scrabble_ru: 'new.rulesRussian', erudit_ru: 'new.rulesErudit', }; // VARIANT_FLAG is the flag shown on a variant button: an emoji for the Scrabble variants; // Erudit uses the bundled USSR flag SVG (public/flag-ussr.svg), so its entry is empty. export const VARIANT_FLAG: Record = { scrabble_en: '🇺🇸', scrabble_ru: '🇷🇺', erudit_ru: '', }; // VARIANT_LANGUAGE maps each variant to its game language. en -> English; // ru -> Russian + Эрудит. export const VARIANT_LANGUAGE: Record = { scrabble_en: 'en', scrabble_ru: 'ru', erudit_ru: 'ru' }; // BLANK_STAR is the glyph drawn on an Эрудит blank tile: the variant's blank is the // "звёздочка" (star) chip, so it carries a star rather than a bare face. U+273B HEAVY // TEARDROP-SPOKED ASTERISK. export const BLANK_STAR = '✻'; // usesStarBlank reports whether a variant marks its blank tiles with BLANK_STAR. Only // Эрудит does: an empty rack blank shows the star centred, and a placed blank carries it // in the value corner (the corner is free — a blank has no point value). The Scrabble // variants leave the blank unmarked (an empty rack face; a placed blank shown by its // designated letter alone). export function usesStarBlank(v: Variant): boolean { return v === 'erudit_ru'; } // DEFAULT_VARIANTS is the variant set a player sees before any preference is stored — a fresh // or offline native client whose profile has not been synced yet (the device-local guest boots // with no profile at all). It mirrors the backend's new-account default (the account service // seeds Erudit only), so the local guest and a later synced account agree on what is enabled. // Every dictionary is still bundled; the other variants are simply off until the player turns // them on in Settings. export const DEFAULT_VARIANTS: Variant[] = ['erudit_ru']; // availableVariants gates ALL_VARIANTS by the player's variant preferences (the set they // enabled in Settings). An empty or absent set falls back to DEFAULT_VARIANTS rather than every // variant: a real profile always carries at least one preference, and a profileless client (a // fresh offline native launch) must match the server's Erudit-only default instead of exposing // the English game before the player opts in. export function availableVariants(preferences: Variant[] | undefined): VariantOption[] { const prefs = preferences ?? []; const effective = prefs.length === 0 ? DEFAULT_VARIANTS : prefs; return ALL_VARIANTS.filter((v) => effective.includes(v.id)); } // supportsMultipleWordsToggle reports whether the New Game "multiple words per turn" toggle // applies to a variant. Only Russian games choose the rule; English is always standard, so // its toggle is not shown. export function supportsMultipleWordsToggle(v: Variant): boolean { return VARIANT_LANGUAGE[v] === 'ru'; } // multipleWordsForRequest resolves the per-turn word rule sent when starting a game of the // variant: Russian games carry the toggle's value, English games are silently standard // (true), so matchmaking and game creation stay one uniform path. export function multipleWordsForRequest(v: Variant, toggle: boolean): boolean { return supportsMultipleWordsToggle(v) ? toggle : true; }