-
-
Save ncalm/23cca0938b472d6309d139754afa4670 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
solver=LAMBDA(grid, | |
LET( | |
numbers, SEQUENCE(9), | |
numgrid, SEQUENCE(9,9,0), | |
vgrid, TOCOL(grid*1), | |
pos, XMATCH(0,vgrid)-1, | |
IF( | |
ISNA(pos), grid, | |
LET( | |
i,INT(pos/9), | |
j,MOD(pos,9), | |
row,INDEX(grid,i+1,), | |
col,INDEX(grid,,j+1), | |
sqr,INDEX(grid,FLOOR(i,3)+{1;2;3},FLOOR(j,3)+{1,2,3}), | |
candidates,UNIQUE(VSTACK(numbers,col,TOCOL(row),TOCOL(sqr)),,1), | |
test(candidates,LAMBDA(candidate,solver(IF(numgrid=pos,candidate,grid)))) | |
) | |
) | |
) | |
); | |
test=LAMBDA(candidates,function, | |
IF( | |
COUNT(candidates), | |
IFNA(function(@candidates), | |
test(DROP(candidates,1),function) | |
), | |
NA() | |
) | |
) | |
//example: | |
// =solver({ | |
// 0,0,0,0,0,0,0,9,0; | |
// 0,9,7,0,0,0,4,0,0; | |
// 0,0,8,0,6,0,0,7,0; | |
// 0,0,0,9,8,7,0,0,0; | |
// 0,0,0,0,0,4,0,0,1; | |
// 0,0,0,0,0,6,0,2,4; | |
// 2,0,0,0,0,0,5,0,3; | |
// 0,4,0,0,5,0,0,0,0; | |
// 6,0,0,8,0,0,0,0,0}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// --- Workbook module --- | |
// A file of name definitions of the form: | |
// name = definition; | |
//ref: https://codereview.stackexchange.com/q/199771 | |
solver=LAMBDA(grid, | |
LET( | |
// These are the numbers found in a Sudoku puzzle. 1-9 | |
numbers, SEQUENCE(9), | |
// A row-major index of each position in the grid | |
numgrid, SEQUENCE(9,9,0), | |
// The grid represented as a single column | |
vgrid, TOCOL(grid*1), | |
// Finds the position of the first available 0 (the number at this position hasn't yet been solved) | |
pos, XMATCH(0,vgrid)-1, | |
IF( | |
/* If pos is NA, there are no more zeroes available in vgrid, and so none in grid | |
which means the puzzle has been solved, and we just return whatever the grid happens to be | |
*/ | |
ISNA(pos), grid, | |
// Otherwise, we found a zero, and the puzzle isn't solved yet... | |
LET( | |
// the grid column index of pos | |
i,INT(pos/9), | |
// the grid row index of pos | |
j,MOD(pos,9), | |
// The row containing pos | |
row,INDEX(grid,i+1,), | |
// The column containing post | |
col,INDEX(grid,,j+1), | |
// The 9x9 square containing pos | |
sqr,INDEX(grid,FLOOR(i,3)+{1;2;3},FLOOR(j,3)+{1,2,3}), | |
/* The unique values not present either in the row, column or square | |
The number we're trying to solve at position pos has to be one of these. | |
The key to making this work is the last argument of UNIQUE - [exactly_once]. | |
Because numbers contains each of the numbers 1 through 9, when we stack the row, | |
the col and the square with numbers, some of those numbers now appear more than once | |
Those duplicated numbers are filtered out by the [exactly_once] argument. | |
Leaving only those numbers that appear once - and only in numbers, i.e. not in any of | |
the row, column or square. These are then the candidate numbers for the current grid position! | |
*/ | |
candidates,UNIQUE(VSTACK(numbers,col,TOCOL(row),TOCOL(sqr)),,1), | |
/* The first argument to the 'test' function is the list of candidates for the current position | |
identified above. | |
The second argument is a function that will take one of those candidates and use the solver | |
function to test if it is a viable candidate for that position. The IF part places the candidate | |
at position 'pos' in the grid and makes the assumption that it's there, and continues to try | |
to solve the rest of the puzzle. If the puzzle solves, then the assumption was valid, and the | |
candidate can stay at that position. If the puzzle doesn't solve, the puzzle won't solve | |
and the test function will move to the next candidate in the list | |
*/ | |
test(candidates,LAMBDA(candidate,solver(IF(numgrid=pos,candidate,grid)))) | |
) | |
) | |
) | |
); | |
test=LAMBDA(candidates,function, | |
IF( | |
COUNT(candidates), | |
IFNA(function(@candidates), | |
test(DROP(candidates,1),function) | |
), | |
NA() | |
) | |
) | |
//example: | |
// =solver({ | |
// 0,0,0,0,0,0,0,9,0; | |
// 0,9,7,0,0,0,4,0,0; | |
// 0,0,8,0,6,0,0,7,0; | |
// 0,0,0,9,8,7,0,0,0; | |
// 0,0,0,0,0,4,0,0,1; | |
// 0,0,0,0,0,6,0,2,4; | |
// 2,0,0,0,0,0,5,0,3; | |
// 0,4,0,0,5,0,0,0,0; | |
// 6,0,0,8,0,0,0,0,0}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment