Update all new Tasks

This commit is contained in:
Ingy döt Net 2015-02-20 09:02:09 -05:00
parent 00a190b0a6
commit 91df62d461
5697 changed files with 93386 additions and 804 deletions

View file

@ -0,0 +1,65 @@
Numbrix puzzles are similar to [[Solve a Hidato puzzle|Hidato]].
The most important difference is that it is only possible to move 1 node left, right, up, or down (sometimes referred to as the [[wp:Von Neumann neighborhood|Von Neumann neighborhood]]).
Published puzzles also tend not to have holes in the grid and may not always indicate the end node.
Two examples follow:
;Example 1
Problem.
<pre>
0 0 0 0 0 0 0 0 0
0 0 46 45 0 55 74 0 0
0 38 0 0 43 0 0 78 0
0 35 0 0 0 0 0 71 0
0 0 33 0 0 0 59 0 0
0 17 0 0 0 0 0 67 0
0 18 0 0 11 0 0 64 0
0 0 24 21 0 1 2 0 0
0 0 0 0 0 0 0 0 0
</pre>
Solution.
<pre>
49 50 51 52 53 54 75 76 81
48 47 46 45 44 55 74 77 80
37 38 39 40 43 56 73 78 79
36 35 34 41 42 57 72 71 70
31 32 33 14 13 58 59 68 69
30 17 16 15 12 61 60 67 66
29 18 19 20 11 62 63 64 65
28 25 24 21 10 1 2 3 4
27 26 23 22 9 8 7 6 5
</pre>
;Example 2
Problem.
<pre>
0 0 0 0 0 0 0 0 0
0 11 12 15 18 21 62 61 0
0 6 0 0 0 0 0 60 0
0 33 0 0 0 0 0 57 0
0 32 0 0 0 0 0 56 0
0 37 0 1 0 0 0 73 0
0 38 0 0 0 0 0 72 0
0 43 44 47 48 51 76 77 0
0 0 0 0 0 0 0 0 0
</pre>
Solution.
<pre>
9 10 13 14 19 20 63 64 65
8 11 12 15 18 21 62 61 66
7 6 5 16 17 22 59 60 67
34 33 4 3 24 23 58 57 68
35 32 31 2 25 54 55 56 69
36 37 30 1 26 53 74 73 70
39 38 29 28 27 52 75 72 71
40 43 44 47 48 51 76 77 78
41 42 45 46 49 50 81 80 79
</pre>
;Task
Write a program to solve puzzles of this ilk,
demonstrating your program by solving the above examples.
Extra credit for other interesting examples.
Related Tasks:
* [[Solve a Hidato puzzle]]
* [[Solve a Holy Knight's tour]]
* [[Solve a Hopido puzzle]]
* [[Knight's tour]]

View file

@ -0,0 +1,155 @@
#include <vector>
#include <sstream>
#include <iostream>
#include <iterator>
#include <stdlib.h>
#include <string.h>
using namespace std;
struct node
{
int val;
unsigned char neighbors;
};
class nSolver
{
public:
nSolver()
{
dx[0] = -1; dy[0] = 0; dx[1] = 1; dy[1] = 0;
dx[2] = 0; dy[2] = -1; dx[3] = 0; dy[3] = 1;
}
void solve( vector<string>& puzz, int max_wid )
{
if( puzz.size() < 1 ) return;
wid = max_wid; hei = static_cast<int>( puzz.size() ) / wid;
int len = wid * hei, c = 0; max = len;
arr = new node[len]; memset( arr, 0, len * sizeof( node ) );
weHave = new bool[len + 1]; memset( weHave, 0, len + 1 );
for( vector<string>::iterator i = puzz.begin(); i != puzz.end(); i++ )
{
if( ( *i ) == "*" ) { max--; arr[c++].val = -1; continue; }
arr[c].val = atoi( ( *i ).c_str() );
if( arr[c].val > 0 ) weHave[arr[c].val] = true;
c++;
}
solveIt(); c = 0;
for( vector<string>::iterator i = puzz.begin(); i != puzz.end(); i++ )
{
if( ( *i ) == "." )
{
ostringstream o; o << arr[c].val;
( *i ) = o.str();
}
c++;
}
delete [] arr;
delete [] weHave;
}
private:
bool search( int x, int y, int w, int dr )
{
if( ( w > max && dr > 0 ) || ( w < 1 && dr < 0 ) || ( w == max && weHave[w] ) ) return true;
node* n = &arr[x + y * wid];
n->neighbors = getNeighbors( x, y );
if( weHave[w] )
{
for( int d = 0; d < 4; d++ )
{
if( n->neighbors & ( 1 << d ) )
{
int a = x + dx[d], b = y + dy[d];
if( arr[a + b * wid].val == w )
if( search( a, b, w + dr, dr ) ) return true;
}
}
return false;
}
for( int d = 0; d < 4; d++ )
{
if( n->neighbors & ( 1 << d ) )
{
int a = x + dx[d], b = y + dy[d];
if( arr[a + b * wid].val == 0 )
{
arr[a + b * wid].val = w;
if( search( a, b, w + dr, dr ) ) return true;
arr[a + b * wid].val = 0;
}
}
}
return false;
}
unsigned char getNeighbors( int x, int y )
{
unsigned char c = 0; int a, b;
for( int xx = 0; xx < 4; xx++ )
{
a = x + dx[xx], b = y + dy[xx];
if( a < 0 || b < 0 || a >= wid || b >= hei ) continue;
if( arr[a + b * wid].val > -1 ) c |= ( 1 << xx );
}
return c;
}
void solveIt()
{
int x, y, z; findStart( x, y, z );
if( z == 99999 ) { cout << "\nCan't find start point!\n"; return; }
search( x, y, z + 1, 1 );
if( z > 1 ) search( x, y, z - 1, -1 );
}
void findStart( int& x, int& y, int& z )
{
z = 99999;
for( int b = 0; b < hei; b++ )
for( int a = 0; a < wid; a++ )
if( arr[a + wid * b].val > 0 && arr[a + wid * b].val < z )
{
x = a; y = b;
z = arr[a + wid * b].val;
}
}
int wid, hei, max, dx[4], dy[4];
node* arr;
bool* weHave;
};
//------------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
int wid; string p;
//p = ". . . . . . . . . . . 46 45 . 55 74 . . . 38 . . 43 . . 78 . . 35 . . . . . 71 . . . 33 . . . 59 . . . 17 . . . . . 67 . . 18 . . 11 . . 64 . . . 24 21 . 1 2 . . . . . . . . . . ."; wid = 9;
//p = ". . . . . . . . . . 11 12 15 18 21 62 61 . . 6 . . . . . 60 . . 33 . . . . . 57 . . 32 . . . . . 56 . . 37 . 1 . . . 73 . . 38 . . . . . 72 . . 43 44 47 48 51 76 77 . . . . . . . . . ."; wid = 9;
p = "17 . . . 11 . . . 59 . 15 . . 6 . . 61 . . . 3 . . . 63 . . . . . . 66 . . . . 23 24 . 68 67 78 . 54 55 . . . . 72 . . . . . . 35 . . . 49 . . . 29 . . 40 . . 47 . 31 . . . 39 . . . 45"; wid = 9;
istringstream iss( p ); vector<string> puzz;
copy( istream_iterator<string>( iss ), istream_iterator<string>(), back_inserter<vector<string> >( puzz ) );
nSolver s; s.solve( puzz, wid );
int c = 0;
for( vector<string>::iterator i = puzz.begin(); i != puzz.end(); i++ )
{
if( ( *i ) != "*" && ( *i ) != "." )
{
if( atoi( ( *i ).c_str() ) < 10 ) cout << "0";
cout << ( *i ) << " ";
}
else cout << " ";
if( ++c >= wid ) { cout << endl; c = 0; }
}
cout << endl << endl;
return system( "pause" );
}

View file

@ -0,0 +1,209 @@
import std.stdio, std.conv, std.string, std.range, std.array, std.typecons, std.algorithm;
struct {
alias BitSet8 = ubyte; // A set of 8 bits.
alias Cell = uint;
enum : string { unavailableInCell = "#", availableInCell = "." }
enum : Cell { unavailableCell = Cell.max, availableCell = 0 }
this(in string inPuzzle) pure @safe {
const rawPuzzle = inPuzzle.splitLines.map!(row => row.split).array;
assert(!rawPuzzle.empty);
assert(!rawPuzzle[0].empty);
assert(rawPuzzle.all!(row => row.length == rawPuzzle[0].length)); // Is rectangular.
gridWidth = rawPuzzle[0].length;
gridHeight = rawPuzzle.length;
immutable nMaxCells = gridWidth * gridHeight;
grid = new Cell[nMaxCells];
auto knownMutable = new bool[nMaxCells + 1];
uint nAvailableMutable = nMaxCells;
bool[Cell] seenCells; // To avoid duplicate input numbers.
uint i = 0;
foreach (const piece; rawPuzzle.join) {
if (piece == unavailableInCell) {
nAvailableMutable--;
grid[i++] = unavailableCell;
continue;
} else if (piece == availableInCell) {
grid[i] = availableCell;
} else {
immutable cell = piece.to!Cell;
assert(cell > 0 && cell <= nMaxCells);
assert(cell !in seenCells);
seenCells[cell] = true;
knownMutable[cell] = true;
grid[i] = cell;
}
i++;
}
known = knownMutable.idup;
nAvailable = nAvailableMutable;
}
@disable this();
auto solve() pure nothrow @safe @nogc
out(result) {
if (!result.isNull) {
// Can't verify 'result' here because it's const.
// assert(!result.get.join.canFind(availableCell.text));
assert(!grid.canFind(availableCell));
auto values = grid.filter!(c => c != unavailableCell);
auto interval = iota(reduce!min(values.front, values.dropOne),
reduce!max(values.front, values.dropOne) + 1);
assert(values.walkLength == interval.length);
assert(interval.all!(c => values.count(c) == 1)); // Quadratic.
}
} body {
auto result = grid
.map!(c => (c == unavailableCell) ? unavailableInCell : c.text)
.chunks(gridWidth);
alias OutRange = Nullable!(typeof(result));
const start = findStart;
if (start.isNull)
return OutRange();
search(start.r, start.c, start.cell + 1, 1);
if (start.cell > 1) {
immutable direction = -1;
search(start.r, start.c, start.cell + direction, direction);
}
if (grid.any!(c => c == availableCell))
return OutRange();
else
return OutRange(result);
}
private:
bool search(in uint r, in uint c, in Cell cell, in int direction)
pure nothrow @safe @nogc {
if ((cell > nAvailable && direction > 0) || (cell == 0 && direction < 0) ||
(cell == nAvailable && known[cell]))
return true; // One solution found.
immutable neighbors = getNeighbors(r, c);
if (known[cell]) {
foreach (immutable i, immutable rc; shifts) {
if (neighbors & (1u << i)) {
immutable c2 = c + rc[0],
r2 = r + rc[1];
if (grid[r2 * gridWidth + c2] == cell)
if (search(r2, c2, cell + direction, direction))
return true;
}
}
return false;
}
foreach (immutable i, immutable rc; shifts) {
if (neighbors & (1u << i)) {
immutable c2 = c + rc[0],
r2 = r + rc[1],
pos = r2 * gridWidth + c2;
if (grid[pos] == availableCell) {
grid[pos] = cell; // Try.
if (search(r2, c2, cell + direction, direction))
return true;
grid[pos] = availableCell; // Restore.
}
}
}
return false;
}
BitSet8 getNeighbors(in uint r, in uint c) const pure nothrow @safe @nogc {
typeof(return) usable = 0;
foreach (immutable i, immutable rc; shifts) {
immutable c2 = c + rc[0],
r2 = r + rc[1];
if (c2 >= gridWidth || r2 >= gridHeight)
continue;
if (grid[r2 * gridWidth + c2] != unavailableCell)
usable |= (1u << i);
}
return usable;
}
auto findStart() const pure nothrow @safe @nogc {
alias Triple = Tuple!(uint,"r", uint,"c", Cell,"cell");
Nullable!Triple result;
auto cell = Cell.max;
foreach (immutable r; 0 .. gridHeight) {
foreach (immutable c; 0 .. gridWidth) {
immutable pos = gridWidth * r + c;
if (grid[pos] != availableCell &&
grid[pos] != unavailableCell && grid[pos] < cell) {
cell = grid[pos];
result = Triple(r, c, cell);
}
}
}
return result;
}
static immutable int[2][4] shifts = [[0, -1], [0, 1], [-1, 0], [1, 0]];
immutable uint gridWidth, gridHeight;
immutable int nAvailable;
immutable bool[] known; // Given known cells of the puzzle.
Cell[] grid; // Flattened mutable game grid.
}
void main() {
// enum NumbrixPuzzle to catch malformed puzzles at compile-time.
enum puzzle1 = ". . . . . . . . .
. . 46 45 . 55 74 . .
. 38 . . 43 . . 78 .
. 35 . . . . . 71 .
. . 33 . . . 59 . .
. 17 . . . . . 67 .
. 18 . . 11 . . 64 .
. . 24 21 . 1 2 . .
. . . . . . . . .".NumbrixPuzzle;
enum puzzle2 = ". . . . . . . . .
. 11 12 15 18 21 62 61 .
. 6 . . . . . 60 .
. 33 . . . . . 57 .
. 32 . . . . . 56 .
. 37 . 1 . . . 73 .
. 38 . . . . . 72 .
. 43 44 47 48 51 76 77 .
. . . . . . . . .".NumbrixPuzzle;
enum puzzle3 = "17 . . . 11 . . . 59
. 15 . . 6 . . 61 .
. . 3 . . . 63 . .
. . . . 66 . . . .
23 24 . 68 67 78 . 54 55
. . . . 72 . . . .
. . 35 . . . 49 . .
. 29 . . 40 . . 47 .
31 . . . 39 . . . 45".NumbrixPuzzle;
foreach (puzzle; [puzzle1, puzzle2, puzzle3]) {
auto solution = puzzle.solve; // Solved at run-time.
if (solution.isNull)
writeln("No solution found for puzzle.\n");
else
writefln("One solution:\n%(%-(%2s %)\n%)\n", solution);
}
}

View file

@ -0,0 +1,90 @@
global nCells, cMap, best
record Pos(r,c)
procedure main(A)
puzzle := showPuzzle("Input",readPuzzle())
QMouse(puzzle,findStart(puzzle),&null,0)
showPuzzle("Output", solvePuzzle(puzzle)) | write("No solution!")
end
procedure readPuzzle()
# Start with a reduced puzzle space
p := []
nCells := maxCols := 0
every line := !&input do {
put(p,[: gencells(line) :])
maxCols <:= *p[-1]
}
# Now normalize all rows to the same length
every i := 1 to *p do p[i] := [: !p[i] | (|-1\(maxCols - *p[i])) :]
return p
end
procedure gencells(s)
static WS, NWS
initial {
NWS := ~(WS := " \t")
cMap := table() # Map to/from internal model
cMap["_"] := 0; cMap[0] := "_"
}
s ? while not pos(0) do {
w := (tab(many(WS))|"", tab(many(NWS))) | break
w := numeric(\cMap[w]|w)
if -1 ~= w then nCells +:= 1
suspend w
}
end
procedure showPuzzle(label, p)
write(label," with ",nCells," cells:")
every r := !p do {
every c := !r do writes(right((\cMap[c]|c),*nCells+1))
write()
}
return p
end
procedure findStart(p)
if \p[r := !*p][c := !*p[r]] = 1 then return Pos(r,c)
end
procedure solvePuzzle(puzzle)
if path := \best then {
repeat {
loc := path.getLoc()
puzzle[loc.r][loc.c] := path.getVal()
path := \path.getParent() | break
}
return puzzle
}
end
class QMouse(puzzle, loc, parent, val)
method getVal(); return val; end
method getLoc(); return loc; end
method getParent(); return parent; end
method atEnd(); return (nCells = val, puzzle[loc.r,loc.c] = (val|0)); end
method visit(r,c); return (/best, validPos(r,c), Pos(r,c)); end
method validPos(r,c)
v := val+1 # number we're looking for
xv := puzzle[r,c] | fail
if (xv ~= 0) & (xv != v) then fail
if xv = (0|v) then {
ancestor := self
while xl := (ancestor := \ancestor.getParent()).getLoc() do
if (xl.r = r) & (xl.c = c) then fail
return
}
end
initially
val := val+1
if atEnd() then return best := self
QMouse(puzzle, visit(loc.r-1,loc.c) , self, val) # North
QMouse(puzzle, visit(loc.r, loc.c+1), self, val) # East
QMouse(puzzle, visit(loc.r+1,loc.c), self, val) # South
QMouse(puzzle, visit(loc.r, loc.c-1), self, val) # West
end

View file

@ -0,0 +1,15 @@
my @adjacent = [-1, 0],
[ 0, -1], [ 0, 1],
[ 1, 0];
solveboard q:to/END/;
__ __ __ __ __ __ __ __ __
__ __ 46 45 __ 55 74 __ __
__ 38 __ __ 43 __ __ 78 __
__ 35 __ __ __ __ __ 71 __
__ __ 33 __ __ __ 59 __ __
__ 17 __ __ __ __ __ 67 __
__ 18 __ __ 11 __ __ 64 __
__ __ 24 21 __ 1 2 __ __
__ __ __ __ __ __ __ __ __
END

View file

@ -0,0 +1,11 @@
solveboard q:to/END/;
0 0 0 0 0 0 0 0 0
0 11 12 15 18 21 62 61 0
0 6 0 0 0 0 0 60 0
0 33 0 0 0 0 0 57 0
0 32 0 0 0 0 0 56 0
0 37 0 1 0 0 0 73 0
0 38 0 0 0 0 0 72 0
0 43 44 47 48 51 76 77 0
0 0 0 0 0 0 0 0 0
END

View file

@ -0,0 +1 @@
Data source: http://rosettacode.org/wiki/Solve_a_Numbrix_puzzle

View file

@ -0,0 +1,53 @@
/*REXX program solves a Numbrix (R) puzzle, displays puzzle & solution.*/
maxr=0; maxc=0; maxx=0; minr=9e9; minc=9e9; minx=9e9; cells=0; @.=
parse arg xxx; PZ='Numbrix puzzle' /*get cell definitions from C.L. */
xxx=translate(xxx, , "/\;:_", ',') /*also allow other chars as comma*/
do while xxx\=''; parse var xxx r c marks ',' xxx
do while marks\=''; _=@.r.c
parse var marks x marks
if datatype(x,'N') then x=abs(x/1) /*normalize X*/
minr=min(minr,r); maxr=max(maxr,r)
minc=min(minc,c); maxc=max(maxc,c)
if x==1 then do; !r=r; !c=c; end /*start cell.*/
if _\=='' then call err "cell at" r c 'is already occupied with:' _
@.r.c=x; c=c+1; cells=cells+1 /*assign mark*/
if x==. then iterate /*hole? Skip.*/
if \datatype(x,'W') then call err 'illegal marker specified:' x
minx=min(minx,x); maxx=max(maxx,x) /*min & max X*/
end /*while marks¬='' */
end /*while xxx ¬='' */
call showGrid /* [↓] used for making fast moves*/
Nr = '0 1 0 -1 -1 1 1 -1' /*possible row for the next move.*/
Nc = '1 0 -1 0 1 -1 1 -1' /* " col " " " " */
pMoves=words(Nr) -4*(left(PZ,1)=='N') /*is this to be a Numbrix puzzle?*/
do i=1 for pMoves; Nr.i=word(Nr,i); Nc.i=word(Nc,i); end /*fast moves*/
if \next(2,!r,!c) then call err 'No solution possible for this' PZ"."
say; say 'A solution for the' PZ "exists."; say; call showGrid
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────ERR subroutine──────────────────────*/
err: say; say '***error!*** (from' PZ"): " arg(1); say; exit 13
/*──────────────────────────────────NEXT subroutine─────────────────────*/
next: procedure expose @. Nr. Nc. cells pMoves; parse arg #,r,c; ##=#+1
do t=1 for pMoves /* [↓] try some moves.*/
parse value r+Nr.t c+Nc.t with nr nc /*next move coördinates*/
if @.nr.nc==. then do; @.nr.nc=# /*a move.*/
if #==cells then return 1 /*last 1?*/
if next(##,nr,nc) then return 1
@.nr.nc=. /*undo the above move. */
iterate /*go & try another move*/
end
if @.nr.nc==# then do /*is this a fill-in ? */
if #==cells then return 1 /*last 1.*/
if next(##,nr,nc) then return 1 /*fill-in*/
end
end /*t*/
return 0 /*This ain't working. */
/*──────────────────────────────────SHOWGRID subroutine─────────────────*/
showGrid: if maxr<1 | maxc<1 then call err 'no legal cell was specified.'
if minx<1 then call err 'no 1 was specified for the puzzle start'
w=length(cells); do r=maxr to minr by -1; _=
do c=minc to maxc; _=_ right(@.r.c,w); end /*c*/
say _
end /*r*/
say; return

View file

@ -0,0 +1,95 @@
#lang racket
;;; Used in my solutions of:
;;; "Solve a Hidato Puzzle"
;;; "Solve a Holy Knights Tour"
;;; "Solve a Numbrix Puzzle"
;;; "Solve a Hopido Puzzle"
;;; As well as the solver being common, the solution renderer and input formats are common
(provide
;; Input: list of neighbour offsets
;; Output: a solver function:
;; Input: a puzzle
;; Output: either the solved puzzle or #f if impossible
solve-hidato-family
;; Input: puzzle
;; optional minimum cell width
;; Output: a pretty string that can be printed
puzzle->string)
;; Cell values are:
;; zero? - unvisited
;; positive? - nth visitied
;; else - unvisitable. In the puzzle layout, it's a _. In the hash it's a -1, so we can care less
;; about number type checking.
;; A puzzle is a sequence of sequences of cell values
;; We work with a puzzle as a hash keyed on (cons row-num col-num)
;; Take a puzzle and get a working hash of it
(define (puzzle->hash p)
(for*/hash
(((r row-num) (in-parallel p (in-naturals)))
((v col-num) (in-parallel r (in-naturals)))
#:when (integer? v))
(values (cons row-num col-num) v)))
;; Takes a hash and recreates a vector of vectors puzzle
(define (hash->puzzle h# (blank '_))
(define keys (hash-keys h#))
(define n-rows (add1 (car (argmax car keys))))
(define n-cols (add1 (cdr (argmax cdr keys))))
(for/vector #:length n-rows ((r n-rows))
(for/vector #:length n-cols ((c n-cols))
(hash-ref h# (cons r c) blank))))
;; See "provide" section for description
(define (puzzle->string p (w #f))
(match p
[#f "unsolved"]
[(? sequence? s)
(define (max-n-digits p)
(and p (add1 (order-of-magnitude (* (vector-length p) (vector-length (vector-ref p 0)))))))
(define min-width (or w (max-n-digits p)))
(string-join
(for/list ((r s))
(string-join
(for/list ((c r)) (~a c #:align 'right #:min-width min-width))
" "))
"\n")]))
(define ((solve-hidato-family neighbour-offsets) board)
(define board# (puzzle->hash board))
;; reverse mapping, will only take note of positive values
(define targets# (for/hash ([(k v) (in-hash board#)] #:when (positive? v)) (values v k)))
(define (neighbours r.c)
(for/list ((r+.c+ neighbour-offsets))
(match-define (list r+ c+) r+.c+)
(match-define (cons r c ) r.c)
(cons (+ r r+) (+ c c+))))
;; Count the moves, rather than check for "no more zeros" in puzzle
(define last-move (length (filter number? (hash-values board#))))
;; Depth first solution of the puzzle (we have to go deep, it's where the solutions are!
(define (inr-solve-pzl b# move r.c)
(cond
[(= move last-move) b#] ; no moves needed, so solved
[else
(define m++ (add1 move))
(for*/or ; check each neighbour as an option
((r.c+ (in-list (neighbours r.c)))
#:when (equal? (hash-ref targets# move r.c) r.c) ; we're where we should be!
#:when (match (hash-ref b# r.c+ -1) (0 #t) ((== m++) #t) (_ #f)))
(inr-solve-pzl (hash-set b# r.c+ m++) m++ r.c+))]))
(define (solution-starting-at n)
(define start-r.c (for/first (((k v) (in-hash board#)) #:when (= n v)) k))
(and start-r.c (inr-solve-pzl board# n start-r.c)))
(define sltn
(cond [(solution-starting-at 1) => values]
;; next clause starts from 0 for hopido
[(solution-starting-at 0) => values]))
(and sltn (hash->puzzle sltn)))

View file

@ -0,0 +1,35 @@
#lang racket
(require "hidato-family-solver.rkt")
(define von-neumann-neighbour-offsets
'((+1 0) (-1 0) (0 +1) (0 -1)))
(define solve-numbrix (solve-hidato-family von-neumann-neighbour-offsets))
(displayln
(puzzle->string
(solve-numbrix
#(#(0 0 0 0 0 0 0 0 0)
#(0 0 46 45 0 55 74 0 0)
#(0 38 0 0 43 0 0 78 0)
#(0 35 0 0 0 0 0 71 0)
#(0 0 33 0 0 0 59 0 0)
#(0 17 0 0 0 0 0 67 0)
#(0 18 0 0 11 0 0 64 0)
#(0 0 24 21 0 1 2 0 0)
#(0 0 0 0 0 0 0 0 0)))))
(newline)
(displayln
(puzzle->string
(solve-numbrix
#(#(0 0 0 0 0 0 0 0 0)
#(0 11 12 15 18 21 62 61 0)
#(0 6 0 0 0 0 0 60 0)
#(0 33 0 0 0 0 0 57 0)
#(0 32 0 0 0 0 0 56 0)
#(0 37 0 1 0 0 0 73 0)
#(0 38 0 0 0 0 0 72 0)
#(0 43 44 47 48 51 76 77 0)
#(0 0 0 0 0 0 0 0 0)))))

View file

@ -0,0 +1,29 @@
require 'HLPsolver'
ADJACENT = [[-1, 0], [0, -1], [0, 1], [1, 0]]
board1 = <<EOS
0 0 0 0 0 0 0 0 0
0 0 46 45 0 55 74 0 0
0 38 0 0 43 0 0 78 0
0 35 0 0 0 0 0 71 0
0 0 33 0 0 0 59 0 0
0 17 0 0 0 0 0 67 0
0 18 0 0 11 0 0 64 0
0 0 24 21 0 1 2 0 0
0 0 0 0 0 0 0 0 0
EOS
HLPsolver.new(board1).solve
board2 = <<EOS
0 0 0 0 0 0 0 0 0
0 11 12 15 18 21 62 61 0
0 6 0 0 0 0 0 60 0
0 33 0 0 0 0 0 57 0
0 32 0 0 0 0 0 56 0
0 37 0 1 0 0 0 73 0
0 38 0 0 0 0 0 72 0
0 43 44 47 48 51 76 77 0
0 0 0 0 0 0 0 0 0
EOS
HLPsolver.new(board2).solve