1
0

Added deduction logic checks (if there's a value only in one row/col in a square, it can't be in the rest of the row/col)

This commit is contained in:
Markus Birth 2014-01-26 15:20:20 +01:00
parent 1afd7e739e
commit ab4a6e96e2
5 changed files with 286 additions and 4 deletions

View File

@ -49,7 +49,7 @@ class @SudokuChecks
@twoValPlaces: (cells) ->
return if not cells[0]
dim2 = cells[0].boardObj.dim2
console.group('twoValPlaces: (%o) %o', dim2, cells)
console.groupCollapsed('twoValPlaces: (%o) %o', dim2, cells)
for i in [0...dim2-1] by 1 # walk all possible 2s combinations
for j in [i+1...dim2] by 1
n = 0
@ -73,3 +73,27 @@ class @SudokuChecks
cells[k].setMask(newMask)
console.groupEnd()
return true
@rowMatch: (blockSubject, blockRest, lineRest) ->
# aaabbbccc ... if bbb is only possibility for one num (check rest of block!), remove from aaa and ccc
return if not blockSubject[0] or not blockRest[0] or not lineRest[0]
console.groupCollapsed('rowMatch: %o, %o, %o', blockSubject, blockRest, lineRest)
for i in [0...blockSubject[0].boardObj.dim2] by 1 # walk all possible values
p = (1 << i)
isInSubject = false
isInRest = false
# test if number is in subject
for j of blockSubject
if blockSubject[j].getValue() is '.' and (p & blockSubject[j].getMask())
isInSubject = true
break
# test if number is in rest of square
for j of blockRest
if blockRest[j].getValue() is '.' and (p & blockRest[j].getMask())
isInRest = true
break
# if only in subject, remove candidate from rest
if isInSubject and not isInRest
for j of lineRest
lineRest[j].setMask(lineRest[j].getMask() & ~p)
console.groupEnd()

View File

