sudoku-solve
Version:
Konni's Sudoku solving library
159 lines (158 loc) • 5.97 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sudoku = void 0;
const Changes_1 = require("./Changes");
const CommonFunctions_1 = require("./CommonFunctions");
const Group_1 = require("./Group");
const Rule_1 = require("./Rule");
const SudokuNumber_1 = require("./SudokuNumber");
class Sudoku {
constructor(numbers) {
this.numbers = numbers.map((n, i) => {
return new SudokuNumber_1.SudokuNumber(CommonFunctions_1.row(i), CommonFunctions_1.col(i), n);
});
this.groups = [];
for (let i = 0; i < 9; i++) {
this.groups.push(Group_1.Group.group(Group_1.GroupType.ROW, i, this.numbers));
this.groups.push(Group_1.Group.group(Group_1.GroupType.COL, i, this.numbers));
this.groups.push(Group_1.Group.group(Group_1.GroupType.BOX, i, this.numbers));
}
}
get(row, col) {
const foundNumber = this.numbers.find((n) => n.row === row && n.col === col);
if (!foundNumber) {
throw new Error(`there is no number at ${row}, ${col}`);
}
return foundNumber;
}
copy() {
return new Sudoku(this.numbers.map((n) => (n.isSolved() ? n.get() : 0)));
}
allImmediatelyPossibleSteps() {
const copy = this.copy();
const changes = copy.groups
.map((g) => Rule_1.Rule.elimination(g))
.reduce((a1, a2) => a1.concat(a2))
.concat(copy.groups
.map((g) => Rule_1.Rule.onlyPossiblePosition(g))
.reduce((a1, a2) => a1.concat(a2)))
.concat(copy.groups
.map((g) => Rule_1.Rule.twins(g))
.reduce((a1, a2) => a1.concat(a2)));
changes.concat(copy.groups
.map((g) => Rule_1.Rule.elimination(g))
.reduce((a1, a2) => a1.concat(a2))
.concat(copy.groups
.map((g) => Rule_1.Rule.onlyPossiblePosition(g))
.reduce((a1, a2) => a1.concat(a2)))
.concat(copy.groups
.map((g) => Rule_1.Rule.twins(g))
.reduce((a1, a2) => a1.concat(a2))));
return changes;
}
allStepsUntilSolution() {
const copy = this.copy();
let changes = new Changes_1.Changes([]);
let newChanges = new Changes_1.Changes([]);
do {
newChanges = copy.groups
.map((g) => Rule_1.Rule.elimination(g))
.reduce((a1, a2) => a1.concat(a2));
newChanges = newChanges.concat(copy.groups
.map((g) => Rule_1.Rule.onlyPossiblePosition(g))
.reduce((a1, a2) => a1.concat(a2)));
newChanges = newChanges.concat(copy.groups
.map((g) => Rule_1.Rule.twins(g))
.reduce((a1, a2) => a1.concat(a2)));
changes = changes.concat(newChanges);
} while (copy.unsolvedCount() > 0 && newChanges.changes.length > 0);
return changes;
}
apply(change) {
if (change.isNone)
return;
if (change.hasSolvedNumber()) {
const solvedNumber = change.solvedNumber;
const number = this.numbers.find((n) => n.row === solvedNumber.row && n.col === solvedNumber.col);
if (number === null || number === void 0 ? void 0 : number.isPossible(solvedNumber.value)) {
number.solve(solvedNumber.rule, solvedNumber.value);
}
else {
throw new Error(`Cannot apply SolvedNumber to ${number}`);
}
}
else if (change.hasRemovedNumbers()) {
const removedNumbers = change.removedNumbers;
const number = this.numbers.find((n) => n.row === removedNumbers.row && n.col === removedNumbers.col);
number === null || number === void 0 ? void 0 : number.remove(removedNumbers.numbers, removedNumbers.rule);
}
}
solve() {
this.allStepsUntilSolution().changes.forEach(change => this.apply(change));
}
solvedCount() {
return this.numbers.filter(n => n.isSolved()).length;
}
unsolvedCount() {
return 81 - this.solvedCount();
}
isSolved() {
return this.solvedCount() === 81;
}
canBeSolved() {
return this.isCorrect() && this.solvedCount() + this.allStepsUntilSolution().solvedNumbers().length === 81;
}
isCorrect() {
return this.groups.every(g => g.isCorrect())
&& this.numbers.every(n => n.isImpossible() === false);
}
incorrectGroups() {
if (this.isCorrect()) {
throw new Error(`This Sudoku is correct!`);
}
return this.groups.filter(g => !g.isCorrect());
}
toString() {
const numbers = this.numbers.map(n => n.toSimpleString());
let text = `---------------------\n${numbers[0]}`;
for (let i = 1; i < numbers.length; i++) {
if (i % 27 === 0) {
text += "\n---------------------\n";
}
else if (i % 9 === 0) {
text += "\n";
}
else if (i % 3 === 0) {
text += " | ";
}
else {
text += " ";
}
text += numbers[i];
}
text += "\n---------------------";
return text;
}
toDetailedString() {
const numbers = this.numbers.map(n => n.toDetailedString());
let text = `---------------------\n${numbers[0]}`;
for (let i = 1; i < numbers.length; i++) {
if (i % 27 === 0) {
text += "\n---------------------\n";
}
else if (i % 9 === 0) {
text += "\n";
}
else if (i % 3 === 0) {
text += " | ";
}
else {
text += " ";
}
text += numbers[i];
}
text += "\n---------------------";
return text;
}
}
exports.Sudoku = Sudoku;