more words

This commit is contained in:
2026-10-24 14:29:08 -04:00
parent eae0dba308
commit f07b5fdadf
13 changed files with 3563 additions and 434 deletions

View File

@@ -1,22 +1,9 @@
import fs from 'node:fs';
import wordlistEnglish from 'wordlist-english';
const validTwo = 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'
]);
import { buildWordSet } from './lexicon-sources.mjs';
// Build a clean, high-frequency recognized vocabulary for client-side unlimited/custom puzzles
const vocabulary = new Set();
for (const word of wordlistEnglish.english) {
const w = word.toLowerCase().trim();
if (w.length === 2 && validTwo.has(w)) {
vocabulary.add(w);
} else if (w.length >= 3 && w.length <= 16 && /^[a-z]+$/.test(w)) {
vocabulary.add(w);
}
}
// (includes regional spellings and the proper-word allowlist)
const vocabulary = buildWordSet();
console.log('Total vocabulary words:', vocabulary.size);

View File

@@ -466,6 +466,249 @@ slippers|sl-ip-pe-rs|home|Soft shoes worn indoors
potatoes|po-ta-to-es|food|Starchy vegetables used for fries
sausages|sa-us-ag-es|food|Seasoned meat in a casing
pretzels|pr-et-ze-ls|food|Twisted salty baked snacks
absolute|ab-so-lu-te|everyday|Complete and total
academic|ac-ad-em-ic|education|Relating to school or scholarship
accident|ac-ci-de-nt|everyday|Unplanned event that causes harm
accidental|ac-ci-den-tal|everyday|Happening by chance
accuracy|ac-cu-ra-cy|everyday|Quality of being correct and precise
accurate|ac-cu-ra-te|everyday|Correct in every detail
activity|ac-ti-vi-ty|everyday|Something you do for fun or a purpose
additional|ad-di-tion-al|everyday|Added or extra
advertise|ad-ver-ti-se|business|Promote a product publicly
agreement|ag-re-em-ent|business|Shared understanding or deal
alternative|al-ter-na-tive|everyday|Another possible choice
analysis|an-al-ys-is|science|Detailed examination of something
announce|an-no-un-ce|everyday|Make known publicly
anonymous|ano-ny-mo-us|everyday|Of unknown name
appearance|ap-pea-ra-nce|everyday|The way someone or something looks
appreciate|ap-pre-ci-ate|emotion|Recognize the value of
approach|ap-pr-oa-ch|everyday|Come near to
appropriate|ap-pro-pri-ate|everyday|Suitable for the situation
argument|ar-gu-me-nt|everyday|Heated disagreement
arrangement|ar-ran-ge-ment|everyday|Plan or organized layout
assembly|as-se-mb-ly|education|Gathering of people, as at school
assistant|as-si-st-ant|jobs|Person who helps another
associate|as-so-ci-ate|business|Connect in the mind; a colleague
attitude|at-ti-tu-de|emotion|Way of thinking or feeling
attractive|at-tra-cti-ve|everyday|Pleasing to look at
audience|au-di-en-ce|arts|People watching a performance
authority|au-th-or-ity|society|Power to give orders
automatic|au-to-ma-tic|tech|Working by itself
available|av-ai-la-ble|everyday|Ready to be used
background|ba-ck-gro-und|arts|Scenery behind the main subject
broadcast|bro-ad-ca-st|tech|Transmit on radio or TV
bulletin|bu-ll-et-in|everyday|Short official news update
business|bu-si-ne-ss|business|Company or commercial activity
calculate|ca-lc-ul-ate|education|Work out with math
candidate|can-di-da-te|society|Person running for office
capacity|ca-pa-ci-ty|everyday|Maximum amount something can hold
category|ca-te-go-ry|everyday|Group of similar things
character|ch-ar-ac-ter|arts|Person in a story
chemical|ch-em-ic-al|science|Substance made by chemistry
collapse|co-ll-ap-se|everyday|Fall down suddenly
commercial|com-mer-ci-al|business|TV advertisement
committee|com-mi-tt-ee|society|Group chosen to make decisions
communicate|com-mu-nic-ate|everyday|Share information
community|com-mu-ni-ty|society|People living in one area
comparison|com-pa-ri-son|everyday|Looking at similarities and differences
complain|co-mp-la-in|emotion|Express unhappiness
complaint|co-mp-la-int|everyday|Statement of dissatisfaction
complete|co-mp-le-te|everyday|Finished; whole
compromise|com-pro-mi-se|society|Deal where both sides give a little
conclusion|co-nc-lu-sion|education|The end or final decision
concrete|co-nc-re-te|city|Hard building material for sidewalks
conference|co-nf-ere-nce|business|Formal meeting for discussion
confident|con-fi-de-nt|emotion|Sure of yourself
consistent|co-ns-ist-ent|everyday|Unchanging over time
constant|co-ns-ta-nt|everyday|Never stopping
continue|co-nt-in-ue|everyday|Keep going
continuous|con-ti-nu-ous|everyday|Without a break
contract|co-nt-ra-ct|business|Written legal agreement
contrast|co-nt-ra-st|arts|Striking difference
contribute|con-tri-bu-te|society|Give to a shared cause
convenient|con-ve-ni-ent|everyday|Easy and handy
creature|cr-ea-tu-re|animals|Living animal or being
critical|cr-it-ic-al|everyday|Extremely important; faultfinding
dangerous|da-ng-er-ous|everyday|Likely to cause harm
decision|de-ci-si-on|everyday|A choice that has been made
decrease|de-cr-ea-se|everyday|Make smaller
dedicate|de-di-ca-te|emotion|Devote to a purpose
definite|de-fi-ni-te|everyday|Clearly decided
deliberate|de-li-ber-ate|everyday|Done on purpose
delivery|de-li-ve-ry|everyday|Bringing goods to the door
democratic|de-mo-cra-tic|history|Ruled by the people
demonstrate|de-mon-stra-te|education|Show how something works
department|de-pa-rt-ment|places|Division of a store or company
describe|de-sc-ri-be|words|Tell what something is like
desperate|de-sp-er-ate|emotion|Willing to try anything
determine|de-ter-mi-ne|everyday|Figure out or decide
difference|di-ff-ere-nce|everyday|Way in which things are unlike
different|di-ff-er-ent|everyday|Not the same
director|di-re-ct-or|arts|Person in charge of making a film
disagree|di-sa-gr-ee|everyday|Have a different opinion
disappear|di-sa-pp-ear|fantasy|Vanish from sight
disaster|di-sa-st-er|nature|Sudden terrible event
discount|di-sc-ou-nt|business|Reduction in price
discussion|di-sc-us-sion|everyday|Conversation about a topic
distance|di-st-an-ce|travel|How far apart things are
distinct|di-st-in-ct|everyday|Clearly different
district|di-st-ri-ct|city|Area of a city or country
division|di-vi-si-on|education|Splitting a number into parts
document|do-cu-me-nt|business|Official paper or file
economic|ec-on-om-ic|business|Relating to money and trade
effective|ef-fe-cti-ve|everyday|Producing the desired result
efficient|ef-fi-ci-ent|everyday|Working without waste
electric|el-ec-tr-ic|tech|Powered by electricity
embarrass|em-ba-rr-ass|emotion|Make someone feel awkward
emergency|em-er-gen-cy|health|Serious situation needing quick action
emphasis|em-ph-as-is|words|Special importance
encounter|en-co-un-ter|everyday|Unexpected meeting
encourage|en-cou-ra-ge|emotion|Give support and confidence
enormous|en-or-mo-us|everyday|Very large
entrance|en-tr-an-ce|places|Way in
equipment|equ-ip-me-nt|sports|Tools needed for a job or sport
essential|ess-en-ti-al|everyday|Absolutely necessary
estimate|es-ti-ma-te|education|Rough calculation
excellent|ex-ce-ll-ent|emotion|Extremely good
exchange|ex-ch-an-ge|business|Trade one thing for another
existence|ex-is-te-nce|mind|State of being real
expensive|ex-pen-si-ve|business|Costing a lot
expression|ex-pr-es-sion|emotion|Look on someone's face
facility|fa-ci-li-ty|places|Building made for a purpose
familiar|fa-mi-li-ar|everyday|Well known
feedback|fe-ed-ba-ck|business|Comments on how you did
financial|fin-an-ci-al|business|Relating to money
forthcoming|for-th-com-ing|everyday|About to happen
frequent|fr-eq-ue-nt|everyday|Happening often
generate|ge-ne-ra-te|tech|Produce or create
governor|go-ve-rn-or|history|Elected head of a state
graduate|gr-ad-ua-te|education|Finish school
grateful|gr-at-ef-ul|emotion|Thankful
guarantee|gu-ar-ant-ee|business|Firm promise
harmless|ha-rm-le-ss|everyday|Not able to hurt
historical|hi-sto-ri-cal|history|Relating to the past
immediate|im-me-di-ate|everyday|Happening right away
importance|im-por-ta-nce|everyday|State of mattering a lot
important|im-po-rt-ant|everyday|Of great value or significance
impression|im-pr-es-sion|emotion|Idea formed about someone
improvement|im-pro-ve-ment|everyday|Change for the better
incident|in-ci-de-nt|everyday|Event or occurrence
incomplete|in-com-ple-te|everyday|Not finished
increase|in-cr-ea-se|everyday|Make larger
indicate|in-di-ca-te|everyday|Point out
infinite|in-fi-ni-te|space|Without end
influence|in-fl-ue-nce|society|Power to affect others
innocent|in-no-ce-nt|emotion|Not guilty
instance|in-st-an-ce|everyday|An example
insurance|in-su-ra-nce|business|Protection against loss
intelligent|in-tel-lig-ent|mind|Smart
interact|in-te-ra-ct|society|Act with one another
irritate|ir-ri-ta-te|emotion|Annoy
language|la-ng-ua-ge|words|System of words people speak
lifetime|li-fe-ti-me|everyday|Length of a life
majority|ma-jo-ri-ty|society|More than half
material|ma-te-ri-al|craft|Fabric or substance things are made of
meaningful|me-an-ing-ful|emotion|Full of significance
membership|me-mb-ers-hip|society|Belonging to a group
military|mi-li-ta-ry|history|Armed forces
minority|mi-no-ri-ty|society|Less than half
movement|mo-ve-me-nt|everyday|Act of moving
national|na-ti-on-al|society|Relating to a nation
necessary|nec-es-sa-ry|everyday|Required
necessity|ne-ce-ss-ity|everyday|Something you need
negative|ne-ga-ti-ve|everyday|Opposite of positive
nonsense|no-ns-en-se|words|Silly talk
occasion|oc-ca-si-on|everyday|Special event
official|of-fi-ci-al|society|Formally approved
opposite|op-po-si-te|words|Completely different
optional|op-ti-on-al|everyday|Not required
ordinary|or-di-na-ry|everyday|Normal; everyday
original|or-ig-in-al|arts|The first version
paragraph|pa-ra-gra-ph|words|Block of sentences
parallel|pa-ra-ll-el|education|Side by side, never meeting
particular|par-ti-cu-lar|everyday|Specific; fussy
peculiar|pe-cu-li-ar|everyday|Strange or odd
performance|per-for-ma-nce|arts|Show in front of an audience
permanent|pe-rm-an-ent|everyday|Lasting forever
permission|per-mis-si-on|everyday|Being allowed to do something
personal|pe-rs-on-al|everyday|Private; your own
persuade|pe-rs-ua-de|words|Convince
physical|ph-ys-ic-al|health|Relating to the body
pleasant|pl-ea-sa-nt|emotion|Nice and enjoyable
pointless|po-in-tl-ess|everyday|Having no purpose
political|po-li-ti-cal|society|Relating to government
positive|po-si-ti-ve|emotion|Hopeful; greater than zero
potential|pot-en-ti-al|everyday|Possible future ability
powerful|po-we-rf-ul|everyday|Very strong
practical|pr-ac-ti-cal|everyday|Useful in real life
practice|pr-ac-ti-ce|sports|Repeat to improve
preference|pr-ef-ere-nce|everyday|Something liked more
presence|pr-es-en-ce|everyday|Being there
preserve|pr-es-er-ve|food|Keep safe; fruit jam
pressure|pr-es-su-re|science|Force pushing on something
previous|pr-ev-io-us|everyday|Coming before
principle|pr-in-ci-ple|mind|Basic rule or belief
procedure|pr-oc-ed-ure|health|Set way of doing something
progress|pr-og-re-ss|everyday|Forward movement
property|pr-op-er-ty|home|Land or belongings
proposal|pr-op-os-al|emotion|Plan or offer, such as of marriage
prospect|pr-os-pe-ct|business|Possibility of future success
puncture|pu-nc-tu-re|travel|Small hole, like in a tire
quantity|qu-an-ti-ty|everyday|Amount
recommend|re-com-me-nd|everyday|Suggest as good
register|re-gi-st-er|business|Sign up officially
relationship|re-la-tion-ship|emotion|Connection between people
relative|re-la-ti-ve|everyday|Family member
relevant|re-le-va-nt|everyday|Related to the topic
reliable|re-li-ab-le|everyday|Dependable
religion|re-li-gi-on|society|Belief system of worship
religious|re-li-gi-ous|society|Devoted to a faith
reluctant|re-lu-ct-ant|emotion|Unwilling
remember|re-me-mb-er|mind|Recall from memory
replacement|re-pl-ace-ment|everyday|Thing used instead
represent|re-pr-es-ent|society|Stand for or speak for
research|re-se-ar-ch|science|Careful study to learn facts
resident|re-si-de-nt|home|Person who lives somewhere
resource|re-so-ur-ce|everyday|Useful supply
response|re-sp-on-se|words|Answer or reply
restrict|re-st-ri-ct|everyday|Limit
ridiculous|ri-dic-ul-ous|emotion|Absurd
secretary|se-cre-ta-ry|jobs|Office assistant
security|se-cu-ri-ty|tech|Safety and protection
sentence|se-nt-en-ce|words|Group of words expressing a complete thought
separate|se-pa-ra-te|everyday|Set apart
sequence|se-qu-en-ce|everyday|Order in which things follow
shortage|sh-or-ta-ge|business|Not enough of something
something|so-me-th-ing|everyday|An unspecified thing
southern|so-ut-he-rn|travel|In or from the south
standard|st-an-da-rd|everyday|Usual level of quality
statement|st-at-em-ent|words|Something said or written formally
straight|st-ra-ig-ht|everyday|Without a bend
strategy|st-ra-te-gy|games|Plan to reach a goal
strength|st-re-ng-th|sports|Being strong
substitute|sub-sti-tu-te|sports|Replacement player
successful|su-cc-ess-ful|endeavor|Achieving a goal
suitable|su-it-ab-le|everyday|Right for the purpose
superior|su-pe-ri-or|everyday|Better; higher in rank
surprise|su-rp-ri-se|emotion|Unexpected event
suspicion|su-spi-ci-on|mystery|Feeling that something is wrong
technical|te-ch-ni-cal|tech|Relating to technology or skill
technique|te-ch-ni-que|arts|Way of doing something skillfully
temporary|te-mp-ora-ry|everyday|Lasting a short time
tendency|te-nd-en-cy|mind|Habit or inclination
thousand|th-ou-sa-nd|education|Ten hundred
together|to-ge-th-er|society|With each other
traditional|tra-di-tion-al|history|Following long-held customs
translate|tr-an-sl-ate|words|Change into another language
transport|tr-an-spo-rt|travel|Carry from place to place
treatment|tre-at-me-nt|health|Medical care
ultimate|ul-ti-ma-te|everyday|Final; best possible
understand|un-der-sta-nd|mind|Grasp the meaning
universal|un-iv-ers-al|everyday|Applying to everyone
unpleasant|un-ple-as-ant|emotion|Not nice
valuable|va-lu-ab-le|everyday|Worth a lot
widespread|wi-de-spr-ead|everyday|Found over a large area
worthwhile|wor-th-whi-le|everyday|Worth the time or effort
`;
export const extraWords = RAW.trim()

View File

@@ -0,0 +1,90 @@
// Finds common English words that can be built from exactly 4 existing tiles
// (chunks) in the target-word pool, but aren't target words yet.
//
// Usage: node scripts/find-chunk-words.mjs [--max-tier=35] [--json]
import wl from 'wordlist-english';
import { DICTIONARY_SET } from '../src/data/lexicon.js';
import { TARGET_WORDS_POOL } from '../src/data/puzzles.js';
const args = Object.fromEntries(process.argv.slice(2).map(a => a.replace(/^--/, '').split('=')));
const MAX_TIER = Number(args['max-tier'] || 35);
const TIERS = [10, 20, 35, 40, 50].filter(t => t <= MAX_TIER);
// Every chunk currently used by a target word
const chunks = new Set(TARGET_WORDS_POOL.flatMap(w => w.tiles.map(t => t.toLowerCase())));
const existing = new Set(TARGET_WORDS_POOL.map(w => w.word));
// Common words only (frequency tiers from wordlist-english)
const candidates = new Set();
for (const t of TIERS) for (const w of wl[`english/${t}`] || []) candidates.add(w);
// Try to split a word into exactly 4 distinct chunks (each >= 2 letters).
// Returns the most balanced split, or null.
function split(word) {
let best = null;
const n = word.length;
for (let a = 2; a <= n - 6; a++) {
const t1 = word.slice(0, a);
if (!chunks.has(t1)) continue;
for (let b = a + 2; b <= n - 4; b++) {
const t2 = word.slice(a, b);
if (!chunks.has(t2)) continue;
for (let c = b + 2; c <= n - 2; c++) {
const t3 = word.slice(b, c);
const t4 = word.slice(c);
if (!chunks.has(t3) || !chunks.has(t4)) continue;
const tiles = [t1, t2, t3, t4];
if (new Set(tiles).size !== 4) continue;
const lens = tiles.map(t => t.length);
const spread = Math.max(...lens) - Math.min(...lens);
if (!best || spread < best.spread) best = { tiles, spread };
}
}
}
return best && best.tiles;
}
// Skip inflected forms whose base word exists (walked, happier, quickly, kittens...)
const allWords = new Set(wl.english);
function isInflection(w) {
const bases = [];
if (w.endsWith('ies')) bases.push(w.slice(0, -3) + 'y');
if (w.endsWith('es')) bases.push(w.slice(0, -2));
if (w.endsWith('s')) bases.push(w.slice(0, -1));
if (w.endsWith('ed')) bases.push(w.slice(0, -2), w.slice(0, -1), w.slice(0, -3));
if (w.endsWith('ied')) bases.push(w.slice(0, -3) + 'y');
if (w.endsWith('ing')) bases.push(w.slice(0, -3), w.slice(0, -3) + 'e', w.slice(0, -4));
if (w.endsWith('ly')) bases.push(w.slice(0, -2), w.slice(0, -3) + 'e');
if (w.endsWith('ily')) bases.push(w.slice(0, -3) + 'y');
if (w.endsWith('er')) bases.push(w.slice(0, -2), w.slice(0, -1), w.slice(0, -3));
if (w.endsWith('est')) bases.push(w.slice(0, -3), w.slice(0, -2), w.slice(0, -4));
if (w.endsWith('ness')) bases.push(w.slice(0, -4));
return bases.some(b => b.length >= 3 && allWords.has(b));
}
// Skip near-duplicates of words already in the pool (e.g. "dolphin" vs "dolphins")
function isInflectionOfExisting(w) {
const stems = [w.replace(/s$/, ''), w.replace(/es$/, ''), w.replace(/ies$/, 'y'), w + 's', w + 'es', w.replace(/y$/, 'ies')];
return stems.some(s => s !== w && existing.has(s));
}
const found = [];
for (const word of candidates) {
if (word.length < 8 || word.length > 14) continue;
if (!/^[a-z]+$/.test(word)) continue;
if (existing.has(word)) continue;
if (word.endsWith('tion')) continue;
if (!DICTIONARY_SET.has(word)) continue;
if (isInflectionOfExisting(word)) continue;
if (!('keep-inflections' in args) && isInflection(word)) continue;
const tiles = split(word);
if (tiles) found.push({ word, tiles });
}
found.sort((a, b) => a.word.localeCompare(b.word));
if ('json' in args) {
console.log(JSON.stringify(found));
} else {
for (const f of found) console.log(`${f.word}|${f.tiles.join('-')}`);
console.error(`\n${chunks.size} chunks, ${found.length} new words found (tiers <= ${MAX_TIER})`);
}

View File

@@ -0,0 +1,68 @@
import wordlistEnglish from 'wordlist-english';
// 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() {
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);
}
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;
}

View File

@@ -1,37 +1,9 @@
import fs from 'node:fs';
import wordlistEnglish from 'wordlist-english';
import { buildWordSet, getCommonnessScore } from './lexicon-sources.mjs';
import { capWordList } from '../src/engine/word-cap.js';
// Valid 2-letter English words accepted in standard games
const validTwo = 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'
]);
// Build standard clean dictionary (excluding vulgarities, numbers, non-alpha)
const cleanWordSet = new Set();
for (const raw of wordlistEnglish.english) {
const w = raw.toLowerCase().trim();
if (w.length === 2 && validTwo.has(w)) {
cleanWordSet.add(w);
} else if (w.length >= 3 && w.length <= 16 && /^[a-z]+$/.test(w)) {
cleanWordSet.add(w);
}
}
// Commonness tiers for intelligent word quality ranking
const t10 = new Set(wordlistEnglish['english/10'] || []);
const t20 = new Set(wordlistEnglish['english/20'] || []);
const t35 = new Set(wordlistEnglish['english/35'] || []);
const t50 = new Set(wordlistEnglish['english/50'] || []);
function getCommonnessScore(word) {
if (t10.has(word)) return 4;
if (t20.has(word)) return 3;
if (t35.has(word)) return 2;
if (t50.has(word)) return 1;
return 0;
}
// Clean dictionary incl. regional spellings + proper-word allowlist
// (same source as src/data/lexicon.js)
const cleanWordSet = buildWordSet();
// Build prefix set for super fast solver pruning (up to 6 letters)
const prefixSet = new Set();
@@ -117,13 +89,8 @@ export function solveBoard(quartets, maxWords = 30) {
return a.word.localeCompare(b.word);
});
// Cap words at maxWords (default: 30). Target quartiles and single-tile words
// (tiles that are words on their own) always keep their spot.
if (maxWords && words.length > maxWords) {
const reserved = words.filter(w => w.isTargetQuartile || w.tileCount === 1);
const rest = words.filter(w => !w.isTargetQuartile && w.tileCount !== 1);
words = [...reserved, ...rest.slice(0, Math.max(0, maxWords - reserved.length))];
}
// Cap words at maxWords (default: 30) while keeping a fair share of 2-tile words.
words = capWordList(words, maxWords);
// Final display sorting: Target quartiles first, then points desc, then alphabetical
words.sort((a, b) => {