@ -31,6 +31,60 @@ class @SudokuSolver
result.push(board.cellAt(i, col))
return result
@getSquareColCells: (board, squareid, squarecol) ->
result = []
rb = Math.floor(squareid / board.dim) * board.dim # base row for square
cx = squareid % board.dim * board.dim + squarecol # x for column
for i in [0...board.dim] by 1
result.push(board.cellAt(cx, rb + i))
return result
@getSquareNonColCells: (board, squareid, squarecol) ->
result = []
rb = Math.floor(squareid / board.dim) * board.dim # base row for square
cx = squareid % board.dim * board.dim # base x for column
for i in [0...board.dim] by 1
continue if i is squarecol
for j in [0...board.dim] by 1
result.push(board.cellAt(cx + i, rb + j))
return result
@getNonSquareColCells: (board, squareid, squarecol) ->
result = []
rb = Math.floor(squareid / board.dim) * board.dim # base row for square
cx = squareid % board.dim * board.dim + squarecol # x for column
for i in [0...board.dim2] by 1
continue if i in [rb...rb+board.dim]
result.push(board.cellAt(cx, i))
return result
@getSquareRowCells: (board, squareid, squarerow) ->
result = []
cb = squareid % board.dim * board.dim # base col for square
ry = Math.floor(squareid / board.dim) * board.dim + squarerow # y for row
for i in [0...board.dim] by 1
result.push(board.cellAt(cb + i, ry))
return result
@getSquareNonRowCells: (board, squareid, squarerow) ->
result = []
cb = squareid % board.dim * board.dim # base column for square
ry = Math.floor(squareid / board.dim) * board.dim # base y for row
for i in [0...board.dim] by 1
continue if i is squarerow
for j in [0...board.dim] by 1
result.push(board.cellAt(cb + j, ry + i))
return result
@getNonSquareRowCells: (board, squareid, squarerow) ->
result = []
cb = squareid % board.dim * board.dim # base col for square
ry = Math.floor(squareid / board.dim) * board.dim + squarerow # y for row
for i in [0...board.dim2] by 1
continue if i in [cb...cb+board.dim]
result.push(board.cellAt(i, ry))
return result
@runAllRows: (board, func) ->
for i in [0...board.dim2] by 1 # walk all rows
rowcells = @getRowCells(board, i)
@ -56,6 +110,28 @@ class @SudokuSolver
SudokuChecks[func](diag1)
SudokuChecks[func](diag2)
@runSpecialColumns: (board, func) ->
blockCol = []
blockRest = []
colRest = []
for s in [0...board.dim2] by 1 # walk all squares
for c in [0...board.dim] by 1 # walk all square columns
blockCol = @getSquareColCells(board, s, c)
blockRest = @getSquareNonColCells(board, s, c)
colRest = @getNonSquareColCells(board, s, c)
SudokuChecks[func](blockCol, blockRest, colRest)
@runSpecialRows: (board, func) ->
blockRow = []
blockRest = []
rowRest = []
for s in [0...board.dim2] by 1 # walk all squares
for r in [0...board.dim] by 1 # walk all square rows
blockRow = @getSquareRowCells(board, s, r)
blockRest = @getSquareNonRowCells(board, s, r)
rowRest = @getNonSquareRowCells(board, s, r)
SudokuChecks[func](blockRow, blockRest, rowRest)
@oneUnknownAll: (board) ->
cells = []
for r in [0...board.dim2] by 1
@ -71,6 +147,8 @@ class @SudokuSolver
@twoValPlacesRow: (board) -> @runAllRows(board, 'twoValPlaces')
@twoValPlacesColumn: (board) -> @runAllColumns(board, 'twoValPlaces')
@twoValPlacesDiag: (board) -> @runBothDiags(board, 'twoValPlaces')
@oneColumnForValue: (board) -> @runSpecialColumns(board, 'rowMatch')
@oneRowForValue: (board) -> @runSpecialRows(board, 'rowMatch')
@solveBoard: (board) ->
checks =
@ -84,6 +162,8 @@ class @SudokuSolver
'twoValPlacesColumn': 'Only two possible places for pair in column.'
'twoValPlacesDiag': 'Only two possible places for pair in diagonale.'
'oneUnknownAll': 'Only one possible value left.'
'oneColumnForValue': 'Only one possible column for value.'
'oneRowForValue': 'Only one possible row for value.'
i = 1
while true

View File

@ -20,9 +20,10 @@
//board.loadString('69..2...............81...32..9......3....4.76.6...3.....5....4.....8..........5..', true);
//board.loadString('.2.....8.3..6.2..5...1.9....74...31...........68...47....5.8...6..7.3..9.3.....4.', false);
//board.loadString('7...54..949..67.1....192347....71.9....63........48.3.35.....716487139...7......3', false);
board.loadString('..3.........4.....98.23.....2.91.8..5.....1.....3.67...4...7.....6...2.91.2.....6', false);
board.loadString('..3.........4.....98.23.....2.91.8..5.....1.....3.67...4...7.....6...2.91.2.....6', false); // HARD
//board.loadString('9.8.6.15...5.1...8.1.2.....8..............349.4.9....71......8..6..9........5..6.', false);
//board.loadString('.......14..92..6.3.4.....9.2..3.4.....579......8162.3....98..2.5.....4...1...3...', false);
//board.loadString('....1..........47576...9.1..34..26..8..9.3..4..68..12..9.4...61147..........2....', false); // HARD
document.write('Base board:<br />');
board.print();
</script>

View File

