fix(board): scramble tiles on initial deal to avoid pre-formed target words

This commit is contained in:
2026-10-23 22:47:54 -04:00
parent 04df4b4574
commit 3cdde460f3

View File

@@ -63,7 +63,12 @@ class GameStateManager {
}));
// Restore saved state from LocalStorage if available
this.restoreProgress();
const hasSavedTileOrder = this.restoreProgress();
// If fresh puzzle without saved order, scramble the board so target words are not formed!
if (!hasSavedTileOrder) {
this.scrambleBoard(puzzle.id || String(Date.now()));
}
// Organize board tiles according to lock state
this.reorganizeBoard();
@@ -71,6 +76,28 @@ class GameStateManager {
this.notify();
}
// Seeded Fisher-Yates scramble for initial board dealing
scrambleBoard(seed = '') {
let h = 0x811c9dc5;
for (let i = 0; i < seed.length; i++) {
h ^= seed.charCodeAt(i);
h = Math.imul(h, 0x01000193);
}
let s = (h >>> 0) || 123456789;
const nextRandom = () => {
s = (Math.imul(1664525, s) + 1013904223) >>> 0;
return s / 4294967296;
};
const shuffled = [...this.boardTiles];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(nextRandom() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
this.boardTiles = shuffled;
this.saveProgress();
}
// Get current date string in YYYY-MM-DD
getTodayDateString() {
const today = new Date();
@@ -99,7 +126,7 @@ class GameStateManager {
// Restore puzzle progress
restoreProgress() {
if (!this.currentPuzzle) return;
if (!this.currentPuzzle) return false;
const saveKey = `quartiles_save_${this.currentPuzzle.id}`;
try {
const saved = localStorage.getItem(saveKey);
@@ -115,10 +142,17 @@ class GameStateManager {
// Restore tile order if saved
if (Array.isArray(parsed.tileOrder) && parsed.tileOrder.length === 20) {
// Check if saved order was the trivial unscrambled [0..19] order
const isSequential = parsed.tileOrder.every((id, idx) => id === idx);
if (isSequential && this.foundWords.size === 0) {
return false;
}
const tileMap = new Map(this.boardTiles.map(t => [t.id, t]));
const reordered = parsed.tileOrder.map(id => tileMap.get(id)).filter(Boolean);
if (reordered.length === 20) {
this.boardTiles = reordered;
return true;
}
}
}
@@ -126,6 +160,7 @@ class GameStateManager {
} catch (err) {
console.warn('Failed to restore progress:', err);
}
return false;
}
// Check if a tile belongs to an already found Quartile