UNPKG

jassl

Version:

Just Another Sudoku Solving Library

108 lines (83 loc) 3.5 kB
const {isUINT} = require('./misc/util.js'); module.exports = class SudokuGroup { constructor(type, sudoku, index, cells) { if (type !== 'row' && type !== 'column' && type !== 'sector') { throw new Error('invalid group type'); } if (!sudoku.groupLength) { throw new Error('sudoku missing group length; unable to continue'); } if (cells.length !== sudoku.groupLength) { throw new Error('invalid list of cells for group'); } this.sudoku = sudoku; this.index = index; this.cells = cells; this.candidates = Array(sudoku.groupLength).fill().map((value, index) => index + 1); this.solved = false; // loop over each cell that is a part of the group and add a reference to the group on the cell cells.forEach(cell => cell[type] = this); } exclude(candidate) { // Validate candidate if (!isUINT(candidate, {min: 1, max: this.sudoku.groupLength})) { throw new Error('invalid candidate'); } // all cells in group have been solved if (this.solved) { return false; } // attempt to find the index of the candidate in the group's candidates list let idx = this.candidates.indexOf(candidate); // candidate not found if (idx === -1) { return false; } // exclude the candidate from all applicable cells in the group this.cells.forEach(cell => cell.exclude(candidate)); // remove the candidate from the group's candidate list this.candidates.splice(idx, 1); // check if group has been solved; if so, update solved property if (this.candidates.length === 0) { this.solved = true; } // return true indicating the puzzle updated return true; } excludeNakedSubsets() { if (this.solved) { return false; } // get a list of unsolved cells with more than 1 candidate let subsets = {}, cells = this.cells.filter(cell => !cell.solved && cell.candidates.length > 1); // build list of candidate subsets and cells belonging to the subset cells.forEach(cell => { let key = cell.candidates.join('-'); if (subsets[key]) { subsets[key].push(cell); } else { subsets[key] = [cell]; } }); // Loop over each subset // returns true if any cell was updated, false otherwise return Object.keys(subsets).reduce((updated, key) => { // Naked subsets should have same number of cells as candidates for the cells if (subsets[key].length !== key.split('-').length) { return updated; } // Get the list of candidates belonging to the subset let candidates = subsets[key][0].candidates; // loop over each cell in the group return cells.reduce((updated, cell) => { // ignore cells that are part of the subset if (cell.candidates.join('-') === key) { return updated; } // Remove candidates from the cell that are part of the subset return candidates.reduce((updated, candidate) => cell.exclude(candidate) || updated, updated); }, updated); }, false); } };