import fs from 'node:fs'; import wordlistEnglish from 'wordlist-english'; const COMBO_WORDS_FILE = new URL('./combo-words.txt', import.meta.url); // Extra words (from scripts/find-missing-combo-words.mjs) that are buildable // from pool tiles but missing from wordlist-english. Accepted in play only. export function loadComboWords() { if (!fs.existsSync(COMBO_WORDS_FILE)) return []; return fs.readFileSync(COMBO_WORDS_FILE, 'utf8') .split(/\r?\n/) .map(l => l.trim().toLowerCase()) .filter(l => l && !l.startsWith('#') && /^[a-z]+$/.test(l)); } // Shared dictionary sources for the client lexicon and the build-time solver. // Valid 2-letter English words accepted in standard games export const VALID_TWO = new Set([ 'am', 'an', 'as', 'at', 'be', 'by', 'do', 'go', 'he', 'hi', 'if', 'in', 'is', 'it', 'me', 'my', 'no', 'of', 'oh', 'on', 'or', 'so', 'to', 'up', 'us', 'we' ]); // The base `english` list excludes regional spellings (neighborhood, color, // theater...), so merge in every regional variant list. const SOURCE_LISTS = [ 'english', 'english/american', 'english/british', 'english/canadian', 'english/australian' ]; // wordlist-english has no proper nouns. Commonly-used proper adjectives and // names that players reasonably expect to be accepted go here. export const PROPER_WORD_ALLOWLIST = [ 'mediterranean', 'atlantic', 'pacific', 'caribbean', 'arctic', 'antarctic', 'european', 'african', 'asian', 'american', 'canadian', 'mexican', 'australian', 'arabian', 'egyptian', 'roman', 'greek', 'latin', 'italian', 'spanish', 'french', 'english', 'german', 'chinese', 'japanese', 'indian', 'olympic', 'olympics', 'christmas', 'easter', 'halloween', 'thanksgiving', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december', 'mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune', 'amazon', 'sahara', 'everest', 'antarctica', 'africa', 'europe', 'america', 'asia', 'australia' ]; function isAcceptable(w) { if (w.length === 2) return VALID_TWO.has(w); return w.length >= 3 && w.length <= 16 && /^[a-z]+$/.test(w); } export function buildWordSet({ includeComboWords = false } = {}) { const set = new Set(); for (const key of SOURCE_LISTS) { for (const raw of wordlistEnglish[key] || []) { const w = raw.toLowerCase().trim(); if (isAcceptable(w)) set.add(w); } } for (const w of PROPER_WORD_ALLOWLIST) { if (isAcceptable(w)) set.add(w); } if (includeComboWords) { for (const w of loadComboWords()) { if (isAcceptable(w)) set.add(w); } } return set; } // Commonness tiers (base + American lists) for word quality ranking const tiers = [10, 20, 35, 50].map(n => new Set([ ...(wordlistEnglish[`english/${n}`] || []), ...(wordlistEnglish[`english/american/${n}`] || []) ])); export function getCommonnessScore(word) { for (let i = 0; i < tiers.length; i++) { if (tiers[i].has(word)) return 4 - i; } return 0; }