46 lines
1.2 KiB
TypeScript
46 lines
1.2 KiB
TypeScript
/**
|
||
* Create a playable Sudoku puzzle by removing random digits
|
||
* from a fully solved board string.
|
||
*
|
||
* Difficulty only determines how many cells are removed.
|
||
* - "easy": removes ~40 cells
|
||
* - "medium": removes ~50 cells
|
||
* - "hard": removes ~60 cells
|
||
*
|
||
* The returned string uses '.' for blanks.
|
||
*/
|
||
export function makePuzzle(
|
||
solution: string,
|
||
difficulty: "easy" | "medium" | "hard" = "medium"
|
||
): string {
|
||
if (solution.length !== 81 || !/^[1-9]+$/.test(solution)) {
|
||
throw new Error("Invalid solved board: must be 81 digits 1–9");
|
||
}
|
||
|
||
// --- removal count based on difficulty ---
|
||
const removeCount = {
|
||
easy: 40,
|
||
medium: 50,
|
||
hard: 60,
|
||
}[difficulty];
|
||
|
||
// --- create a mutable copy ---
|
||
const board = solution.split("");
|
||
|
||
// --- build a shuffled list of indices 0–80 ---
|
||
const indices = Array.from({ length: 81 }, (_, i) => i);
|
||
for (let i = indices.length - 1; i > 0; i--) {
|
||
const j = Math.floor(Math.random() * (i + 1));
|
||
[indices[i], indices[j]] = [indices[j], indices[i]];
|
||
}
|
||
|
||
// --- remove the desired number of cells ---
|
||
for (let i = 0; i < removeCount; i++) {
|
||
const idx = indices[i];
|
||
board[idx] = ".";
|
||
}
|
||
|
||
return board.join("");
|
||
}
|
||
|