Just another update

This commit is contained in:
Ingy döt Net 2015-02-20 00:35:01 -05:00
parent a25938f123
commit 00a190b0a6
6591 changed files with 94363 additions and 23227 deletions

View file

@ -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.

View file

@ -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)))))))

View file

@ -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

View 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

View file

@ -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
View 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
View 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));
}

View file

@ -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;

View 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;

View 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;

View 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 "
}
}
}

View 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.

View file

@ -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))

View 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