init
This commit is contained in:
108
scripts/solver.mjs
Normal file
108
scripts/solver.mjs
Normal file
@@ -0,0 +1,108 @@
|
||||
import fs from 'node:fs';
|
||||
import wordlistEnglish from 'wordlist-english';
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Build prefix set for super fast solver pruning (up to 6 letters)
|
||||
const prefixSet = new Set();
|
||||
for (const word of cleanWordSet) {
|
||||
for (let len = 1; len <= Math.min(word.length, 6); len++) {
|
||||
prefixSet.add(word.slice(0, len));
|
||||
}
|
||||
}
|
||||
|
||||
// Function to solve a 20-tile board and return all valid words and paths
|
||||
export function solveBoard(quartets) {
|
||||
const tiles = quartets.flat();
|
||||
const foundMap = new Map();
|
||||
|
||||
function search(prefix, usedIndices) {
|
||||
if (prefix.length > 0) {
|
||||
const checkLen = Math.min(prefix.length, 6);
|
||||
if (!prefixSet.has(prefix.slice(0, checkLen))) return;
|
||||
}
|
||||
|
||||
if (prefix.length >= 2 && cleanWordSet.has(prefix)) {
|
||||
if (!foundMap.has(prefix)) {
|
||||
foundMap.set(prefix, [...usedIndices]);
|
||||
}
|
||||
}
|
||||
if (usedIndices.length >= 4) return;
|
||||
|
||||
for (let i = 0; i < tiles.length; i++) {
|
||||
if (usedIndices.includes(i)) continue;
|
||||
usedIndices.push(i);
|
||||
search(prefix + tiles[i], usedIndices);
|
||||
usedIndices.pop();
|
||||
}
|
||||
}
|
||||
|
||||
search('', []);
|
||||
|
||||
const quartilesList = quartets.map(q => q.join(''));
|
||||
const quartileSet = new Set(quartilesList);
|
||||
|
||||
const words = [];
|
||||
let totalScore = 0;
|
||||
let quartileCount = 0;
|
||||
|
||||
for (const [word, path] of foundMap.entries()) {
|
||||
const tileSegments = path.map(idx => tiles[idx]);
|
||||
const tileCount = path.length;
|
||||
let points = tileCount === 4 ? 8 : tileCount === 3 ? 4 : tileCount;
|
||||
const isTargetQuartile = quartileSet.has(word);
|
||||
const isQuartile = tileCount === 4;
|
||||
|
||||
if (isQuartile) quartileCount++;
|
||||
totalScore += points;
|
||||
|
||||
words.push({
|
||||
word,
|
||||
points,
|
||||
tileCount,
|
||||
tileIndices: path,
|
||||
tileSegments,
|
||||
isTargetQuartile,
|
||||
isQuartile
|
||||
});
|
||||
}
|
||||
|
||||
// Add 40 bonus points for finding all 5 quartiles
|
||||
const maxPossibleScore = totalScore + 40;
|
||||
|
||||
// Sort words: Quartiles first, then by points descending, then alphabetically
|
||||
words.sort((a, b) => {
|
||||
if (a.isTargetQuartile !== b.isTargetQuartile) return a.isTargetQuartile ? -1 : 1;
|
||||
if (b.points !== a.points) return b.points - a.points;
|
||||
return a.word.localeCompare(b.word);
|
||||
});
|
||||
|
||||
return {
|
||||
tiles,
|
||||
quartets,
|
||||
targetQuartiles: quartilesList,
|
||||
words,
|
||||
wordCount: words.length,
|
||||
quartileCount: 5,
|
||||
totalBaseScore: totalScore,
|
||||
maxPossibleScore
|
||||
};
|
||||
}
|
||||
|
||||
console.log('Solver module ready. Clean words count:', cleanWordSet.size);
|
||||
Reference in New Issue
Block a user