@ -75,7 +75,7 @@
return;
}
dim2 = cells[0].boardObj.dim2;
console.group('twoValPlaces: (%o) %o', dim2, cells);
console.groupCollapsed('twoValPlaces: (%o) %o', dim2, cells);
for (i = _i = 0, _ref = dim2 - 1; _i < _ref; i = _i += 1) {
for (j = _j = _ref1 = i + 1; _j < dim2; j = _j += 1) {
n = 0;
@ -110,6 +110,37 @@
return true;
};
SudokuChecks.rowMatch = function(blockSubject, blockRest, lineRest) {
var i, isInRest, isInSubject, j, p, _i, _ref;
if (!blockSubject[0] || !blockRest[0] || !lineRest[0]) {
return;
}
console.groupCollapsed('rowMatch: %o, %o, %o', blockSubject, blockRest, lineRest);
for (i = _i = 0, _ref = blockSubject[0].boardObj.dim2; _i < _ref; i = _i += 1) {
p = 1 << i;
isInSubject = false;
isInRest = false;
for (j in blockSubject) {
if (blockSubject[j].getValue() === '.' && (p & blockSubject[j].getMask())) {
isInSubject = true;
break;
}
}
for (j in blockRest) {
if (blockRest[j].getValue() === '.' && (p & blockRest[j].getMask())) {
isInRest = true;
break;
}
}
if (isInSubject && !isInRest) {
for (j in lineRest) {
lineRest[j].setMask(lineRest[j].getMask() & ~p);
}
}
}
return console.groupEnd();
};
return SudokuChecks;
})();

View File

