Files

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