57 lines
1.5 KiB
JavaScript
57 lines
1.5 KiB
JavaScript
// Word masking and hint helpers for Skribbl mode.
|
|
|
|
function normalize(s) {
|
|
return String(s || "")
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/\s+/g, " ");
|
|
}
|
|
|
|
function maskWord(word, revealed = new Set()) {
|
|
// produce a mask like "_ _ _ _" preserving spaces
|
|
return word
|
|
.split("")
|
|
.map((ch, i) => {
|
|
if (ch === " ") return " ";
|
|
if (revealed.has(i)) return ch.toLowerCase();
|
|
return "_";
|
|
})
|
|
.join("");
|
|
}
|
|
|
|
function pickRevealIndex(word, revealed) {
|
|
const candidates = [];
|
|
for (let i = 0; i < word.length; i++) {
|
|
if (word[i] === " ") continue;
|
|
if (revealed.has(i)) continue;
|
|
candidates.push(i);
|
|
}
|
|
if (candidates.length === 0) return null;
|
|
return candidates[Math.floor(Math.random() * candidates.length)];
|
|
}
|
|
|
|
function isCloseGuess(guess, word) {
|
|
const g = normalize(guess);
|
|
const w = normalize(word);
|
|
if (!g || !w || g === w) return false;
|
|
if (Math.abs(g.length - w.length) > 2) return false;
|
|
// simple Levenshtein
|
|
const m = g.length, n = w.length;
|
|
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
for (let i = 1; i <= m; i++) {
|
|
for (let j = 1; j <= n; j++) {
|
|
const cost = g[i - 1] === w[j - 1] ? 0 : 1;
|
|
dp[i][j] = Math.min(
|
|
dp[i - 1][j] + 1,
|
|
dp[i][j - 1] + 1,
|
|
dp[i - 1][j - 1] + cost
|
|
);
|
|
}
|
|
}
|
|
return dp[m][n] <= 2;
|
|
}
|
|
|
|
module.exports = { normalize, maskWord, pickRevealIndex, isCloseGuess };
|