55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
/**
|
||
* Generate a fully valid Sudoku solution using a base pattern and structural shuffles.
|
||
* Returns a string of 81 digits (row-major order).
|
||
*/
|
||
export function generateSolvedBoard(): string {
|
||
const size = 9;
|
||
const box = 3;
|
||
|
||
// --- 1️⃣ Base pattern function ---
|
||
const pattern = (r: number, c: number) =>
|
||
(box * (r % box) + Math.floor(r / box) + c) % size;
|
||
|
||
// --- 2️⃣ Create base board ---
|
||
const baseBoard: number[][] = Array.from({ length: size }, (_, r) =>
|
||
Array.from({ length: size }, (_, c) => pattern(r, c) + 1)
|
||
);
|
||
|
||
// --- 3️⃣ Helper to shuffle an array in place ---
|
||
const shuffle = <T>(arr: T[]): T[] => {
|
||
const copy = [...arr];
|
||
for (let i = copy.length - 1; i > 0; i--) {
|
||
const j = Math.floor(Math.random() * (i + 1));
|
||
[copy[i], copy[j]] = [copy[j], copy[i]];
|
||
}
|
||
return copy;
|
||
};
|
||
|
||
// --- 4️⃣ Build shuffled row/col indices preserving 3×3 structure ---
|
||
const rows = shuffle([0, 1, 2])
|
||
.concat(shuffle([3, 4, 5]))
|
||
.concat(shuffle([6, 7, 8]));
|
||
const cols = shuffle([0, 1, 2])
|
||
.concat(shuffle([3, 4, 5]))
|
||
.concat(shuffle([6, 7, 8]));
|
||
|
||
// --- 5️⃣ Shuffle entire bands and stacks (groups of 3) ---
|
||
const rowBands = shuffle([0, 1, 2]);
|
||
const colStacks = shuffle([0, 1, 2]);
|
||
|
||
const shuffledRows = rowBands.flatMap(band => rows.slice(band * 3, band * 3 + 3));
|
||
const shuffledCols = colStacks.flatMap(stack => cols.slice(stack * 3, stack * 3 + 3));
|
||
|
||
// --- 6️⃣ Digit permutation (1–9) ---
|
||
const digits = shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||
|
||
// --- 7️⃣ Build the final shuffled board ---
|
||
const board: number[][] = shuffledRows.map(r =>
|
||
shuffledCols.map(c => digits[baseBoard[r][c] - 1])
|
||
);
|
||
|
||
// --- 8️⃣ Flatten to 81-char string ---
|
||
return board.flat().join('');
|
||
}
|
||
|