Files
quartiles/scripts/lexicon-sources.mjs
2026-10-24 14:34:22 -04:00

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;
}