Skip to content

Instantly share code, notes, and snippets.

@ncalm
Forked from lhem/sudoku
Last active April 23, 2024 20:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ncalm/23cca0938b472d6309d139754afa4670 to your computer and use it in GitHub Desktop.
Save ncalm/23cca0938b472d6309d139754afa4670 to your computer and use it in GitHub Desktop.
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})
// --- 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