word-waltz / files / scripts / build-word-list.js
build-word-list.js
4e18328c 2026-03-26 Pavel Safronov | 1 | #!/usr/bin/env node |
| 2 | // Builds src/utils/wordList.ts from filtered-wordle-words.csv (answers) | |
| 3 | // and src/utils/validGuesses.ts from the word-list npm package. | |
| 4 | import { readFileSync, writeFileSync } from 'fs'; | |
| 5 | import { resolve, dirname } from 'path'; | |
| 6 | import { fileURLToPath } from 'url'; | |
| 7 | import wordListPath from 'word-list'; | |
| 8 | ||
| 9 | const __dirname = dirname(fileURLToPath(import.meta.url)); | |
| 10 | const root = resolve(__dirname, '..'); | |
| 11 | ||
| 12 | // --- 1. Generate validGuesses.ts from word-list package --- | |
| 13 | const allWords = readFileSync(wordListPath, 'utf8').split('\n'); | |
| 14 | const guesses = allWords.filter(w => /^[a-z]{5}$/.test(w)); | |
| 15 | writeFileSync( | |
| 16 | resolve(root, 'src/utils/validGuesses.ts'), | |
| 17 | [ | |
| 18 | '// cspell:disable', | |
| 19 | '// AUTO-GENERATED — do not edit. Run: node scripts/build-word-list.js', | |
| 20 | `export const VALID_GUESSES = new Set<string>(${JSON.stringify(guesses)});`, | |
| 21 | '', | |
| 22 | ].join('\n'), | |
| 23 | 'utf8', | |
| 24 | ); | |
| 25 | console.log(`validGuesses.ts: ${guesses.length} words`); | |
| 26 | ||
| 27 | // --- 2. Generate wordList.ts from CSV answers list --- | |
| 28 | const answers = readFileSync(resolve(root, 'filtered-wordle-words.csv'), 'utf8') | |
| 29 | .split('\n') | |
| 30 | .map(w => w.trim().toLowerCase()) | |
| 31 | .filter(w => /^[a-z]{5}$/.test(w)) | |
| 32 | .sort(); | |
| 33 | ||
| 34 | const answerLines = answers.map(w => ` "${w}",`).join('\n'); | |
| 35 | const wordListTs = `// cspell:disable | |
| 36 | import { VALID_GUESSES } from './validGuesses.js'; | |
| 37 | ||
| 38 | // The ~2300 common words Wordle picks from. | |
| 39 | // Used for WordPicker validation (challenger) and getRandomWord() in solo mode. | |
| 40 | const ANSWER_WORDS: string[] = [ | |
| 41 | ${answerLines} | |
| 42 | ]; | |
| 43 | ||
| 44 | const ANSWER_SET = new Set<string>(ANSWER_WORDS); | |
| 45 | ||
| 46 | /** Returns true if the word is a valid guess (full dictionary — answers + extended list). */ | |
| 47 | export function isValidWord(word: string): boolean { | |
| 48 | const w = word.toLowerCase(); | |
| 49 | return w.length === 5 && (ANSWER_SET.has(w) || VALID_GUESSES.has(w)); | |
| 50 | } | |
| 51 | ||
| 52 | /** Returns true if the word can be used as a challenge (answers list only). */ | |
| 53 | export function isValidAnswer(word: string): boolean { | |
| 54 | return word.length === 5 && ANSWER_SET.has(word.toLowerCase()); | |
| 55 | } | |
| 56 | ||
| 57 | /** Returns a random word from the answers list, upper-cased. */ | |
| 58 | export function getRandomWord(): string { | |
| 59 | return ANSWER_WORDS[Math.floor(Math.random() * ANSWER_WORDS.length)].toUpperCase(); | |
| 60 | } | |
| 61 | `; | |
| 62 | ||
| 63 | writeFileSync(resolve(root, 'src/utils/wordList.ts'), wordListTs, 'utf8'); | |
| 64 | console.log(`wordList.ts: ${answers.length} answer words`); |