@ -1,5 +1,7 @@
// Generated by CoffeeScript 1.6.3
(function() {
var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
this.SudokuSolver = (function() {
function SudokuSolver() {}
@ -50,6 +52,96 @@
return result;
};
SudokuSolver.getSquareColCells = function(board, squareid, squarecol) {
var cx, i, rb, result, _i, _ref;
result = [];
rb = Math.floor(squareid / board.dim) * board.dim;
cx = squareid % board.dim * board.dim + squarecol;
for (i = _i = 0, _ref = board.dim; _i < _ref; i = _i += 1) {
result.push(board.cellAt(cx, rb + i));
}
return result;
};
SudokuSolver.getSquareNonColCells = function(board, squareid, squarecol) {
var cx, i, j, rb, result, _i, _j, _ref, _ref1;
result = [];
rb = Math.floor(squareid / board.dim) * board.dim;
cx = squareid % board.dim * board.dim;
for (i = _i = 0, _ref = board.dim; _i < _ref; i = _i += 1) {
if (i === squarecol) {
continue;
}
for (j = _j = 0, _ref1 = board.dim; _j < _ref1; j = _j += 1) {
result.push(board.cellAt(cx + i, rb + j));
}
}
return result;
};
SudokuSolver.getNonSquareColCells = function(board, squareid, squarecol) {
var cx, i, rb, result, _i, _j, _ref, _ref1, _results;
result = [];
rb = Math.floor(squareid / board.dim) * board.dim;
cx = squareid % board.dim * board.dim + squarecol;
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
if (__indexOf.call((function() {
_results = [];
for (var _j = rb, _ref1 = rb + board.dim; rb <= _ref1 ? _j < _ref1 : _j > _ref1; rb <= _ref1 ? _j++ : _j--){ _results.push(_j); }
return _results;
}).apply(this), i) >= 0) {
continue;
}
result.push(board.cellAt(cx, i));
}
return result;
};
SudokuSolver.getSquareRowCells = function(board, squareid, squarerow) {
var cb, i, result, ry, _i, _ref;
result = [];
cb = squareid % board.dim * board.dim;
ry = Math.floor(squareid / board.dim) * board.dim + squarerow;
for (i = _i = 0, _ref = board.dim; _i < _ref; i = _i += 1) {
result.push(board.cellAt(cb + i, ry));
}
return result;
};
SudokuSolver.getSquareNonRowCells = function(board, squareid, squarerow) {
var cb, i, j, result, ry, _i, _j, _ref, _ref1;
result = [];
cb = squareid % board.dim * board.dim;
ry = Math.floor(squareid / board.dim) * board.dim;
for (i = _i = 0, _ref = board.dim; _i < _ref; i = _i += 1) {
if (i === squarerow) {
continue;
}
for (j = _j = 0, _ref1 = board.dim; _j < _ref1; j = _j += 1) {
result.push(board.cellAt(cb + j, ry + i));
}
}
return result;
};
SudokuSolver.getNonSquareRowCells = function(board, squareid, squarerow) {
var cb, i, result, ry, _i, _j, _ref, _ref1, _results;
result = [];
cb = squareid % board.dim * board.dim;
ry = Math.floor(squareid / board.dim) * board.dim + squarerow;
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
if (__indexOf.call((function() {
_results = [];
for (var _j = cb, _ref1 = cb + board.dim; cb <= _ref1 ? _j < _ref1 : _j > _ref1; cb <= _ref1 ? _j++ : _j--){ _results.push(_j); }
return _results;
}).apply(this), i) >= 0) {
continue;
}
result.push(board.cellAt(i, ry));
}
return result;
};
SudokuSolver.runAllRows = function(board, func) {
var i, rowcells, _i, _ref, _results;
_results = [];
@ -95,6 +187,50 @@
return SudokuChecks[func](diag2);
};
SudokuSolver.runSpecialColumns = function(board, func) {
var blockCol, blockRest, c, colRest, s, _i, _ref, _results;
blockCol = [];
blockRest = [];
colRest = [];
_results = [];
for (s = _i = 0, _ref = board.dim2; _i < _ref; s = _i += 1) {
_results.push((function() {
var _j, _ref1, _results1;
_results1 = [];
for (c = _j = 0, _ref1 = board.dim; _j < _ref1; c = _j += 1) {
blockCol = this.getSquareColCells(board, s, c);
blockRest = this.getSquareNonColCells(board, s, c);
colRest = this.getNonSquareColCells(board, s, c);
_results1.push(SudokuChecks[func](blockCol, blockRest, colRest));
}
return _results1;
}).call(this));
}
return _results;
};
SudokuSolver.runSpecialRows = function(board, func) {
var blockRest, blockRow, r, rowRest, s, _i, _ref, _results;
blockRow = [];
blockRest = [];
rowRest = [];
_results = [];
for (s = _i = 0, _ref = board.dim2; _i < _ref; s = _i += 1) {
_results.push((function() {
var _j, _ref1, _results1;
_results1 = [];
for (r = _j = 0, _ref1 = board.dim; _j < _ref1; r = _j += 1) {
blockRow = this.getSquareRowCells(board, s, r);
blockRest = this.getSquareNonRowCells(board, s, r);
rowRest = this.getNonSquareRowCells(board, s, r);
_results1.push(SudokuChecks[func](blockRow, blockRest, rowRest));
}
return _results1;
}).call(this));
}
return _results;
};
SudokuSolver.oneUnknownAll = function(board) {
var c, cells, r, _i, _j, _ref, _ref1;
cells = [];
@ -138,6 +274,14 @@
return this.runBothDiags(board, 'twoValPlaces');
};
SudokuSolver.oneColumnForValue = function(board) {
return this.runSpecialColumns(board, 'rowMatch');
};
SudokuSolver.oneRowForValue = function(board) {
return this.runSpecialRows(board, 'rowMatch');
};
SudokuSolver.solveBoard = function(board) {
var body, c, checks, description, i;
checks = {
@ -150,7 +294,9 @@
'twoValPlacesRow': 'Only two possible places for pair in row.',
'twoValPlacesColumn': 'Only two possible places for pair in column.',
'twoValPlacesDiag': 'Only two possible places for pair in diagonale.',
'oneUnknownAll': 'Only one possible value left.'
'oneUnknownAll': 'Only one possible value left.',
'oneColumnForValue': 'Only one possible column for value.',
'oneRowForValue': 'Only one possible row for value.'
};
i = 1;
while (true) {