Update all new Tasks
This commit is contained in:
parent
00a190b0a6
commit
91df62d461
5697 changed files with 93386 additions and 804 deletions
22
Task/Solve-a-Hopido-puzzle/00DESCRIPTION
Normal file
22
Task/Solve-a-Hopido-puzzle/00DESCRIPTION
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Hopido puzzles are similar to [[Solve a Hidato puzzle | Hidato]]. The most important difference is that the only moves allowed are: hop over one tile diagonally; and over two tiles horizontally and vertically. It should be possible to start anywhere in the path, the end point isn't indicated and there are no intermediate clues. [http://gamesandinnovation.com/2010/02/10/hopido-design-post-mortem/ Hopido Design Post Mortem] contains the following:
|
||||
|
||||
"Big puzzles represented another problem. Up until quite late in the project our puzzle solver was painfully slow with most puzzles above 7×7 tiles. Testing the solution from each starting point could take hours. If the tile layout was changed even a little, the whole puzzle had to be tested again. We were just about to give up the biggest puzzles entirely when our programmer suddenly came up with a magical algorithm that cut the testing process down to only minutes. Hooray!"
|
||||
|
||||
Knowing the kindness in the heart of every contributor to Rosetta Code I know that we shall feel that as an act of humanity we must solve these puzzles for them in let's say milliseconds.
|
||||
|
||||
Example:
|
||||
|
||||
. 0 0 . 0 0 .
|
||||
0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0
|
||||
. 0 0 0 0 0 .
|
||||
. . 0 0 0 . .
|
||||
. . . 0 . . .
|
||||
|
||||
Extra credits are available for other interesting designs.
|
||||
|
||||
Realated Tasks:
|
||||
* [[Solve a Hidato puzzle]]
|
||||
* [[Solve a Holy Knight's tour]]
|
||||
* [[Solve a Numbrix puzzle]]
|
||||
* [[Knight's tour]]
|
||||
133
Task/Solve-a-Hopido-puzzle/C++/solve-a-hopido-puzzle.cpp
Normal file
133
Task/Solve-a-Hopido-puzzle/C++/solve-a-hopido-puzzle.cpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#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] = -2; dy[0] = -2; dx[1] = -2; dy[1] = 2;
|
||||
dx[2] = 2; dy[2] = -2; dx[3] = 2; dy[3] = 2;
|
||||
dx[4] = -3; dy[4] = 0; dx[5] = 3; dy[5] = 0;
|
||||
dx[6] = 0; dy[6] = -3; dx[7] = 0; dy[7] = 3;
|
||||
}
|
||||
|
||||
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 ) );
|
||||
|
||||
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() );
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
bool search( int x, int y, int w )
|
||||
{
|
||||
if( w > max ) return true;
|
||||
|
||||
node* n = &arr[x + y * wid];
|
||||
n->neighbors = getNeighbors( x, y );
|
||||
|
||||
for( int d = 0; d < 8; 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 + 1 ) ) 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 < 8; 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 );
|
||||
}
|
||||
|
||||
void findStart( int& x, int& y, int& z )
|
||||
{
|
||||
for( int b = 0; b < hei; b++ )
|
||||
for( int a = 0; a < wid; a++ )
|
||||
if( arr[a + wid * b].val == 0 )
|
||||
{
|
||||
x = a; y = b; z = 1;
|
||||
arr[a + wid * b].val = z;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int wid, hei, max, dx[8], dy[8];
|
||||
node* arr;
|
||||
};
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
int wid; string p;
|
||||
p = "* . . * . . * . . . . . . . . . . . . . . * . . . . . * * * . . . * * * * * . * * *"; wid = 7;
|
||||
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" );
|
||||
}
|
||||
114
Task/Solve-a-Hopido-puzzle/D/solve-a-hopido-puzzle-1.d
Normal file
114
Task/Solve-a-Hopido-puzzle/D/solve-a-hopido-puzzle-1.d
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import std.stdio, std.conv, std.string, std.range, std.algorithm, std.typecons;
|
||||
|
||||
|
||||
struct HopidoPuzzle {
|
||||
private alias InputCellBaseType = char;
|
||||
private enum InputCell : InputCellBaseType { available = '#', unavailable = '.' }
|
||||
private alias Cell = uint;
|
||||
private enum : Cell { unknownCell = 0, unavailableCell = Cell.max } // Special Cell values.
|
||||
|
||||
// Neighbors, [shift row, shift column].
|
||||
private static immutable int[2][8] shifts = [[-2, -2], [2, -2], [-2, 2], [2, 2],
|
||||
[ 0, -3], [0, 3], [-3, 0], [3, 0]];
|
||||
|
||||
private immutable size_t gridWidth, gridHeight;
|
||||
private immutable Cell nAvailableCells;
|
||||
private /*immutable*/ const InputCell[] flatPuzzle;
|
||||
private Cell[] grid; // Flattened mutable game grid.
|
||||
|
||||
@disable this();
|
||||
|
||||
|
||||
this(in string[] rawPuzzle) pure @safe
|
||||
in {
|
||||
assert(!rawPuzzle.empty);
|
||||
assert(!rawPuzzle[0].empty);
|
||||
assert(rawPuzzle.all!(row => row.length == rawPuzzle[0].length)); // Is rectangular.
|
||||
|
||||
// Has at least one start point.
|
||||
assert(rawPuzzle.join.representation.canFind(InputCell.available));
|
||||
} body {
|
||||
//immutable puzzle = rawPuzzle.to!(InputCell[][]);
|
||||
immutable puzzle = rawPuzzle.map!representation.array.to!(InputCell[][]);
|
||||
|
||||
gridWidth = puzzle[0].length;
|
||||
gridHeight = puzzle.length;
|
||||
flatPuzzle = puzzle.join;
|
||||
nAvailableCells = flatPuzzle.representation.count!(ic => ic == InputCell.available);
|
||||
|
||||
grid = flatPuzzle
|
||||
.representation
|
||||
.map!(ic => ic == InputCell.available ? unknownCell : unavailableCell)
|
||||
.array;
|
||||
}
|
||||
|
||||
|
||||
Nullable!(string[][]) solve() pure /*nothrow*/ @safe
|
||||
out(result) {
|
||||
if (!result.isNull)
|
||||
assert(!grid.canFind(unknownCell));
|
||||
} body {
|
||||
// Try all possible start positions.
|
||||
foreach (immutable r; 0 .. gridHeight) {
|
||||
foreach (immutable c; 0 .. gridWidth) {
|
||||
immutable pos = r * gridWidth + c;
|
||||
if (grid[pos] == unknownCell) {
|
||||
immutable Cell startCell = 1; // To lay the first cell value.
|
||||
grid[pos] = startCell; // Try.
|
||||
if (search(r, c, startCell + 1)) {
|
||||
auto result = zip(flatPuzzle, grid)
|
||||
//.map!({p, c} => ...
|
||||
.map!(pc => (pc[0] == InputCell.available) ?
|
||||
pc[1].text :
|
||||
InputCellBaseType(pc[0]).text)
|
||||
.array
|
||||
.chunks(gridWidth)
|
||||
.array;
|
||||
return typeof(return)(result);
|
||||
}
|
||||
grid[pos] = unknownCell; // Restore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return typeof(return)();
|
||||
}
|
||||
|
||||
|
||||
private bool search(in size_t r, in size_t c, in Cell cell) pure nothrow @safe @nogc {
|
||||
if (cell > nAvailableCells)
|
||||
return true; // One solution found.
|
||||
|
||||
foreach (immutable sh; shifts) {
|
||||
immutable r2 = r + sh[0],
|
||||
c2 = c + sh[1],
|
||||
pos = r2 * gridWidth + c2;
|
||||
// No need to test for >= 0 because uint wraps around.
|
||||
if (c2 < gridWidth && r2 < gridHeight && grid[pos] == unknownCell) {
|
||||
grid[pos] = cell; // Try.
|
||||
if (search(r2, c2, cell + 1))
|
||||
return true;
|
||||
grid[pos] = unknownCell; // Restore.
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void main() @safe {
|
||||
// enum HopidoPuzzle to catch malformed puzzles at compile-time.
|
||||
enum puzzle = ".##.##.
|
||||
#######
|
||||
#######
|
||||
.#####.
|
||||
..###..
|
||||
...#...".split.HopidoPuzzle;
|
||||
|
||||
immutable solution = puzzle.solve; // Solved at run-time.
|
||||
if (solution.isNull)
|
||||
writeln("No solution found.");
|
||||
else
|
||||
writefln("One solution:\n%(%-(%2s %)\n%)", solution);
|
||||
}
|
||||
98
Task/Solve-a-Hopido-puzzle/D/solve-a-hopido-puzzle-2.d
Normal file
98
Task/Solve-a-Hopido-puzzle/D/solve-a-hopido-puzzle-2.d
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
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 := [[-1],[-1]]
|
||||
nCells := maxCols := 0
|
||||
every line := !&input do {
|
||||
put(p,[: -1 | -1 | gencells(line) | -1 | -1 :])
|
||||
maxCols <:= *p[-1]
|
||||
}
|
||||
every put(p, [-1]|[-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["#"] := -1; cMap["_"] := 0
|
||||
cMap[-1] := " "; 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; end
|
||||
|
||||
method visit(r,c)
|
||||
if /best & validPos(r,c) then return Pos(r,c)
|
||||
end
|
||||
|
||||
method validPos(r,c)
|
||||
v := val+1
|
||||
xv := (0 <= puzzle[r][c]) | fail
|
||||
if xv = (v|0) then { # make sure this path hasn't already gone there
|
||||
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-3,loc.c), self, val)
|
||||
QMouse(puzzle, visit(loc.r-2,loc.c-2), self, val)
|
||||
QMouse(puzzle, visit(loc.r, loc.c-3), self, val)
|
||||
QMouse(puzzle, visit(loc.r+2,loc.c-2), self, val)
|
||||
QMouse(puzzle, visit(loc.r+3,loc.c), self, val)
|
||||
QMouse(puzzle, visit(loc.r+2,loc.c+2), self, val)
|
||||
QMouse(puzzle, visit(loc.r, loc.c+3), self, val)
|
||||
QMouse(puzzle, visit(loc.r-2,loc.c+2), self, val)
|
||||
end
|
||||
14
Task/Solve-a-Hopido-puzzle/Perl-6/solve-a-hopido-puzzle.pl6
Normal file
14
Task/Solve-a-Hopido-puzzle/Perl-6/solve-a-hopido-puzzle.pl6
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
my @adjacent = [3, 0],
|
||||
[2, -2], [2, 2],
|
||||
[0, -3], [0, 3],
|
||||
[-2, -2], [-2, 2],
|
||||
[-3, 0];
|
||||
|
||||
solveboard q:to/END/;
|
||||
. 0 0 . 0 0 .
|
||||
0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0
|
||||
. 0 0 0 0 0 .
|
||||
. . 0 0 0 . .
|
||||
. . . 1 . . .
|
||||
END
|
||||
1
Task/Solve-a-Hopido-puzzle/README
Normal file
1
Task/Solve-a-Hopido-puzzle/README
Normal file
|
|
@ -0,0 +1 @@
|
|||
Data source: http://rosettacode.org/wiki/Solve_a_Hopido_puzzle
|
||||
57
Task/Solve-a-Hopido-puzzle/REXX/solve-a-hopido-puzzle.rexx
Normal file
57
Task/Solve-a-Hopido-puzzle/REXX/solve-a-hopido-puzzle.rexx
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*REXX program solves a Hopido puzzle, displays puzzle and the solution.*/
|
||||
call time 'Reset' /*reset the REXX elapsed timer. */
|
||||
maxr=0; maxc=0; maxx=0; minr=9e9; minc=9e9; minx=9e9; cells=0; @.=
|
||||
parse arg xxx; /*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=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 3 0 -3 -2 2 2 -2' /*possible row for the next move.*/
|
||||
Nc = '3 0 -3 0 2 -2 2 -2' /* " col " " " " */
|
||||
pMoves=words(Nr) /*the number of possible moves. */
|
||||
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 Hopido puzzle.'
|
||||
say 'A solution for the Hopido exists.'; say; call showGrid
|
||||
et=format(time('Elapsed'),,2) /*get REXX elapsed time (in secs)*/
|
||||
if et<.1 then say 'and took less than 1/10 of a second.'
|
||||
else say 'and took' et "seconds."
|
||||
exit /*stick a fork in it, we're done.*/
|
||||
/*──────────────────────────────────ERR subroutine──────────────────────*/
|
||||
err: say; say '***error!*** (from Hopido): ' 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 leave /*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
|
||||
17
Task/Solve-a-Hopido-puzzle/Racket/solve-a-hopido-puzzle.rkt
Normal file
17
Task/Solve-a-Hopido-puzzle/Racket/solve-a-hopido-puzzle.rkt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#lang racket
|
||||
(require "hidato-family-solver.rkt")
|
||||
|
||||
(define hoppy-moore-neighbour-offsets
|
||||
'((+3 0) (-3 0) (0 +3) (0 -3) (+2 +2) (-2 -2) (-2 +2) (+2 -2)))
|
||||
|
||||
(define solve-hopido (solve-hidato-family hoppy-moore-neighbour-offsets))
|
||||
|
||||
(displayln
|
||||
(puzzle->string
|
||||
(solve-hopido
|
||||
#(#(_ 0 0 _ 0 0 _)
|
||||
#(0 0 0 0 0 0 0)
|
||||
#(0 0 0 0 0 0 0)
|
||||
#(_ 0 0 0 0 0 _)
|
||||
#(_ _ 0 0 0 _ _)
|
||||
#(_ _ _ 0 _ _ _)))))
|
||||
15
Task/Solve-a-Hopido-puzzle/Ruby/solve-a-hopido-puzzle.rb
Normal file
15
Task/Solve-a-Hopido-puzzle/Ruby/solve-a-hopido-puzzle.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
require 'HLPsolver'
|
||||
|
||||
ADJACENT = [[-3, 0], [0, -3], [0, 3], [3, 0], [-2, -2], [-2, 2], [2, -2], [2, 2]]
|
||||
|
||||
board1 = <<EOS
|
||||
. 0 0 . 0 0 .
|
||||
0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0
|
||||
. 0 0 0 0 0 .
|
||||
. . 0 0 0 . .
|
||||
. . . 1 . . .
|
||||
EOS
|
||||
t0 = Time.now
|
||||
HLPsolver.new(board1).solve
|
||||
puts " #{Time.now - t0} sec"
|
||||
96
Task/Solve-a-Hopido-puzzle/Tcl/solve-a-hopido-puzzle.tcl
Normal file
96
Task/Solve-a-Hopido-puzzle/Tcl/solve-a-hopido-puzzle.tcl
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package require Tcl 8.6
|
||||
|
||||
oo::class create HopidoSolver {
|
||||
variable grid start limit
|
||||
constructor {puzzle} {
|
||||
set grid $puzzle
|
||||
for {set y 0} {$y < [llength $grid]} {incr y} {
|
||||
for {set x 0} {$x < [llength [lindex $grid $y]]} {incr x} {
|
||||
if {[set cell [lindex $grid $y $x]] == 1} {
|
||||
set start [list $y $x]
|
||||
}
|
||||
incr limit [expr {$cell>=0}]
|
||||
}
|
||||
}
|
||||
if {![info exist start]} {
|
||||
return -code error "no starting position found"
|
||||
}
|
||||
}
|
||||
method moves {} {
|
||||
return {
|
||||
0 -3
|
||||
-2 -2 -2 2
|
||||
-3 0 3 0
|
||||
-2 2 2 2
|
||||
0 3
|
||||
}
|
||||
}
|
||||
method Moves {g r c} {
|
||||
set valid {}
|
||||
foreach {dr dc} [my moves] {
|
||||
set R [expr {$r + $dr}]
|
||||
set C [expr {$c + $dc}]
|
||||
if {[lindex $g $R $C] == 0} {
|
||||
lappend valid $R $C
|
||||
}
|
||||
}
|
||||
return $valid
|
||||
}
|
||||
|
||||
method Solve {g r c v} {
|
||||
lset g $r $c [incr v]
|
||||
if {$v >= $limit} {return $g}
|
||||
foreach {r c} [my Moves $g $r $c] {
|
||||
return [my Solve $g $r $c $v]
|
||||
}
|
||||
return -code continue
|
||||
}
|
||||
|
||||
method solve {} {
|
||||
while {[incr i]==1} {
|
||||
set grid [my Solve $grid {*}$start 0]
|
||||
return
|
||||
}
|
||||
return -code error "solution not possible"
|
||||
}
|
||||
method solution {} {return $grid}
|
||||
}
|
||||
|
||||
proc parsePuzzle {str} {
|
||||
foreach line [split $str "\n"] {
|
||||
if {[string trim $line] eq ""} continue
|
||||
lappend rows [lmap {- c} [regexp -all -inline {(.)\s?} $line] {
|
||||
string map {" " -1 "." -1} $c
|
||||
}]
|
||||
}
|
||||
set len [tcl::mathfunc::max {*}[lmap r $rows {llength $r}]]
|
||||
for {set i 0} {$i < [llength $rows]} {incr i} {
|
||||
while {[llength [lindex $rows $i]] < $len} {
|
||||
lset rows $i end+1 -1
|
||||
}
|
||||
}
|
||||
return $rows
|
||||
}
|
||||
proc showPuzzle {grid name} {
|
||||
foreach row $grid {foreach cell $row {incr c [expr {$cell>=0}]}}
|
||||
set len [string length $c]
|
||||
set u [string repeat "_" $len]
|
||||
puts "$name with $c cells"
|
||||
foreach row $grid {
|
||||
puts [format " %s" [join [lmap c $row {
|
||||
format "%*s" $len [if {$c==-1} list elseif {$c==0} {set u} {set c}]
|
||||
}]]]
|
||||
}
|
||||
}
|
||||
set puzzle [parsePuzzle {
|
||||
. 0 0 . 0 0 .
|
||||
0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0
|
||||
. 0 0 0 0 0 .
|
||||
. . 0 0 0 . .
|
||||
. . . 1 . . .
|
||||
}]
|
||||
showPuzzle $puzzle "Input"
|
||||
HopidoSolver create hop $puzzle
|
||||
hop solve
|
||||
showPuzzle [hop solution] "Output"
|
||||
Loading…
Add table
Add a link
Reference in a new issue