fix(board): scramble tiles on initial deal to avoid pre-formed target words
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user