Just another update
This commit is contained in:
parent
a25938f123
commit
00a190b0a6
6591 changed files with 94363 additions and 23227 deletions
|
|
@ -1 +1,2 @@
|
|||
Solve a partially filled-in normal 9x9 [[wp:Sudoku|Sudoku]] grid and display the result in a human-readable format. [[wp:Algorithmics_of_sudoku|Algorithmics of Sudoku]] may help implement this.
|
||||
Solve a partially filled-in normal 9x9 [[wp:Sudoku|Sudoku]] grid and display the result in a human-readable format.
|
||||
[[wp:Algorithmics_of_sudoku|Algorithmics of Sudoku]] may help implement this.
|
||||
|
|
|
|||
|
|
@ -1,41 +1,24 @@
|
|||
(ns sudoku
|
||||
(:use [clojure.contrib.math :only (sqrt)]))
|
||||
(ns rosettacode.sudoku
|
||||
(:use [clojure.pprint :only (cl-format)]))
|
||||
|
||||
(defn print-grid [grid]
|
||||
(doseq [y (range (count grid))]
|
||||
(doseq [x (range (count grid))]
|
||||
(print (retrieve grid x y) " "))
|
||||
(println))
|
||||
(println))
|
||||
(defn- compatible? [m x y n]
|
||||
(let [n= #(= n (get-in m [%1 %2]))]
|
||||
(or (n= y x)
|
||||
(let [c (count m)]
|
||||
(and (zero? (get-in m [y x]))
|
||||
(not-any? #(or (n= y %) (n= % x)) (range c))
|
||||
(let [zx (* c (quot x c)), zy (* c (quot y c))]
|
||||
(every? false?
|
||||
(map n= (range zy (+ zy c)) (range zx (+ zx c))))))))))
|
||||
|
||||
(defn retrieve [grid x y]
|
||||
(get (get grid y) x))
|
||||
|
||||
(defn store [grid x y n]
|
||||
(assoc grid y (assoc (get grid y) x n)))
|
||||
|
||||
(defn coordinates [grid x y]
|
||||
(let [n (sqrt (count grid))
|
||||
zx (* n (quot x n))
|
||||
zy (* n (quot y n))]
|
||||
(for [x (range zx (+ zx n)) y (range zy (+ zy n))]
|
||||
[x y])))
|
||||
|
||||
(defn compatible? [grid x y n]
|
||||
(or
|
||||
(= n (retrieve grid x y))
|
||||
(and
|
||||
(zero? (retrieve grid x y))
|
||||
(every? #(and (not= n (retrieve grid % y)) (not= n (retrieve grid x %))) (range (count grid)))
|
||||
(every? #(not= n (retrieve grid (first %) (second %))) (coordinates grid x y)))))
|
||||
|
||||
(defn solve [grid x y]
|
||||
(let [m (count grid)]
|
||||
(if (= y m)
|
||||
(print-grid grid)
|
||||
(doseq [n (range 1 (inc m))]
|
||||
(when (compatible? grid x y n)
|
||||
(let [new-grid (store grid x y n)]
|
||||
(if (= x (dec m))
|
||||
(solve new-grid 0 (inc y))
|
||||
(solve new-grid (inc x) y))))))))
|
||||
(defn solve [m]
|
||||
(let [c (count m)]
|
||||
(loop [m m, x 0, y 0]
|
||||
(if (= y c) m
|
||||
(let [ng (->> (range 1 c)
|
||||
(filter #(compatible? m x y %))
|
||||
first
|
||||
(assoc-in m [y x]))]
|
||||
(if (= x (dec c))
|
||||
(recur ng 0 (inc y))
|
||||
(recur ng (inc x) y)))))))
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
sudoku> (solve [[3 9 4 0 0 2 6 7 0]
|
||||
[0 0 0 3 0 0 4 0 0]
|
||||
[5 0 0 6 9 0 0 2 0]
|
||||
[0 4 5 0 0 0 9 0 0]
|
||||
[6 0 0 0 0 0 0 0 7]
|
||||
[0 0 7 0 0 0 5 8 0]
|
||||
[0 1 0 0 6 7 0 0 8]
|
||||
[0 0 9 0 0 8 0 0 0]
|
||||
[0 2 6 4 0 0 7 3 5]]
|
||||
0 0)
|
||||
sudoku>(cl-format true "~{~{~a~^ ~}~%~}"
|
||||
(solve [[3 9 4 0 0 2 6 7 0]
|
||||
[0 0 0 3 0 0 4 0 0]
|
||||
[5 0 0 6 9 0 0 2 0]
|
||||
[0 4 5 0 0 0 9 0 0]
|
||||
[6 0 0 0 0 0 0 0 7]
|
||||
[0 0 7 0 0 0 5 8 0]
|
||||
[0 1 0 0 6 7 0 0 8]
|
||||
[0 0 9 0 0 8 0 0 0]
|
||||
[0 2 6 4 0 0 7 3 5]])
|
||||
3 9 4 8 5 2 6 7 1
|
||||
2 6 8 3 7 1 4 5 9
|
||||
5 7 1 6 9 4 8 2 3
|
||||
|
|
|
|||
34
Task/Sudoku/Curry/sudoku-2.curry
Normal file
34
Task/Sudoku/Curry/sudoku-2.curry
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import CLPFD
|
||||
import Constraint (allC)
|
||||
import List (transpose)
|
||||
|
||||
|
||||
sudoku :: [[Int]] -> Success
|
||||
sudoku rows =
|
||||
domain (concat rows) 1 9
|
||||
& different rows
|
||||
& different (transpose rows)
|
||||
& different blocks
|
||||
& labeling [] (concat rows)
|
||||
where
|
||||
different = allC allDifferent
|
||||
|
||||
blocks = [concat ys | xs <- each3 rows
|
||||
, ys <- transpose $ map each3 xs
|
||||
]
|
||||
each3 xs = case xs of
|
||||
(x:y:z:rest) -> [x,y,z] : each3 rest
|
||||
rest -> [rest]
|
||||
|
||||
|
||||
test = [ [_,_,3,_,_,_,_,_,_]
|
||||
, [4,_,_,_,8,_,_,3,6]
|
||||
, [_,_,8,_,_,_,1,_,_]
|
||||
, [_,4,_,_,6,_,_,7,3]
|
||||
, [_,_,_,9,_,_,_,_,_]
|
||||
, [_,_,_,_,_,2,_,_,5]
|
||||
, [_,_,4,_,7,_,_,6,8]
|
||||
, [6,_,_,_,_,_,_,_,_]
|
||||
, [7,_,_,6,_,_,5,_,_]
|
||||
]
|
||||
main | sudoku xs = xs where xs = test
|
||||
|
|
@ -1,21 +1,14 @@
|
|||
import std.stdio, std.range, std.string, std.algorithm, std.array,
|
||||
std.typetuple, std.ascii, std.typecons;
|
||||
|
||||
template Range(size_t stop) { // For loop unrolling.
|
||||
static if (stop == 0)
|
||||
alias TypeTuple!() Range;
|
||||
else
|
||||
alias TypeTuple!(Range!(stop - 1), stop - 1) Range;
|
||||
}
|
||||
std.ascii, std.typecons;
|
||||
|
||||
struct Digit {
|
||||
immutable char d;
|
||||
|
||||
this(in char d_) pure nothrow
|
||||
this(in char d_) pure nothrow @safe @nogc
|
||||
in { assert(d_ >= '0' && d_ <= '9'); }
|
||||
body { this.d = d_; }
|
||||
|
||||
this(in int d_) pure nothrow
|
||||
this(in int d_) pure nothrow @safe @nogc
|
||||
in { assert(d_ >= '0' && d_ <= '9'); }
|
||||
body { this.d = cast(char)d_; } // Required cast.
|
||||
|
||||
|
|
@ -34,30 +27,31 @@ pure nothrow {
|
|||
problem[].map!(c => c - '0').copy(grid[]);
|
||||
|
||||
// DMD doesn't inline this function. Performance loss.
|
||||
Tgrid access(in size_t x, in size_t y) nothrow {
|
||||
Tgrid access(in size_t x, in size_t y) nothrow @safe @nogc {
|
||||
return grid[y * sudokuSide + x];
|
||||
}
|
||||
|
||||
// DMD doesn't inline this function. If you want to retain
|
||||
// the same performance as the C++ entry and you use the DMD
|
||||
// compiler then this function must be manually inlined.
|
||||
bool checkValidity(in Tgrid val, in size_t x, in size_t y) nothrow {
|
||||
/*static*/ foreach (immutable i; Range!sudokuSide)
|
||||
bool checkValidity(in Tgrid val, in size_t x, in size_t y)
|
||||
pure nothrow @safe @nogc {
|
||||
/*static*/ foreach (immutable i; staticIota!(0, sudokuSide))
|
||||
if (access(i, y) == val || access(x, i) == val)
|
||||
return false;
|
||||
|
||||
immutable startX = (x / sudokuUnitSide) * sudokuUnitSide;
|
||||
immutable startY = (y / sudokuUnitSide) * sudokuUnitSide;
|
||||
|
||||
/*static*/ foreach (immutable i; Range!sudokuUnitSide)
|
||||
/*static*/ foreach (immutable j; Range!sudokuUnitSide)
|
||||
/*static*/ foreach (immutable i; staticIota!(0, sudokuUnitSide))
|
||||
/*static*/ foreach (immutable j; staticIota!(0, sudokuUnitSide))
|
||||
if (access(startX + j, startY + i) == val)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool canPlaceNumbers(in size_t pos=0) nothrow {
|
||||
bool canPlaceNumbers(in size_t pos=0) nothrow @safe @nogc {
|
||||
if (pos == SudokuTable.length)
|
||||
return true;
|
||||
if (grid[pos] > 0)
|
||||
|
|
@ -87,7 +81,7 @@ pure nothrow {
|
|||
}
|
||||
|
||||
string representSudoku(in ref SudokuTable sudo)
|
||||
pure nothrow out(result) {
|
||||
pure nothrow @safe out(result) {
|
||||
assert(result.countchars("1-9") == sudo[].count!q{a != '0'});
|
||||
assert(result.countchars(".") == sudo[].count!q{a == '0'});
|
||||
} body {
|
||||
34
Task/Sudoku/D/sudoku-2.d
Normal file
34
Task/Sudoku/D/sudoku-2.d
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import std.stdio, std.algorithm, std.range;
|
||||
|
||||
const(int)[] solve(immutable int[] s) pure nothrow @safe {
|
||||
immutable i = s.countUntil(0);
|
||||
if (i == -1)
|
||||
return s;
|
||||
|
||||
enum B = (int i, int j) => i / 27 ^ j / 27 | (i%9 / 3 ^ j%9 / 3);
|
||||
immutable c = iota(81)
|
||||
.filter!(j => !((i - j) % 9 * (i/9 ^ j/9) * B(i, j)))
|
||||
.map!(j => s[j]).array;
|
||||
|
||||
foreach (immutable v; 1 .. 10)
|
||||
if (!c.canFind(v)) {
|
||||
const r = solve(s[0 .. i] ~ v ~ s[i + 1 .. $]);
|
||||
if (!r.empty)
|
||||
return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void main() {
|
||||
immutable problem = [
|
||||
8, 5, 0, 0, 0, 2, 4, 0, 0,
|
||||
7, 2, 0, 0, 0, 0, 0, 0, 9,
|
||||
0, 0, 4, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0, 7, 0, 0, 2,
|
||||
3, 0, 5, 0, 0, 0, 9, 0, 0,
|
||||
0, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 8, 0, 0, 7, 0,
|
||||
0, 1, 7, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 3, 6, 0, 4, 0];
|
||||
writefln("%(%s\n%)", problem.solve.chunks(9));
|
||||
}
|
||||
42
Task/Sudoku/D/sudoku-3.d
Normal file
42
Task/Sudoku/D/sudoku-3.d
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import std.stdio, std.algorithm, std.range, std.typecons;
|
||||
|
||||
Nullable!(const ubyte[81]) solve(in ubyte[81] s) pure nothrow @safe @nogc {
|
||||
immutable i = s[].countUntil(0);
|
||||
if (i == -1)
|
||||
return typeof(return)(s);
|
||||
|
||||
static immutable B = (in int i, in int j) pure nothrow @safe @nogc =>
|
||||
i / 27 ^ j / 27 | (i % 9 / 3 ^ j % 9 / 3);
|
||||
|
||||
ubyte[81] c = void;
|
||||
size_t len = 0;
|
||||
foreach (immutable int j; 0 .. c.length)
|
||||
if (!((i - j) % 9 * (i/9 ^ j/9) * B(i, j)))
|
||||
c[len++] = s[j];
|
||||
|
||||
foreach (immutable ubyte v; 1 .. 10)
|
||||
if (!c[0 .. len].canFind(v)) {
|
||||
ubyte[81] s2 = void;
|
||||
s2[0 .. i] = s[0 .. i];
|
||||
s2[i] = v;
|
||||
s2[i + 1 .. $] = s[i + 1 .. $];
|
||||
const r = solve(s2);
|
||||
if (!r.isNull)
|
||||
return typeof(return)(r);
|
||||
}
|
||||
return typeof(return)();
|
||||
}
|
||||
|
||||
void main() {
|
||||
immutable ubyte[81] problem = [
|
||||
8, 5, 0, 0, 0, 2, 4, 0, 0,
|
||||
7, 2, 0, 0, 0, 0, 0, 0, 9,
|
||||
0, 0, 4, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0, 7, 0, 0, 2,
|
||||
3, 0, 5, 0, 0, 0, 9, 0, 0,
|
||||
0, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 8, 0, 0, 7, 0,
|
||||
0, 1, 7, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 3, 6, 0, 4, 0];
|
||||
writefln("%(%s\n%)", problem.solve.get[].chunks(9));
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ type
|
|||
|
||||
function CheckValidity(val: Integer; x: Integer; y: Integer): Boolean;
|
||||
function ToString: string; reintroduce;
|
||||
procedure PlaceNumber(pos: Integer);
|
||||
function PlaceNumber(pos: Integer): Boolean;
|
||||
public
|
||||
constructor Create(s: string);
|
||||
|
||||
|
|
@ -79,15 +79,19 @@ begin
|
|||
Result := sb;
|
||||
end;
|
||||
|
||||
procedure TSudokuSolver.PlaceNumber(pos: Integer);
|
||||
function TSudokuSolver.PlaceNumber(pos: Integer): Boolean;
|
||||
var
|
||||
n: Integer;
|
||||
begin
|
||||
Result := False;
|
||||
if Pos = 81 then
|
||||
raise Exception.Create('Finished!');
|
||||
begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
if FGrid[pos] > 0 then
|
||||
begin
|
||||
PlaceNumber(Succ(pos));
|
||||
Result := PlaceNumber(Succ(pos));
|
||||
Exit;
|
||||
end;
|
||||
for n := 1 to 9 do
|
||||
|
|
@ -95,8 +99,9 @@ begin
|
|||
if CheckValidity(n, pos mod 9, pos div 9) then
|
||||
begin
|
||||
FGrid[pos] := n;
|
||||
PlaceNumber(Succ(pos));
|
||||
FGrid[pos] := 0;
|
||||
Result := PlaceNumber(Succ(pos));
|
||||
if not Result then
|
||||
FGrid[pos] := 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
|
@ -112,11 +117,9 @@ end;
|
|||
|
||||
procedure TSudokuSolver.Solve;
|
||||
begin
|
||||
try
|
||||
PlaceNumber(0);
|
||||
if not PlaceNumber(0) then
|
||||
ShowMessage('Unsolvable');
|
||||
except
|
||||
ShowMessage((ExceptObject as Exception).Message);
|
||||
ShowMessage(ToString);
|
||||
else
|
||||
ShowMessage('Solved!');
|
||||
end;
|
||||
end;
|
||||
|
|
|
|||
83
Task/Sudoku/PL-I/sudoku-1.pli
Normal file
83
Task/Sudoku/PL-I/sudoku-1.pli
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
sudoku: procedure options (main); /* 27 July 2014 */
|
||||
|
||||
declare grid (9,9) fixed (1) static initial (
|
||||
0, 0, 3, 0, 2, 0, 6, 0, 0,
|
||||
9, 0, 0, 3, 0, 5, 0, 0, 1,
|
||||
0, 0, 1, 8, 0, 6, 4, 0, 0,
|
||||
0, 0, 8, 1, 0, 2, 9, 0, 0,
|
||||
7, 0, 0, 0, 0, 0, 0, 0, 8,
|
||||
0, 0, 6, 7, 0, 8, 2, 0, 0,
|
||||
0, 0, 2, 6, 0, 9, 5, 0, 0,
|
||||
8, 0, 0, 2, 0, 3, 0, 0, 9,
|
||||
0, 0, 5, 0, 1, 0, 3, 0, 0 );
|
||||
|
||||
declare grid_solved (9,9) fixed (1);
|
||||
|
||||
call print_sudoku (grid);
|
||||
call solve (1, 1);
|
||||
put skip (2);
|
||||
call print_sudoku (grid_solved);
|
||||
|
||||
solve: procedure (i, j) recursive options (reorder);
|
||||
declare (i, j) fixed binary;
|
||||
declare (n, n_tmp) fixed binary;
|
||||
|
||||
if i > 9 then
|
||||
grid_solved = grid;
|
||||
else
|
||||
do n = 1 to 9;
|
||||
if is_safe (i, j, n) then
|
||||
do;
|
||||
n_tmp = grid (i, j);
|
||||
grid (i, j) = n;
|
||||
if j = 9 then
|
||||
call solve (i + 1, 1);
|
||||
else
|
||||
call solve (i, j + 1);
|
||||
grid (i, j) = n_tmp;
|
||||
end;
|
||||
end;
|
||||
|
||||
end solve;
|
||||
|
||||
is_safe: procedure (i, j, n) returns (bit(1) aligned) options (reorder);
|
||||
declare (i, j, n) fixed binary;
|
||||
declare (true value ('1'b), false value ('0'b) ) bit (1);
|
||||
declare (i_min, j_min, ii, jj) fixed binary;
|
||||
declare kk bit(1) aligned;
|
||||
|
||||
if grid (i, j) = n then return (true);
|
||||
if grid (i, j) ^= 0 then return (false);
|
||||
if any (grid (i, *) = n) then return (false);
|
||||
if any (grid (*, j) = n) then return (false);
|
||||
|
||||
/* i_min and j_min are the co-ordinates of the top left-hand corner */
|
||||
/* of 3 x 3 grid in which element (i,j) exists. */
|
||||
i_min = 1 + 3 * trunc((i - 1) / 3);
|
||||
j_min = 1 + 3 * trunc((j - 1) / 3);
|
||||
|
||||
begin;
|
||||
declare sub_grid(3,3) fixed (1) defined grid(1sub+i_min-1,2sub+j_min-1);
|
||||
|
||||
kk = true;
|
||||
if any(sub_grid = n) then kk = false;
|
||||
end;
|
||||
return (kk);
|
||||
end is_safe;
|
||||
|
||||
print_sudoku: procedure (grid);
|
||||
declare grid (*,*) fixed (1);
|
||||
declare ( i, j, ii) fixed binary;
|
||||
declare bar character (19) initial ( '+-----+-----+-----+' );
|
||||
declare frame (9) character (1) initial (' ', ' ', '|', ' ', ' ', '|', ' ', ' ', '|' );
|
||||
|
||||
put skip list (bar);
|
||||
do i = 1 to 7 by 3;
|
||||
do ii = i to i + 2;
|
||||
put skip edit ( '|', (grid (ii, j), frame(j) do j = 1 to 9) ) (a, f(1));
|
||||
end;
|
||||
put skip list (bar);
|
||||
end;
|
||||
end print_sudoku;
|
||||
|
||||
end sudoku;
|
||||
138
Task/Sudoku/PL-I/sudoku-2.pli
Normal file
138
Task/Sudoku/PL-I/sudoku-2.pli
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
*PROCESS MARGINS(1,120) LIBS(SINGLE,STATIC);
|
||||
*PROCESS OPTIMIZE(2) DFT(REORDER);
|
||||
|
||||
|
||||
sudoku: proc(parms) options(main);
|
||||
dcl parms char (100) var;
|
||||
|
||||
define alias bits bit (9) aligned;
|
||||
dcl total (81) type bits;
|
||||
dcl matrix (9, 9) type bits based(addr(total));
|
||||
dcl box (9, 3, 3) type bits defined (total(trunc((1sub-1) /3) * 27 + mod(1sub-1, 3) * 3 + (2sub-1) * 9 + 3sub));
|
||||
|
||||
dcl posbit (0:9) type bits init('000000000'b, '100000000'b, '010000000'b, '001000000'b,
|
||||
'000100000'b, '000010000'b, '000001000'b, '000000100'b,
|
||||
'000000010'b, '000000001'b);
|
||||
|
||||
dcl (i, j, k) fixed bin(31);
|
||||
dcl (start, finish) float(18);
|
||||
dcl result fixed dec(5,3);
|
||||
|
||||
dcl buffer char(81);
|
||||
dcl in file;
|
||||
|
||||
/* ON UNIT for the Sudoku data conversion */
|
||||
on conversion
|
||||
begin;
|
||||
put skip
|
||||
list('Sudoku data not valid.');
|
||||
stop;
|
||||
end;
|
||||
|
||||
/* ON UNIT to display info about the usage */
|
||||
on undefinedfile(in)
|
||||
begin;
|
||||
put skip
|
||||
list('Usage: ' || procedurename() || ' /filename');
|
||||
stop;
|
||||
end;
|
||||
|
||||
open file(in)
|
||||
title ('/'||parms||',type(fixed), recsize(81)') record input;
|
||||
|
||||
/* Ignore the endfile condition */
|
||||
on endfile(in);
|
||||
|
||||
/* Read the Sudoku data into buffer as one record */
|
||||
read file(in) into(buffer);
|
||||
close file(in);
|
||||
|
||||
/* Convert numbers -> position bit presentation and assign into the Sudoku board */
|
||||
do k = 1 to 81;
|
||||
total(k) = posbit(substr(buffer, k, 1));
|
||||
end;
|
||||
|
||||
/* Start solving the Sudoku */
|
||||
start = secs();
|
||||
if solve() then
|
||||
do;
|
||||
finish = secs();
|
||||
result = finish - start + 0.0005;
|
||||
put skip list('Sudoku solved! Time: ' || trim(result) || ' seconds');
|
||||
put skip(2);
|
||||
|
||||
/* display the solved Sudoku if solution exist */
|
||||
do i = 1 to 9;
|
||||
do j = 1 to 9;
|
||||
put edit(trim(index(matrix(i, j), '1'b))) (a(3));
|
||||
end;
|
||||
put skip(2);
|
||||
end;
|
||||
end;
|
||||
else put skip list('Impossible!');
|
||||
|
||||
|
||||
/*************************************/
|
||||
/* Simple backtracking sudoku solver */
|
||||
/*************************************/
|
||||
solve: proc recursive returns(bit(1));
|
||||
dcl (i, j, k) fixed bin(31);
|
||||
dcl result type bits;
|
||||
|
||||
/* find free cell */
|
||||
do i = 1 to 9;
|
||||
do j = 1 to 9;
|
||||
if matrix(i, j) = posbit(0) then goto skip;
|
||||
end;
|
||||
end;
|
||||
|
||||
/* No more free cells. Check if the completed Sudoku is valid. */
|
||||
/* Number in the cell is valid if the matching position bit is set. */
|
||||
do i = 1 to 9;
|
||||
do j = 1 to 9;
|
||||
k = index(matrix(i, j), '1'b);
|
||||
matrix(i, j) = posbit(0);
|
||||
result = ^(any(matrix(i, *)) | any(matrix(*, j)) | any(box(numbox(i, j), *, *)));
|
||||
if substr(result, k, 1) = '0'b then return('0'b);
|
||||
matrix(i, j) = posbit(k);
|
||||
end;
|
||||
end;
|
||||
|
||||
return('1'b);
|
||||
skip:
|
||||
|
||||
/* Go through and test possible values for the free cell untill the Sudoku is completed */
|
||||
result = ^(any(matrix(i, *)) | any(matrix(*, j)) | any(box(numbox(i, j), *, *)));
|
||||
k = 0;
|
||||
do forever;
|
||||
k = search(result, '1'b, k+1);
|
||||
if k = 0 then leave;
|
||||
matrix(i, j) = posbit(k);
|
||||
if solve() then return('1'b);
|
||||
else matrix(i, j) = posbit(0);
|
||||
end;
|
||||
|
||||
return('0'b);
|
||||
end solve;
|
||||
|
||||
|
||||
/********************************************/
|
||||
/* Returns box number for the sudoku coords */
|
||||
/********************************************/
|
||||
numbox: proc(i, j) returns(fixed bin(31));
|
||||
dcl (i, j) fixed bin(31);
|
||||
|
||||
dcl lookup (9, 9) fixed bin(31) static init( (3)1, (3)2, (3)3,
|
||||
(3)1, (3)2, (3)3,
|
||||
(3)1, (3)2, (3)3,
|
||||
(3)4, (3)5, (3)6,
|
||||
(3)4, (3)5, (3)6,
|
||||
(3)4, (3)5, (3)6,
|
||||
(3)7, (3)8, (3)9,
|
||||
(3)7, (3)8, (3)9,
|
||||
(3)7, (3)8, (3)9 );
|
||||
|
||||
return(lookup(i, j));
|
||||
end numbox;
|
||||
|
||||
end sudoku;
|
||||
224
Task/Sudoku/Perl-6/sudoku-2.pl6
Normal file
224
Task/Sudoku/Perl-6/sudoku-2.pl6
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
#!/usr/bin/env perl6
|
||||
use v6;
|
||||
#
|
||||
# In this code, a sudoku puzzle is represented as a two-dimentional
|
||||
# array. The cells that are not yet solved are represented by yet
|
||||
# another array of all the possible values.
|
||||
#
|
||||
# This implementation is not a simple brute force evaluation of all
|
||||
# the options, but rather makes four extra attempts to guide the
|
||||
# solution:
|
||||
#
|
||||
# 1) For every change in the grid, usually made by an attempt at a
|
||||
# solution, we will reduce the search space of the possible values
|
||||
# in all the other cells before going forward.
|
||||
#
|
||||
# 2) When a cell that is not yet resolved is the only one that can
|
||||
# hold a specific value, resolve it immediately instead of
|
||||
# performing the regular search.
|
||||
#
|
||||
# 3) Instead of trying from cell 1,1 and moving in sequence, this
|
||||
# implementation will start trying on the cell that is the closest
|
||||
# to being solved already.
|
||||
#
|
||||
# 4) Instead of trying all possible values in sequence, start with
|
||||
# the value that is the most unique. I.e.: If the options for this
|
||||
# cell are 1,4,6 and 6 is only a candidate for two of the
|
||||
# competing cells, we start with that one.
|
||||
#
|
||||
|
||||
# keep a list with all the cells, handy for traversal
|
||||
my @cells = do for 0..8 X 0..8 -> $x, $y { [ $x, $y ] };
|
||||
|
||||
#
|
||||
# Try to solve this puzzle and return the resolved puzzle if it is at
|
||||
# all solvable in this configuration.
|
||||
sub solve($sudoku, Int $level) {
|
||||
# cleanup the impossible values first,
|
||||
if (cleanup-impossible-values($sudoku, $level)) {
|
||||
# try to find implicit answers
|
||||
while (find-implicit-answers($sudoku, $level)) {
|
||||
# and every time you find some, re-do the cleanup and try again
|
||||
cleanup-impossible-values($sudoku, $level);
|
||||
}
|
||||
# Now let's actually try to solve a new value. But instead of
|
||||
# going in sequence, we select the cell that is the closest to
|
||||
# being solved already. This will reduce the overall number of
|
||||
# guesses.
|
||||
for sort { solution-complexity-factor($sudoku, $_[0], $_[1]) },
|
||||
grep { $sudoku[$_[0]][$_[1]] ~~ Array },
|
||||
@cells -> $cell
|
||||
{
|
||||
my Int ($x, $y) = @($cell);
|
||||
# Now let's try the possible values in the order of
|
||||
# uniqueness.
|
||||
for sort { matches-in-competing-cells($sudoku, $x, $y, $_) }, @($sudoku[$x][$y]) -> $val {
|
||||
trace $level, "Trying $val on "~($x+1)~","~($y+1)~" "~$sudoku[$x][$y].perl;
|
||||
my $solution = clone-sudoku($sudoku);
|
||||
$solution[$x][$y] = $val;
|
||||
my $solved = solve($solution, $level+1);
|
||||
if $solved {
|
||||
trace $level, "Solved... ($val on "~($x+1)~","~($y+1)~")";
|
||||
return $solved;
|
||||
}
|
||||
}
|
||||
# if we fell through, it means that we found no valid
|
||||
# value for this cell
|
||||
trace $level, "Backtrack, path unsolvable... (on "~($x+1)~" "~($y+1)~")";
|
||||
return 0;
|
||||
}
|
||||
# all cells are already solved.
|
||||
return $sudoku;
|
||||
} else {
|
||||
# if the cleanup failed, it means this is an invalid grid.
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
# This function reduces the search space from values that are already
|
||||
# assigned to competing cells.
|
||||
sub cleanup-impossible-values($sudoku, Int $level = 1) {
|
||||
my Bool $resolved;
|
||||
repeat {
|
||||
$resolved = False;
|
||||
for grep { $sudoku[$_[0]][$_[1]] ~~ Array },
|
||||
@cells -> $cell {
|
||||
my Int ($x, $y) = @($cell);
|
||||
# which block is this cell in
|
||||
my Int $bx = Int($x / 3);
|
||||
my Int $by = Int($y / 3);
|
||||
|
||||
# A unfilled cell is not resolved, so it shouldn't match
|
||||
my multi match-resolved-cell(Array $other, Int $this) {
|
||||
return 0;
|
||||
}
|
||||
my multi match-resolved-cell(Int $other, Int $this) {
|
||||
return $other == $this;
|
||||
}
|
||||
|
||||
# Reduce the possible values to the ones that are still
|
||||
# valid
|
||||
my @r =
|
||||
grep { !match-resolved-cell($sudoku[any(0..2)+3*$bx][any(0..2)+3*$by], $_) }, # same block
|
||||
grep { !match-resolved-cell($sudoku[any(0..8)][$y], $_) }, # same line
|
||||
grep { !match-resolved-cell($sudoku[$x][any(0..8)], $_) }, # same column
|
||||
@($sudoku[$x][$y]);
|
||||
if (@r.elems == 1) {
|
||||
# if only one element is left, then make it resolved
|
||||
$sudoku[$x][$y] = @r[0];
|
||||
$resolved = True;
|
||||
} elsif (@r.elems == 0) {
|
||||
# This is an invalid grid
|
||||
return 0;
|
||||
} else {
|
||||
$sudoku[$x][$y] = @r;
|
||||
}
|
||||
}
|
||||
} while $resolved; # repeat if there was any change
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub solution-complexity-factor($sudoku, Int $x, Int $y) {
|
||||
my Int $bx = Int($x / 3); # this block
|
||||
my Int $by = Int($y / 3);
|
||||
my multi count-values(Array $val) {
|
||||
return $val.elems;
|
||||
}
|
||||
my multi count-values(Int $val) {
|
||||
return 1;
|
||||
}
|
||||
# the number of possible values should take precedence
|
||||
my Int $f = 1000 * count-values($sudoku[$x][$y]);
|
||||
for 0..2 X 0..2 -> $lx, $ly {
|
||||
$f += count-values($sudoku[$lx+$bx*3][$ly+$by*3])
|
||||
}
|
||||
for 0..^($by*3), (($by+1)*3)..8 -> $ly {
|
||||
$f += count-values($sudoku[$x][$ly])
|
||||
}
|
||||
for 0..^($bx*3), (($bx+1)*3)..8 -> $lx {
|
||||
$f += count-values($sudoku[$lx][$y])
|
||||
}
|
||||
return $f;
|
||||
}
|
||||
|
||||
sub matches-in-competing-cells($sudoku, Int $x, Int $y, Int $val) {
|
||||
my Int $bx = Int($x / 3); # this block
|
||||
my Int $by = Int($y / 3);
|
||||
# Function to decide which possible value to try first
|
||||
my multi cell-matching(Int $cell) {
|
||||
return $val == $cell ?? 1 !! 0;
|
||||
}
|
||||
my multi cell-matching(Array $cell) {
|
||||
return $cell.grep({ $val == $_ }) ?? 1 !! 0;
|
||||
}
|
||||
my Int $c = 0;
|
||||
for 0..2 X 0..2 -> $lx, $ly {
|
||||
$c += cell-matching($sudoku[$lx+$bx*3][$ly+$by*3])
|
||||
}
|
||||
for 0..^($by*3), (($by+1)*3)..8 -> $ly {
|
||||
$c += cell-matching($sudoku[$x][$ly])
|
||||
}
|
||||
for 0..^($bx*3), (($bx+1)*3)..8 -> $lx {
|
||||
$c += cell-matching($sudoku[$lx][$y])
|
||||
}
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub find-implicit-answers($sudoku, Int $level) {
|
||||
my Bool $resolved = False;
|
||||
for grep { $sudoku[$_[0]][$_[1]] ~~ Array },
|
||||
@cells -> $cell {
|
||||
my Int ($x, $y) = @($cell);
|
||||
for @($sudoku[$x][$y]) -> $val {
|
||||
# If this is the only cell with this val as a possibility,
|
||||
# just make it resolved already
|
||||
if (matches-in-competing-cells($sudoku, $x, $y, $val) == 1) {
|
||||
$sudoku[$x][$y] = $val;
|
||||
$resolved = True;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
my $puzzle =
|
||||
map { [ map { $_ == 0 ?? [1..9] !! $_+0 }, @($_) ] },
|
||||
[ 0,0,0,0,3,7,6,0,0 ],
|
||||
[ 0,0,0,6,0,0,0,9,0 ],
|
||||
[ 0,0,8,0,0,0,0,0,4 ],
|
||||
[ 0,9,0,0,0,0,0,0,1 ],
|
||||
[ 6,0,0,0,0,0,0,0,9 ],
|
||||
[ 3,0,0,0,0,0,0,4,0 ],
|
||||
[ 7,0,0,0,0,0,8,0,0 ],
|
||||
[ 0,1,0,0,0,9,0,0,0 ],
|
||||
[ 0,0,2,5,4,0,0,0,0 ];
|
||||
|
||||
my $solved = solve($puzzle, 0);
|
||||
if $solved {
|
||||
print-sudoku($solved,0);
|
||||
} else {
|
||||
say "unsolvable.";
|
||||
}
|
||||
|
||||
# Utility functions, not really part of the solution
|
||||
|
||||
sub trace(Int $level, Str $message) {
|
||||
say '.' x $level, $message;
|
||||
}
|
||||
|
||||
sub clone-sudoku($sudoku) {
|
||||
my $clone;
|
||||
for 0..8 X 0..8 -> $x, $y {
|
||||
$clone[$x][$y] = $sudoku[$x][$y];
|
||||
}
|
||||
return $clone;
|
||||
}
|
||||
|
||||
sub print-sudoku($sudoku, Int $level = 1) {
|
||||
trace $level, '-' x 5*9;
|
||||
for @($sudoku) -> $row {
|
||||
trace $level, join " ", do for @($row) -> $cell {
|
||||
$cell ~~ Array ?? "#{$cell.elems}#" !! " $cell "
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Task/Sudoku/Prolog/sudoku-2.pro
Normal file
54
Task/Sudoku/Prolog/sudoku-2.pro
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
:- initialization(main).
|
||||
|
||||
|
||||
solve(Rows) :-
|
||||
maplist(domain_1_9, Rows)
|
||||
, different(Rows)
|
||||
, transpose(Rows,Cols), different(Cols)
|
||||
, blocks(Rows,Blocks) , different(Blocks)
|
||||
, maplist(fd_labeling, Rows)
|
||||
.
|
||||
|
||||
domain_1_9(Rows) :- fd_domain(Rows,1,9).
|
||||
different(Rows) :- maplist(fd_all_different, Rows).
|
||||
|
||||
blocks(Rows,Blocks) :-
|
||||
maplist(split3,Rows,Xs), transpose(Xs,Ys)
|
||||
, concat(Ys,Zs), concat_map(split3,Zs,Blocks)
|
||||
. % where
|
||||
split3([X,Y,Z|L],[[X,Y,Z]|R]) :- split3(L,R).
|
||||
split3([],[]).
|
||||
|
||||
|
||||
% utils/list
|
||||
concat_map(F,Xs,Ys) :- call(F,Xs,Zs), maplist(concat,Zs,Ys).
|
||||
|
||||
concat([],[]).
|
||||
concat([X|Xs],Ys) :- append(X,Zs,Ys), concat(Xs,Zs).
|
||||
|
||||
transpose([],[]).
|
||||
transpose([[X]|Col], [[X|Row]]) :- transpose(Col,[Row]).
|
||||
transpose([[X|Row]], [[X]|Col]) :- transpose([Row],Col).
|
||||
transpose([[X|Row]|Xs], [[X|Col]|Ys]) :-
|
||||
maplist(bind_head, Row, Ys, YX)
|
||||
, maplist(bind_head, Col, Xs, XY)
|
||||
, transpose(XY,YX)
|
||||
. % where
|
||||
bind_head(H,[H|T],T).
|
||||
bind_head([],[],[]).
|
||||
|
||||
|
||||
% tests
|
||||
test([ [_,_,3,_,_,_,_,_,_]
|
||||
, [4,_,_,_,8,_,_,3,6]
|
||||
, [_,_,8,_,_,_,1,_,_]
|
||||
, [_,4,_,_,6,_,_,7,3]
|
||||
, [_,_,_,9,_,_,_,_,_]
|
||||
, [_,_,_,_,_,2,_,_,5]
|
||||
, [_,_,4,_,7,_,_,6,8]
|
||||
, [6,_,_,_,_,_,_,_,_]
|
||||
, [7,_,_,6,_,_,5,_,_]
|
||||
]).
|
||||
|
||||
main :- test(T), solve(T), maplist(show,T), halt.
|
||||
show(X) :- write(X), nl.
|
||||
|
|
@ -1,39 +1,28 @@
|
|||
def read_matrix(fh)
|
||||
matrix = []
|
||||
|
||||
(0..8).each { |i|
|
||||
l = fh.readline
|
||||
matrix[i] = []
|
||||
(0..8).each { |j|
|
||||
matrix[i][j] = l[j..j].to_i
|
||||
}
|
||||
}
|
||||
matrix
|
||||
def read_matrix(data)
|
||||
lines = data.each_line.to_a # ver 2.0 later data.lines
|
||||
9.times.collect { |i| 9.times.collect { |j| lines[i][j].to_i } }
|
||||
end
|
||||
|
||||
def permissible(matrix, i, j)
|
||||
ok = [true] * 9
|
||||
ok = [nil, *1..9]
|
||||
# Same as another in the column isn't permissible...
|
||||
(0..8).each { |i2|
|
||||
next if matrix[i2][j] == 0
|
||||
ok[matrix[i2][j] - 1] = false
|
||||
}
|
||||
9.times do |i2|
|
||||
ok[matrix[i2][j]] = nil if matrix[i2][j].nonzero?
|
||||
end
|
||||
# Same as another in the row isn't permissible...
|
||||
(0..8).each { |j2|
|
||||
next if matrix[i][j2] == 0
|
||||
ok[matrix[i][j2] - 1] = false
|
||||
}
|
||||
9.times do |j2|
|
||||
ok[matrix[i][j2]] = nil if matrix[i][j2].nonzero?
|
||||
end
|
||||
# Same as another in the 3x3 block isn't permissible...
|
||||
igroup = (i / 3) * 3
|
||||
jgroup = (j / 3) * 3
|
||||
(igroup..(igroup + 2)).each { |i2|
|
||||
(jgroup..(jgroup + 2)).each { |j2|
|
||||
next if matrix[i2][j2] == 0
|
||||
ok[matrix[i2][j2] - 1] = false
|
||||
}
|
||||
}
|
||||
# Convert to the array format...
|
||||
(1..9).select { |i2| ok[i2-1] }
|
||||
irange = (ig = (i / 3) * 3) .. ig + 2
|
||||
jrange = (jg = (j / 3) * 3) .. jg + 2
|
||||
irange.each do |i2|
|
||||
jrange.each do |j2|
|
||||
ok[matrix[i2][j2]] = nil if matrix[i2][j2].nonzero?
|
||||
end
|
||||
end
|
||||
# Gathering only permitted one
|
||||
ok.compact
|
||||
end
|
||||
|
||||
def deep_copy_sudoku(matrix)
|
||||
|
|
@ -43,63 +32,55 @@ end
|
|||
def solve_sudoku(matrix)
|
||||
loop do
|
||||
options = []
|
||||
(0..8).each { |i|
|
||||
(0..8).each { |j|
|
||||
next if matrix[i][j] != 0
|
||||
9.times do |i|
|
||||
9.times do |j|
|
||||
next if matrix[i][j].nonzero?
|
||||
p = permissible(matrix, i, j)
|
||||
# If nothing is permissible, there is no solution at this level.
|
||||
return nil if p.length == 0
|
||||
options.push({:i => i, :j => j, :permissible => p})
|
||||
}
|
||||
}
|
||||
return if p.empty? # return nil
|
||||
options << [i, j, p]
|
||||
end
|
||||
end
|
||||
# If the matrix is complete, we have a solution...
|
||||
return matrix if options.length == 0
|
||||
return matrix if options.empty?
|
||||
|
||||
omin = options.min_by { |x| x[:permissible].length }
|
||||
i, j, permissible = options.min_by { |x| x.last.length }
|
||||
|
||||
# If there is an option with only one solution, set it and re-check permissibility
|
||||
if omin[:permissible].length == 1
|
||||
matrix[omin[:i]][omin[:j]] = omin[:permissible][0]
|
||||
if permissible.length == 1
|
||||
matrix[i][j] = permissible[0]
|
||||
next
|
||||
end
|
||||
|
||||
# We have two or more choices. We need to search both...
|
||||
omin[:permissible].each { |v|
|
||||
permissible.each do |v|
|
||||
mtmp = deep_copy_sudoku(matrix)
|
||||
mtmp[omin[:i]][omin[:j]] = v
|
||||
mtmp[i][j] = v
|
||||
ret = solve_sudoku(mtmp)
|
||||
return ret if ret
|
||||
}
|
||||
end
|
||||
|
||||
# We did an exhaustive search on this branch and nothing worked out.
|
||||
return nil
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def print_matrix(matrix)
|
||||
if not matrix
|
||||
puts "Impossible"
|
||||
return
|
||||
end
|
||||
puts "Impossible" or return unless matrix
|
||||
|
||||
border = "+-----+-----+-----+"
|
||||
(0..8).each { |i|
|
||||
9.times do |i|
|
||||
puts border if i%3 == 0
|
||||
(0..8).each { |j|
|
||||
print(j%3 == 0 ? "|" : " ")
|
||||
print(matrix[i][j] == 0 ? "." : matrix[i][j])
|
||||
}
|
||||
print "|\n"
|
||||
}
|
||||
9.times do |j|
|
||||
print j%3 == 0 ? "|" : " "
|
||||
print matrix[i][j] == 0 ? "." : matrix[i][j]
|
||||
end
|
||||
puts "|"
|
||||
end
|
||||
puts border
|
||||
end
|
||||
|
||||
matrix = read_matrix(DATA)
|
||||
print_matrix(matrix)
|
||||
puts
|
||||
print_matrix(solve_sudoku(matrix))
|
||||
|
||||
__END__
|
||||
data = <<EOS
|
||||
394__267_
|
||||
___3__4__
|
||||
5__69__2_
|
||||
|
|
@ -109,3 +90,9 @@ __7___58_
|
|||
_1__67__8
|
||||
__9__8___
|
||||
_264__735
|
||||
EOS
|
||||
|
||||
matrix = read_matrix(data)
|
||||
print_matrix(matrix)
|
||||
puts
|
||||
print_matrix(solve_sudoku(matrix))
|
||||
|
|
|
|||
102
Task/Sudoku/VBScript/sudoku.vb
Normal file
102
Task/Sudoku/VBScript/sudoku.vb
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
Dim grid(9, 9)
|
||||
Dim gridSolved(9, 9)
|
||||
|
||||
Public Sub Solve(i, j)
|
||||
If i > 9 Then
|
||||
'exit with gridSolved = Grid
|
||||
For r = 1 To 9
|
||||
For c = 1 To 9
|
||||
gridSolved(r, c) = grid(r, c)
|
||||
Next 'c
|
||||
Next 'r
|
||||
Exit Sub
|
||||
End If
|
||||
For n = 1 To 9
|
||||
If isSafe(i, j, n) Then
|
||||
nTmp = grid(i, j)
|
||||
grid(i, j) = n
|
||||
If j = 9 Then
|
||||
Solve i + 1, 1
|
||||
Else
|
||||
Solve i, j + 1
|
||||
End If
|
||||
grid(i, j) = nTmp
|
||||
End If
|
||||
Next 'n
|
||||
End Sub 'Solve
|
||||
|
||||
Public Function isSafe(i, j, n)
|
||||
If grid(i, j) <> 0 Then
|
||||
isSafe = (grid(i, j) = n)
|
||||
Exit Function
|
||||
End If
|
||||
'grid(i,j) is an empty cell. Check if n is OK
|
||||
'first check the row i
|
||||
For c = 1 To 9
|
||||
If grid(i, c) = n Then
|
||||
isSafe = False
|
||||
Exit Function
|
||||
End If
|
||||
Next 'c
|
||||
'now check the column j
|
||||
For r = 1 To 9
|
||||
If grid(r, j) = n Then
|
||||
isSafe = False
|
||||
Exit Function
|
||||
End If
|
||||
Next 'r
|
||||
'finally, check the 3x3 subsquare containing grid(i,j)
|
||||
iMin = 1 + 3 * Int((i - 1) / 3)
|
||||
jMin = 1 + 3 * Int((j - 1) / 3)
|
||||
For r = iMin To iMin + 2
|
||||
For c = jMin To jMin + 2
|
||||
If grid(r, c) = n Then
|
||||
isSafe = False
|
||||
Exit Function
|
||||
End If
|
||||
Next 'c
|
||||
Next 'r
|
||||
'all tests were OK
|
||||
isSafe = True
|
||||
End Function 'isSafe
|
||||
|
||||
Public Sub Sudoku()
|
||||
'main routine
|
||||
Dim s(9)
|
||||
s(1) = "001005070"
|
||||
s(2) = "920600000"
|
||||
s(3) = "008000600"
|
||||
s(4) = "090020401"
|
||||
s(5) = "000000000"
|
||||
s(6) = "304080090"
|
||||
s(7) = "007000300"
|
||||
s(8) = "000007069"
|
||||
s(9) = "010800700"
|
||||
For i = 1 To 9
|
||||
For j = 1 To 9
|
||||
grid(i, j) = Int(Mid(s(i), j, 1))
|
||||
Next 'j
|
||||
Next 'j
|
||||
'print problem
|
||||
Wscript.echo "Problem:"
|
||||
For i = 1 To 9
|
||||
c=""
|
||||
For j = 1 To 9
|
||||
c=c & grid(i, j) & " "
|
||||
Next 'j
|
||||
Wscript.echo c
|
||||
Next 'i
|
||||
'solve it!
|
||||
Solve 1, 1
|
||||
'print solution
|
||||
Wscript.echo "Solution:"
|
||||
For i = 1 To 9
|
||||
c=""
|
||||
For j = 1 To 9
|
||||
c=c & gridSolved(i, j) & " "
|
||||
Next 'j
|
||||
Wscript.echo c
|
||||
Next 'i
|
||||
End Sub 'Sudoku
|
||||
|
||||
Call sudoku
|
||||
Loading…
Add table
Add a link
Reference in a new issue