87 lines
3.0 KiB
JavaScript
87 lines
3.0 KiB
JavaScript
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;
|
|
}
|