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


import java.util.*;

public class Numbrix {

    final static String[] board = {
        "00,00,00,00,00,00,00,00,00",
        "00,00,46,45,00,55,74,00,00",
        "00,38,00,00,43,00,00,78,00",
        "00,35,00,00,00,00,00,71,00",
        "00,00,33,00,00,00,59,00,00",
        "00,17,00,00,00,00,00,67,00",
        "00,18,00,00,11,00,00,64,00",
        "00,00,24,21,00,01,02,00,00",
        "00,00,00,00,00,00,00,00,00"};

    final static int[][] moves = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

    static int[][] grid;
    static int[] clues;
    static int totalToFill;

    public static void main(String[] args) {
        int nRows = board.length + 2;
        int nCols = board[0].split(",").length + 2;
        int startRow = 0, startCol = 0;

        grid = new int[nRows][nCols];
        totalToFill = (nRows - 2) * (nCols - 2);
        List<Integer> lst = new ArrayList<>();

        for (int r = 0; r < nRows; r++) {
            Arrays.fill(grid[r], -1);

            if (r >= 1 && r < nRows - 1) {

                String[] row = board[r - 1].split(",");

                for (int c = 1; c < nCols - 1; c++) {
                    int val = Integer.parseInt(row[c - 1]);
                    if (val > 0)
                        lst.add(val);
                    if (val == 1) {
                        startRow = r;
                        startCol = c;
                    }
                    grid[r][c] = val;
                }
            }
        }

        clues = lst.stream().sorted().mapToInt(i -> i).toArray();

        if (solve(startRow, startCol, 1, 0))
            printResult();
    }

    static boolean solve(int r, int c, int count, int nextClue) {
        if (count > totalToFill)
            return true;

        if (grid[r][c] != 0 && grid[r][c] != count)
            return false;

        if (grid[r][c] == 0 && nextClue < clues.length)
            if (clues[nextClue] == count)
                return false;

        int back = grid[r][c];
        if (back == count)
            nextClue++;

        grid[r][c] = count;
        for (int[] move : moves)
            if (solve(r + move[1], c + move[0], count + 1, nextClue))
                return true;

        grid[r][c] = back;
        return false;
    }

    static void printResult() {
        for (int[] row : grid) {
            for (int i : row) {
                if (i == -1)
                    continue;
                System.out.printf("%2d ", i);
            }
            System.out.println();
        }
    }
}


using .Hidato

const numbrixmoves = [[-1, 0], [0, -1], [0, 1], [1, 0]]

board, maxmoves, fixed, starts = hidatoconfigure(numbrix1)
printboard(board, " 0 ", "   ")
hidatosolve(board, maxmoves, numbrixmoves, fixed, starts[1][1], starts[1][2], 1)
printboard(board)

board, maxmoves, fixed, starts = hidatoconfigure(numbrix2)
printboard(board, " 0 ", "   ")
hidatosolve(board, maxmoves, numbrixmoves, fixed, starts[1][1], starts[1][2], 1)
printboard(board)


ClearAll[NeighbourQ, CellDistance, VisualizeHidato, HiddenSingle, \
NakedN, HiddenN, ChainSearch, HidatoSolve, Cornering, ValidPuzzle, \
GapSearch, ReachDelete, GrowNeighbours]
NeighbourQ[cell1_, cell2_] := (CellDistance[cell1, cell2] === 1)
ValidPuzzle[cells_List, cands_List] :=
 MemberQ[cands, {1}] \[And] MemberQ[cands, {Length[cells]}] \[And]
  Length[cells] == Length[candidates] \[And]
  MinMax[Flatten[cands]] === {1,
    Length[cells]} \[And] (Union @@ cands === Range[Length[cells]])
CellDistance[cell1_, cell2_] := ManhattanDistance[cell1, cell2]
VisualizeHidato[cells_List, cands_List, path_ : {}] :=
 Module[{grid, nums, cb, hx, pt},
  grid = {EdgeForm[Thick],
    MapThread[
     If[Length[#2] > 1, {FaceForm[],
        Rectangle[#1]}, {FaceForm[LightGray],
        Rectangle[#1]}] &, {cells, cands}]};
  nums =
   MapThread[
    If[Length[#1] == 1, Text[Style[First[#1], 16], #2 + 0.5 {1, 1}],
      Text[
       Tooltip[Style[Length[#1], Red, 10], #1], #2 +
        0.5 {1, 1}]] &, {cands, cells}];
  cb = CoordinateBounds[cells];
  If[Length[path] > 0,
   pt = Arrow[# + {0.5, 0.5} & /@ cells[[path]]];
   ,
   pt = {};
   ];
  Graphics[{grid, nums, pt},
   PlotRange -> cb + {{-0.5, 1.5}, {-0.5, 1.5}},
   ImageSize -> 60 (1 + cb[[1, 2]] - cb[[1, 1]])]
  ]
HiddenSingle[cands_List] := Module[{singles, newcands = cands},
  singles = Cases[Tally[Flatten[cands]], {_, 1}];
  If[Length[singles] > 0,
   singles = Sort[singles[[All, 1]]];
   newcands =
    If[ContainsAny[#, singles], Intersection[#, singles], #] & /@
     newcands;
   newcands
   ,
   cands
   ]
  ]
HiddenN[cands_List, n_Integer?(# > 1 &)] := Module[{tmp, out},
  tmp = cands;
  tmp = Join @@ MapIndexed[{#1, First[#2]} &, tmp, {2}];
  tmp = Transpose /@ GatherBy[tmp, First];
  tmp[[All, 1]] = tmp[[All, 1, 1]];
  tmp = Select[tmp, 2 <= Length[Last[#]] <= n &];
  If[Length[tmp] > 0,
   tmp = Transpose /@ Subsets[tmp, {n}];
   tmp[[All, 2]] = Union @@@ tmp[[All, 2]];
   tmp = Select[tmp, Length[Last[#]] == n &];
   If[Length[tmp] > 0,
    (* for each tmp {cands,
    cells} in each of the cells delete everything except the cands *)

        out = cands;
    Do[
     Do[
      out[[c]] = Select[out[[c]], MemberQ[t[[1]], #] &];
      ,
      {c, t[[2]]}
      ]
     ,
     {t, tmp}
     ];
    out
    ,
    cands
    ]
   ,
   cands
   ]
  ]
NakedN[cands_List, n_Integer?(# > 1 &)] := Module[{tmp, newcands, ids},
  tmp = {Range[Length[cands]], cands}\[Transpose];
  tmp = Select[tmp, 2 <= Length[Last[#]] <= n &];
  If[Length[tmp] > 0,
   tmp = Transpose /@ Subsets[tmp, {n}];
   tmp[[All, 2]] = Union @@@ tmp[[All, 2]];
   tmp = Select[tmp, Length[Last[#]] == n &];
   If[Length[tmp] > 0,
    newcands = cands;
    Do[
     ids = Complement[Range[Length[newcands]], t[[1]]];
     newcands[[ids]] =
      DeleteCases[newcands[[ids]],
       Alternatives @@ t[[2]], \[Infinity]];
     ,
     {t, tmp}
     ];
    newcands
    ,
    cands
    ]
   ,
   cands
   ]
  ]
Cornering[cells_List, cands_List] :=
 Module[{newcands, neighbours, filled, neighboursfiltered, cellid,
   filledneighours, begin, end, beginend},
  filled = Flatten[MapIndexed[If[Length[#1] == 1, #2, {}] &, cands]];
  begin = If[MemberQ[cands, {1}], {}, {1}];
  end = If[MemberQ[cands, {Length[cells]}], {}, {Length[cells]}];
  beginend = Join[begin, end];
  neighbours = Outer[NeighbourQ, cells, cells, 1];
  neighbours =
   Association[
    MapIndexed[
     First[#2] -> {Complement[Flatten[Position[#1, True]], filled],
        Intersection[Flatten[Position[#1, True]], filled]} &,
     neighbours]];
  KeyDropFrom[neighbours, filled];
  neighbours = Select[neighbours, Length[First[#]] == 1 &];
  If[Length[neighbours] > 0,
   newcands = cands;
   neighbours = KeyValueMap[List, neighbours];
   Do[
    cellid = n[[1]];
    filledneighours = n[[2, 2]];
    filledneighours = Join @@ cands[[filledneighours]];
    filledneighours =
     Union[filledneighours - 1, filledneighours + 1];
    filledneighours = Union[filledneighours, beginend];
    newcands[[cellid]] =
     Intersection[newcands[[cellid]], filledneighours];
    ,
    {n, neighbours}
    ];
   newcands
   ,
   cands
   ]
  ]
ChainSearch[cells_, cands_] := Module[{neighbours, sols, out},
  neighbours = Outer[NeighbourQ, cells, cells, 1];
  neighbours =
   Association[
    MapIndexed[First[#2] -> Flatten[Position[#1, True]] &,
     neighbours]];
  sols = Reap[ChainSearch[neighbours, cands, {}];][[2]];
  If[Length[sols] > 0,
   sols = sols[[1]];
   If[Length[sols] > 1,
    Print["multiple solutions found, showing first"];
    ];
   sols = First[sols];
   out = cands;
   out[[sols]] = List /@ Range[Length[out]];
   out
   ,
   cands
   ]
  ]
ChainSearch[neighbours_, cands_List, solcellids_List] :=
 Module[{largest, largestid, next, poss},
  largest = Length[solcellids];
  largestid = Last[solcellids, 0];
  If[largest < Length[cands],
   next = largest + 1;
   poss =
    Flatten[MapIndexed[If[MemberQ[#1, next], First[#2], {}] &, cands]];
   If[Length[poss] > 0,
    If[largest > 0,
     poss = Intersection[poss, neighbours[largestid]];
     ];
    poss = Complement[poss, solcellids]; (* can't be in previous path*)

        If[Length[poss] > 0, (* there are 'next' ones iterate over,
     calling this function *)
     Do[
      ChainSearch[neighbours, cands, Append[solcellids, p]]
      ,
      {p, poss}
      ]
     ]
    ,
    Print["There should be a next!"];
    Abort[];
    ]
   ,
   Sow[solcellids] (*
   we found a solution with this ordering of cells *)
   ]
  ]
GrowNeighbours[neighbours_, set_List] :=
 Module[{lastdone, ids, newneighbours, old},
  old = Join @@ set[[All, All, 1]];
  lastdone = Last[set];
  ids = lastdone[[All, 1]];
  newneighbours = Union @@ (neighbours /@ ids);
  newneighbours = Complement[newneighbours, old]; (*only new ones*)

  If[Length[newneighbours] > 0,
   Append[set, Thread[{newneighbours, lastdone[[1, 2]] + 1}]]
   ,
   set
   ]
  ]
ReachDelete[cells_List, cands_List, neighbours_, startid_] :=
 Module[{seed, distances, val, newcands},
  If[MatchQ[cands[[startid]], {_}],
   val = cands[[startid, 1]];
   seed = {{{startid, 0}}};
   distances =
    Join @@ FixedPoint[GrowNeighbours[neighbours, #] &, seed];
   If[Length[distances] > 0,
    distances = Select[distances, Last[#] > 0 &];
    If[Length[distances] > 0,
     newcands = cands;
     distances[[All, 2]] =
      Transpose[
       val + Outer[Times, {-1, 1}, distances[[All, 2]] - 1]];
     Do[newcands[[\[CurlyPhi][[1]]]] =
        Complement[newcands[[\[CurlyPhi][[1]]]],
         Range @@ \[CurlyPhi][[2]]];
      , {\[CurlyPhi], distances}
      ];
     newcands
     ,
     cands
     ]
    ,
    cands
    ]
   ,
   Print["invalid starting point for neighbour search"];
   Abort[];
   ]
  ]
GapSearch[cells_List, cands_List] :=
 Module[{givensid, givens, neighbours},
  givensid = Flatten[Position[cands, {_}]];
  givens = {cells[[givensid]], givensid,
     Flatten[cands[[givensid]]]}\[Transpose];
  If[Length[givens] > 0,
   givens = SortBy[givens, Last];
   givens = Split[givens, Last[#2] == Last[#1] + 1 &];
   givens = If[Length[#] <= 2, #, #[[{1, -1}]]] & /@ givens;
   If[Length[givens] > 0,
    givens = Join @@ givens;
    If[Length[givens] > 0,
     neighbours = Outer[NeighbourQ, cells, cells, 1];
     neighbours =
      Association[
       MapIndexed[First[#2] -> Flatten[Position[#1, True]] &,
        neighbours]];
     givens = givens[[All, 2]];
     Fold[ReachDelete[cells, #1, neighbours, #2] &, cands, givens]
     ,
     cands
     ]
    ,
    cands
    ]
   ,
   cands
   ]
  ]
HidatoSolve[cells_List, cands_List] :=
 Module[{newcands = cands, old},
  [email protected][cells, newcands];
  If[ValidPuzzle[cells, cands] \[Or] 1 == 1,
   old = -1;
   newcands = GapSearch[cells, newcands];
   While[old =!= newcands,
    old = newcands;
    newcands = GapSearch[cells, newcands];
    If[old === newcands,
     newcands = HiddenSingle[newcands];
     If[old === newcands,
      newcands = NakedN[newcands, 2];
      newcands = HiddenN[newcands, 2];
      If[old === newcands,
       newcands = NakedN[newcands, 3];
       newcands = HiddenN[newcands, 3];
       If[old === newcands,
        newcands = Cornering[cells, newcands];
        If[old === newcands,
         newcands = NakedN[newcands, 4];
         newcands = HiddenN[newcands, 4];
         If[old === newcands \[And] 2 == 3,
          newcands = NakedN[newcands, 5];
          newcands = HiddenN[newcands, 5];
          If[old === newcands,
           newcands = NakedN[newcands, 6];
           newcands = HiddenN[newcands, 6];
           If[old === newcands,
            newcands = NakedN[newcands, 7];
            newcands = HiddenN[newcands, 7];
            If[old === newcands,
             newcands = NakedN[newcands, 8];
             newcands = HiddenN[newcands, 8];
             ]
            ]
           ]
          ]
         ]
        ]
       ]
      ]
     ]
    ];
   If[Length[Flatten[newcands]] > Length[newcands], (*
    if not solved do a depth-first brute force search*)

    newcands = ChainSearch[cells, newcands];
    ];
   [email protected][cells, newcands];
   newcands
   ,
   Print[
    "There seems to be something wrong with your Hidato puzzle. Check \
if the begin and endpoints are given, the cells and candidates have \
the same length, all the numbers are among the \
candidates\[Ellipsis]"]
   ]
  ]

puzz = "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";
puzz = StringSplit[#, " "] & /@
   StringSplit[StringReplace[puzz, "  " -> " "], "\n"];
puzz = Map[StringTrim /* ToExpression, puzz, {2}];
puzz //= Transpose;
puzz //= Map[Reverse];
pos = Position[puzz, Except[0], {2}, Heads -> False];
clues = Thread[{pos, List /@ Extract[puzz, pos]}];
cells = Tuples[Range[9], 2];
candidates = ConstantArray[[email protected][cells], Length[cells]];
indices = Flatten[Position[cells, #] & /@ clues[[All, 1]]];
candidates[[indices]] = clues[[All, 2]];
out = HidatoSolve[cells, candidates];

puzz = " 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";
puzz = StringSplit[#, " "] & /@
   StringSplit[StringReplace[puzz, "  " -> " "], "\n"];
puzz = Map[StringTrim /* ToExpression, puzz, {2}];
puzz //= Transpose;
puzz //= Map[Reverse];
pos = Position[puzz, Except[0], {2}, Heads -> False];
clues = Thread[{pos, List /@ Extract[puzz, pos]}];
cells = Tuples[Range[9], 2];
candidates = ConstantArray[[email protected][cells], Length[cells]];
indices = Flatten[Position[cells, #] & /@ clues[[All, 1]]];
candidates[[indices]] = clues[[All, 2]];
out = HidatoSolve[cells, candidates];


import algorithm, sequtils, strformat, strutils

const Moves = [(1, 0), (0, 1), (-1, 0), (0, -1)]

type Numbrix = object
  grid: seq[seq[int]]
  clues: seq[int]
  totalToFill: Natural
  startRow, startCol : Natural

proc initNumbrix(board: openArray[string]): Numbrix =

  let nRows = board.len + 2
  let nCols = board[0].split(',').len + 2
  result.grid = newSeqWith(nRows, repeat(-1, nCols))
  result.totalToFill = (nRows - 2) * (nCols - 2)

  var list: seq[int]
  for r in 0..board.high:
    let row = board[r].split(',')
    for c in 0..row.high:
      let val = parseInt(row[c])
      result.grid[r + 1][c + 1] = val
      if val > 0:
        list.add val
        if val == 1:
          result.startRow = r + 1
          result.startCol = c + 1

  list.sort()
  result.clues = list

proc solve(numbrix: var Numbrix; row, col, count: Natural; nextClue: int): bool =

  if count > numbrix.totalToFill:
    return true

  let back = numbrix.grid[row][col]
  if back notin [0, count]:
    return false
  if back == 0 and nextClue < numbrix.clues.len and numbrix.clues[nextClue] == count:
    return false

  var nextClue = nextClue
  if back == count: inc nextClue

  numbrix.grid[row][col] = count
  for move in Moves:
    if numbrix.solve(row + move[1], col + move[0], count + 1, nextClue):
      return true
  numbrix.grid[row][col] = back

proc print(numbrix: Numbrix) =
  for row in numbrix.grid:
    for val in row:
      if val != -1:
        stdout.write &"{val:2} "
    echo()

when isMainModule:

  const

    Example1 = ["00,00,00,00,00,00,00,00,00",
                "00,00,46,45,00,55,74,00,00",
                "00,38,00,00,43,00,00,78,00",
                "00,35,00,00,00,00,00,71,00",
                "00,00,33,00,00,00,59,00,00",
                "00,17,00,00,00,00,00,67,00",
                "00,18,00,00,11,00,00,64,00",
                "00,00,24,21,00,01,02,00,00",
                "00,00,00,00,00,00,00,00,00"]

    Example2 = ["00,00,00,00,00,00,00,00,00",
                "00,11,12,15,18,21,62,61,00",
                "00,06,00,00,00,00,00,60,00",
                "00,33,00,00,00,00,00,57,00",
                "00,32,00,00,00,00,00,56,00",
                "00,37,00,01,00,00,00,73,00",
                "00,38,00,00,00,00,00,72,00",
                "00,43,44,47,48,51,76,77,00",
                "00,00,00,00,00,00,00,00,00"]

  for i, board in [1: Example1, 2: Example2]:
    var numbrix = initNumbrix(board)
    if numbrix.solve(numbrix.startRow, numbrix.startCol, 1, 0):
      echo &"Solution for example {i}:"
      numbrix.print()
    else:
      echo "No solution."


with javascript_semantics
sequence board, knownx, knowny

integer size, limit, nchars, tries
string fmt, blank

constant ROW = 1, COL = 2
constant moves = {{-1,0},{0,-1},{0,1},{1,0}}

function onboard(integer row, integer col)
    return row>=1 and row<=size and col>=nchars and col<=nchars*size
end function

function solve(integer row, integer col, integer n)
integer nrow, ncol
    tries+= 1
    if n>limit then return 1 end if
    if knownx[n] then
        for move=1 to length(moves) do
            nrow = row+moves[move][ROW]
            ncol = col+moves[move][COL]*nchars
            if nrow = knownx[n]
            and ncol = knowny[n] then
                if solve(nrow,ncol,n+1) then return 1 end if
                exit
            end if
        end for
        return 0
    end if
    sequence wmoves = {}
    for move=1 to length(moves) do
        nrow = row+moves[move][ROW]
        ncol = col+moves[move][COL]*nchars
        if onboard(nrow,ncol)
        and board[nrow][ncol]='.' then
            board[nrow][ncol-nchars+1..ncol] = sprintf(fmt,n)
            if solve(nrow,ncol,n+1) then return 1 end if
            board[nrow][ncol-nchars+1..ncol] = blank
        end if
    end for
    return 0
end function

procedure Numbrix(sequence s)
integer y, ch, ch2, k
atom t0 = time()
    s = split(s,'\n')
    size = length(s)
    limit = size*size
    nchars = length(sprintf(" %d",limit))
    fmt = sprintf(" %%%dd",nchars-1)
    blank = repeat('.',nchars)
    board = repeat(repeat(' ',size*nchars),size)
    knownx = repeat(0,limit)
    knowny = repeat(0,limit)
    for x=1 to size do
        for y=nchars to size*nchars by nchars do
            ch = s[x][y]
            if ch!='.' then
                k = ch-'0'
                ch2 = s[x][y-1]
                if ch2!=' ' then
                    k += (ch2-'0')*10
                    board[x][y-1] = ch2
                end if
                knownx[k] = x
                knowny[k] = y
            end if
            board[x][y] = ch
        end for
    end for
    tries = 0
    if solve(knownx[1],knowny[1],2) then
        puts(1,join(board,"\n"))
        printf(1,"\nsolution found in %d tries (%3.2fs)\n",{tries,time()-t0})
    else
        puts(1,"no solutions found\n")
    end if
end procedure

constant board1 = """
  .  .  .  .  .  .  .  .  .
  .  . 46 45  . 55 74  .  .
  . 38  .  . 43  .  . 78  .
  . 35  .  .  .  .  . 71  .
  .  . 33  .  .  . 59  .  .
  . 17  .  .  .  .  . 67  .
  . 18  .  . 11  .  . 64  .
  .  . 24 21  .  1  2  .  .
  .  .  .  .  .  .  .  .  ."""
Numbrix(board1)

constant board2 = """
  .  .  .  .  .  .  .  .  .
  . 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  .
  .  .  .  .  .  .  .  .  ."""
Numbrix(board2)


from sys import stdout
neighbours = [[-1, 0], [0, -1], [1, 0], [0, 1]]
exists = []
lastNumber = 0
wid = 0
hei = 0

def find_next(pa, x, y, z):
    for i in range(4):
        a = x + neighbours[i][0]
        b = y + neighbours[i][1]
        if wid > a > -1 and hei > b > -1:
            if pa[a][b] == z:
                return a, b

    return -1, -1

def find_solution(pa, x, y, z):
    if z > lastNumber:
        return 1
    if exists[z] == 1:
        s = find_next(pa, x, y, z)
        if s[0] < 0:
            return 0
        return find_solution(pa, s[0], s[1], z + 1)

    for i in range(4):
        a = x + neighbours[i][0]
        b = y + neighbours[i][1]
        if wid > a > -1 and hei > b > -1:
            if pa[a][b] == 0:
                pa[a][b] = z
                r = find_solution(pa, a, b, z + 1)
                if r == 1:
                    return 1
                pa[a][b] = 0

    return 0

def solve(pz, w, h):
    global lastNumber, wid, hei, exists

    lastNumber = w * h
    wid = w
    hei = h
    exists = [0 for j in range(lastNumber + 1)]

    pa = [[0 for j in range(h)] for i in range(w)]
    st = pz.split()
    idx = 0
    for j in range(h):
        for i in range(w):
            if st[idx] == ".":
                idx += 1
            else:
                pa[i][j] = int(st[idx])
                exists[pa[i][j]] = 1
                idx += 1

    x = 0
    y = 0
    t = w * h + 1
    for j in range(h):
        for i in range(w):
            if pa[i][j] != 0 and pa[i][j] < t:
                t = pa[i][j]
                x = i
                y = j

    return find_solution(pa, x, y, t + 1), pa

def show_result(r):
    if r[0] == 1:
        for j in range(hei):
            for i in range(wid):
                stdout.write(" {:0{}d}".format(r[1][i][j], 2))
            print()
    else:
        stdout.write("No Solution!\n")

    print()

r = solve(". . . . . . . . . . . 46 45 . 55 74 . . . 38 . . 43 . . 78 . . 35 . . . . . 71 . . . 33 . . . 59 . . . 17"
          " . . . . . 67 . . 18 . . 11 . . 64 . . . 24 21 . 1  2 . . . . . . . . . . .", 9, 9)
show_result(r)

r = solve(". . . . . . . . . . 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 . . . . . . . . . .", 9, 9)
show_result(r)

r = solve("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", 9, 9)
show_result(r)


SolveHopido(Grid, Locked, Max, row, col, num:=1, R:="", C:=""){
	if (R&&C)							; if neighbors (not first iteration)
	{
		Grid[R, C] := ">" num 					; place num in current neighbor and mark it visited ">"
		row:=R, col:=C						; move to current neighbor
	}

	num++								; increment num
	if (num=max)							; if reached end
		return map(Grid)					; return solution

	if locked[num]							; if current num is a locked value
	{
		row := StrSplit((StrSplit(locked[num], ",").1) , ":").1	; find row of num
		col := StrSplit((StrSplit(locked[num], ",").1) , ":").2	; find col of num
		if SolveHopido(Grid, Locked, Max, row, col, num)	; solve for current location and value
			return map(Grid)				; if solved, return solution
	}
	else
	{
		for each, value in StrSplit(Neighbor(row,col), ",")
		{
			R := StrSplit(value, ":").1
			C := StrSplit(value, ":").2

			if (Grid[R,C] = "")				; a hole or out of bounds
			|| InStr(Grid[R, C], ">")			; visited
			|| Locked[num+1] && !(Locked[num+1]~= "\b" R ":" C "\b") ; not neighbor of locked[num+1]
			|| Locked[num-1] && !(Locked[num-1]~= "\b" R ":" C "\b") ; not neighbor of locked[num-1]
			|| Locked[num]					; locked value
			|| Locked[Grid[R, C]]				; locked cell
				continue

			if SolveHopido(Grid, Locked, Max, row, col, num, R, C)	; solve for current location, neighbor and value
				return map(Grid)			; if solved, return solution
		}
	}
	num--								; step back
	for i, line in Grid
		for j, element in line
			if InStr(element, ">") && (StrReplace(element, ">") >= num)
				Grid[i, j] := 0
}
;--------------------------------
;--------------------------------
;--------------------------------
Neighbor(row,col){
	return Trim( ""
	. ","  row ":" col-3
	. ","  row ":" col+3
	. ","  row-3 ":" col
	. ","  row+3 ":" col

	. ","  row+2 ":" col+2
	. ","  row+2 ":" col-2
	. ","  row-2 ":" col+2
	. ","  row-2 ":" col-2
	, ",")
}
;--------------------------------
map(Grid){
	for i, row in Grid
	{
		for j, element in row
			line .= (A_Index > 1 ? "`t" : "") element
		map .= (map<>""?"`n":"") line
		line := ""
	}
	return StrReplace(map, ">")
}


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


using System.Collections;
using System.Collections.Generic;
using static System.Console;
using static System.Math;
using static System.Linq.Enumerable;

public class Solver
{
    private static readonly (int dx, int dy)[]
        //other puzzle types elided
        hopidoMoves = {(-3,0),(0,-3),(0,3),(3,0),(-2,-2),(-2,2),(2,-2),(2,2)},

    private (int dx, int dy)[] moves;

    public static void Main()
    {
        Print(new Solver(hopidoMoves).Solve(false,
            ".00.00.",
            "0000000",
            "0000000",
            ".00000.",
            "..000..",
            "...0..."
        ));
    }

    public Solver(params (int dx, int dy)[] moves) => this.moves = moves;

    public int[,] Solve(bool circular, params string[] puzzle)
    {
        var (board, given, count) = Parse(puzzle);
        return Solve(board, given, count, circular);
    }

    public int[,] Solve(bool circular, int[,] puzzle)
    {
        var (board, given, count) = Parse(puzzle);
        return Solve(board, given, count, circular);
    }

    private int[,] Solve(int[,] board, BitArray given, int count, bool circular)
    {
        var (height, width) = (board.GetLength(0), board.GetLength(1));
        bool solved = false;
        for (int x = 0; x < height && !solved; x++) {
            solved = Range(0, width).Any(y => Solve(board, given, circular, (height, width), (x, y), count, (x, y), 1));
            if (solved) return board;
        }
        return null;
    }

    private bool Solve(int[,] board, BitArray given, bool circular,
        (int h, int w) size, (int x, int y) start, int last, (int x, int y) current, int n)
    {
        var (x, y) = current;
        if (x < 0 || x >= size.h || y < 0 || y >= size.w) return false;
        if (board[x, y] < 0) return false;
        if (given[n - 1]) {
            if (board[x, y] != n) return false;
        } else if (board[x, y] > 0) return false;
        board[x, y] = n;
        if (n == last) {
            if (!circular || AreNeighbors(start, current)) return true;
        }
        for (int i = 0; i < moves.Length; i++) {
            var move = moves[i];
            if (Solve(board, given, circular, size, start, last, (x + move.dx, y + move.dy), n + 1)) return true;
        }
        if (!given[n - 1]) board[x, y] = 0;
        return false;

        bool AreNeighbors((int x, int y) p1, (int x, int y) p2) => moves.Any(m => (p2.x + m.dx, p2.y + m.dy).Equals(p1));
    }

    private static (int[,] board, BitArray given, int count) Parse(string[] input)
    {
        (int height, int width) = (input.Length, input[0].Length);
        int[,] board = new int[height, width];
        int count = 0;
        for (int x = 0; x < height; x++) {
            string line = input[x];
            for (int y = 0; y < width; y++) {
                board[x, y] = y < line.Length && char.IsDigit(line[y]) ? line[y] - '0' : -1;
                if (board[x, y] >= 0) count++;
            }
        }
        BitArray given = Scan(board, count, height, width);
        return (board, given, count);
    }

    private static (int[,] board, BitArray given, int count) Parse(int[,] input)
    {
        (int height, int width) = (input.GetLength(0), input.GetLength(1));
        int[,] board = new int[height, width];
        int count = 0;
        for (int x = 0; x < height; x++)
            for (int y = 0; y < width; y++)
                if ((board[x, y] = input[x, y]) >= 0) count++;
        BitArray given = Scan(board, count, height, width);
        return (board, given, count);
    }

    private static BitArray Scan(int[,] board, int count, int height, int width)
    {
        var given = new BitArray(count + 1);
        for (int x = 0; x < height; x++)
            for (int y = 0; y < width; y++)
                if (board[x, y] > 0) given[board[x, y] - 1] = true;
        return given;
    }

    private static void Print(int[,] board)
    {
        if (board == null) {
            WriteLine("No solution");
        } else {
            int w = board.Cast<int>().Where(i => i > 0).Max(i => (int?)Ceiling(Log10(i+1))) ?? 1;
            string e = new string('-', w);
            foreach (int x in Range(0, board.GetLength(0)))
                WriteLine(string.Join(" ", Range(0, board.GetLength(1))
                    .Select(y => board[x, y] < 0 ? e : board[x, y].ToString().PadLeft(w, ' '))));
        }
        WriteLine();
    }

}


/*REXX program solves a  Numbrix (R) puzzle, it also displays the puzzle and solution.  */
maxR=   0;     maxC=   0;          maxX=   0;    /*define  maxR,  maxC,  and  maxX.     */
minR= 9e9;     minC= 9e9;          minX= 9e9;    /*   "    minR,  minC,   "   minX.     */
cells=  0                                        /*the number of cells  (so far).       */
parse arg xxx                                    /*get the cell definitions from the CL.*/
xxx=translate(xxx,  ',,,,,'  ,  "/\;:_")         /*also allow other characters as comma.*/
@.=
           do  while xxx\='';       parse var  xxx    r c   marks  ','  xxx
               do  while marks\='';               [email protected].r.c
               parse var marks  x  marks
               if datatype(x, 'N')  then x= abs(x) / 1                /*normalize  │x│  */
               minR= min(minR, r);       minC= min(minC, c)           /*find min R and C*/
               maxR= max(maxR, r);       maxC= max(maxC, c)           /*  "  max "  "  "*/
               if x==1    then do;   !r= r;   !c= c                   /*the START cell. */
                               end
               if _\==''  then call err  "cell at"  r  c  'is already occupied with:'  _
               @.r.c= x;       c=  c +1;           cells= cells + 1   /*assign a mark.  */
               if x==.              then iterate                      /*is a 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 show                                        /* [↓]  is 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'            /*   "      column   "   "    "    "   */
pMoves= words(Nr)   -   4                        /*is this to be a Numbrix puzzle ?     */

     do i=1  for pMoves;    Nr.i= word(Nr, i);    Nc.i= word(Nc, i)   /*for fast moves. */
     end   /*i*/
say
if \next(2, !r, !c)  then call err   'No solution possible for this Numbrix puzzle.'
say 'A solution for the Numbrix puzzle exists.';          say;          call show
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
err:  say;   say '***error*** (from Numbrix puzzle): '    arg(1);       say;       exit 13
/*──────────────────────────────────────────────────────────────────────────────────────*/
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= #     /*let's try this move.  */
                            if #==cells          then return 1  /*is this the last move?*/
                            if next(##, nr, nc)  then return 1
                            @.nr.nc=.                           /*undo the above move.  */
                            iterate                             /*go & try another move.*/
                            end
        if @.nr.nc==#  then do                                  /*this a fill─in move ? */
                            if #==cells          then return 1  /*this is the last move.*/
                            if next(##, nr, nc)  then return 1  /*a fill─in move.       */
                           end
        end   /*t*/
      return 0                                                  /*this ain't working.   */
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: 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= max(2, 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


#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" );
}


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


# Loop over adjacent pairs in a list.
# Example:
#  % eachpair {a b} {1 2 3} {puts $a $b}
#  1 2
#  2 3
proc eachpair {varNames ls script} {
    if {[lassign $varNames _i _j] ne ""} {
        return -code error "Must supply exactly two arguments"
    }
    tailcall foreach $_i [lrange $ls 0 end-1] $_j [lrange $ls 1 end] $script
}

namespace eval numbrix {

    namespace path {::tcl::mathop ::tcl::mathfunc}

    proc parse {txt} {
        set map [split [string trim $txt] \n]
    }

    proc print {map} {
        join [lmap row $map {
            join [lmap val $row {
                format %2d $val
            }] " "
        }] \n
    }

    proc mark {map x y i} {
        lset map $x $y $i
    }

    proc moves {x y} {
        foreach {dx dy} {
                0  1
            -1 0      1 0
                0 -1
        } {
            lappend r [+ $dx $x] [+ $dy $y]
        }
        return $r
    }

    proc rmap {map} {   ;# generate a reverse map in a dict {val {x y} ..}
        set rmap {}
        set h [llength $map]
        set w [llength [lindex $map 0]]
        set x $w
        while {[incr x -1]>=0} {
            set y $h
            while {[incr y -1]>=0} {
                set i [lindex $map $x $y]
                if {$i} {
                    dict set rmap [lindex $map $x $y] [list $x $y]
                }
            }
        }
        return $rmap
    }

    proc gaps {rmap} {  ;# list all the gaps to be filled
        set known [lsort -integer [dict keys $rmap]]
        set gaps {}
        eachpair {i j} $known {
            if {$j > $i+1} {
                lappend gaps $i $j
            }
        }
        return $gaps
    }

    proc fixgaps {map rmap gaps} {  ;# add a "tail" gap if needed
        set w [llength $map]
        set h [llength [lindex $map 0]]
        set size [* $h $w]
        set max [max {*}[dict keys $rmap]]
        if {$max ne $size} {
            lappend gaps $max Inf
        }
        return $gaps
    }

    proc paths {map x0 y0 n} {  ;# generate all the maps with a single path filled legally
        if {$n == 0} {return [list $map]}
        set i [lindex $map $x0 $y0]
        set paths {}
        foreach {x y} [moves $x0 $y0] {
            set j [lindex $map $x $y]
            if {$j eq ""} {
                continue
            } elseif {$j == 0 && $n == $n+1} {
                return [list [mark $map $x $y [+ $i 1]]]
            } elseif {$j == $i+1} {
                lappend paths $map
                continue
            } elseif {$j || ($n == 1)} {
                continue
            } else {
                lappend paths {*}[
                    paths [
                        mark $map $x $y [+ $i 1]
                    ] $x $y [- $n 1]
                ]
            }
        }
        return $paths
    }

    proc solve {map} {
        # fixpoint map
        while 1 {   ;# first we iteratively fill in paths with distinct solutions
            set rmap [rmap $map]
            set gaps [gaps $rmap]
            set gaps [fixgaps $map $rmap $gaps]
            if {$gaps eq ""} {
                return $map
            }
            set oldmap $map
            foreach {i j} $gaps {
                lassign [dict get $rmap $i] x0 y0
                set n [- $j $i]
                set paths [paths $map $x0 $y0 $n]
                if {$paths eq ""} {
                    return ""
                } elseif {[llength $paths] == 1} {
                    #puts "solved $i..$j"
                    #puts [print $map]
                    set map [lindex $paths 0]
                }
                ;# we could intersect the paths to maybe get some tiles
            }
            if {$map eq $oldmap} {
                break
            }
        }
        #puts "unique paths exhausted - going DFS"
        try {   ;# for any left over paths, go DFS
            ;# we might want to sort the gaps first
            foreach {i j} $gaps {
                lassign [dict get $rmap $i] x0 y0
                set n [- $j $i]
                set paths [paths $map $x0 $y0 $n]
                foreach path $paths {
                    #puts "recursing on $i..$j"
                    set sol [solve $path]
                    if {$sol ne ""} {
                        return $sol
                    }
                }
            }
        }
    }

    namespace export {[a-z]*}
    namespace ensemble create
}

set puzzles {
    {
        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
    }

    {
        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
    }
}

foreach puzzle $puzzles {
    set map [numbrix parse $puzzle]
    puts "\n== Puzzle [incr i] =="
    puts [numbrix print $map]
    set sol [numbrix solve $map]
    if {$sol ne ""} {
        puts "\n== Solution $i =="
        puts [numbrix print $sol]
    } else {
        puts "\n== No Solution for Puzzle $i =="
    }
}


package main

import (
    "fmt"
    "sort"
)

var board = []string{
    ".00.00.",
    "0000000",
    "0000000",
    ".00000.",
    "..000..",
    "...0...",
}

var moves = [][2]int{
    {-3, 0}, {0, 3}, {3, 0}, {0, -3},
    {2, 2}, {2, -2}, {-2, 2}, {-2, -2},
}

var grid [][]int

var totalToFill = 0

func solve(r, c, count int) bool {
    if count > totalToFill {
        return true
    }
    nbrs := neighbors(r, c)
    if len(nbrs) == 0 && count != totalToFill {
        return false
    }
    sort.Slice(nbrs, func(i, j int) bool {
        return nbrs[i][2] < nbrs[j][2]
    })

    for _, nb := range nbrs {
        r = nb[0]
        c = nb[1]
        grid[r][c] = count
        if solve(r, c, count+1) {
            return true
        }
        grid[r][c] = 0
    }
    return false
}

func neighbors(r, c int) (nbrs [][3]int) {
    for _, m := range moves {
        x := m[0]
        y := m[1]
        if grid[r+y][c+x] == 0 {
            num := countNeighbors(r+y, c+x) - 1
            nbrs = append(nbrs, [3]int{r + y, c + x, num})
        }
    }
    return
}

func countNeighbors(r, c int) int {
    num := 0
    for _, m := range moves {
        if grid[r+m[1]][c+m[0]] == 0 {
            num++
        }
    }
    return num
}

func printResult() {
    for _, row := range grid {
        for _, i := range row {
            if i == -1 {
                fmt.Print("   ")
            } else {
                fmt.Printf("%2d ", i)
            }
        }
        fmt.Println()
    }
}

func main() {
    nRows := len(board) + 6
    nCols := len(board[0]) + 6
    grid = make([][]int, nRows)
    for r := 0; r < nRows; r++ {
        grid[r] = make([]int, nCols)
        for c := 0; c < nCols; c++ {
            grid[r][c] = -1
        }
        for c := 3; c < nCols-3; c++ {
            if r >= 3 && r < nRows-3 {
                if board[r-3][c-3] == '0' {
                    grid[r][c] = 0
                    totalToFill++
                }
            }
        }
    }
    pos, r, c := -1, 0, 0
    for {
        for {
            pos++
            r = pos / nCols
            c = pos % nCols
            if grid[r][c] != -1 {
                break
            }
        }
        grid[r][c] = 1
        if solve(r, c, 2) {
            break
        }
        grid[r][c] = 0
        if pos >= nRows*nCols {
            break
        }
    }
    printResult()
}


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


     // Solve Hidato/Hopido/Numbrix puzzles
class Puzzle{  // hold info concerning this puzzle
   var board, nrows,ncols, cells,
       start,      // (r,c) where 1 is located, Void if no 1
       terminated, // if board holds highest numbered cell
       given,	   // all the pre-loaded cells
       adj,        // a list of (r,c) that are valid next cells
       ;

   fcn print_board{
      d:=Dictionary(-1,"  ", 0,"__");
      foreach r in (board){
	 r.pump(String,'wrap(c){ "%2s ".fmt(d.find(c,c)) }).println();
      }
   }
   fcn init(s,adjacent){
      adj=adjacent;
      lines:=s.split("\n");
      ncols,nrows=lines[0].split().len(),lines.len();
      board=nrows.pump(List(), ncols.pump(List(),-1).copy);
      given,start=List(),Void;
      cells,terminated=0,True;
      foreach r,row in (lines.enumerate()){
	 foreach c,cell in (row.split().enumerate()){
	    if(cell=="X") continue;   // X == not in play, leave at -1
	    cells+=1;
	    val:=cell.toInt();
	    board[r][c]=val;
	    given.append(val);
	    if(val==1) start=T(r,c);
	 }
      }
      println("Number of cells = ",cells);
      if(not given.holds(cells)){ given.append(cells); terminated=False; }
      given=given.filter().sort();
   }
   fcn solve{   //-->Bool
      if(start) return(_solve(start.xplode()));
      foreach r,c in (nrows,ncols){
	 if(board[r][c]==0 and _solve(r,c)) return(True);
      }
      False
   }
   fcn [private] _solve(r,c,n=1, next=0){
      if(n>given[-1])                       		   return(True);
      if(not ( (0<=r<nrows) and (0<=c<ncols) ))		   return(False);
      if(board[r][c] and board[r][c]!=n)                   return(False);
      if(terminated and board[r][c]==0 and given[next]==n) return(False);

      back:=0;
      if(board[r][c]==n){ next+=1; back=n; }

      board[r][c]=n;
      foreach i,j in (adj){ if(self.fcn(r+i,c+j,n+1, next)) return(True) }
      board[r][c]=back;
      False
   }
} // Puzzle


import java.util.*;

public class Hopido {

    final static String[] board = {
        ".00.00.",
        "0000000",
        "0000000",
        ".00000.",
        "..000..",
        "...0..."};

    final static int[][] moves = {{-3, 0}, {0, 3}, {3, 0}, {0, -3},
    {2, 2}, {2, -2}, {-2, 2}, {-2, -2}};
    static int[][] grid;
    static int totalToFill;

    public static void main(String[] args) {
        int nRows = board.length + 6;
        int nCols = board[0].length() + 6;

        grid = new int[nRows][nCols];

        for (int r = 0; r < nRows; r++) {
            Arrays.fill(grid[r], -1);
            for (int c = 3; c < nCols - 3; c++)
                if (r >= 3 && r < nRows - 3) {
                    if (board[r - 3].charAt(c - 3) == '0') {
                        grid[r][c] = 0;
                        totalToFill++;
                    }
                }
        }

        int pos = -1, r, c;
        do {
            do {
                pos++;
                r = pos / nCols;
                c = pos % nCols;
            } while (grid[r][c] == -1);

            grid[r][c] = 1;
            if (solve(r, c, 2))
                break;
            grid[r][c] = 0;

        } while (pos < nRows * nCols);

        printResult();
    }

    static boolean solve(int r, int c, int count) {
        if (count > totalToFill)
            return true;

        List<int[]> nbrs = neighbors(r, c);

        if (nbrs.isEmpty() && count != totalToFill)
            return false;

        Collections.sort(nbrs, (a, b) -> a[2] - b[2]);

        for (int[] nb : nbrs) {
            r = nb[0];
            c = nb[1];
            grid[r][c] = count;
            if (solve(r, c, count + 1))
                return true;
            grid[r][c] = 0;
        }

        return false;
    }

    static List<int[]> neighbors(int r, int c) {
        List<int[]> nbrs = new ArrayList<>();

        for (int[] m : moves) {
            int x = m[0];
            int y = m[1];
            if (grid[r + y][c + x] == 0) {
                int num = countNeighbors(r + y, c + x) - 1;
                nbrs.add(new int[]{r + y, c + x, num});
            }
        }
        return nbrs;
    }

    static int countNeighbors(int r, int c) {
        int num = 0;
        for (int[] m : moves)
            if (grid[r + m[1]][c + m[0]] == 0)
                num++;
        return num;
    }

    static void printResult() {
        for (int[] row : grid) {
            for (int i : row) {
                if (i == -1)
                    System.out.printf("%2s ", ' ');
                else
                    System.out.printf("%2d ", i);
            }
            System.out.println();
        }
    }
}


using .Hidato       # Note that the . here means to look locally for the module rather than in the libraries

const hopid = """
 . 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 . . . """

const hopidomoves = [[-3, 0], [0, -3], [-2, -2], [-2, 2], [2, -2], [0, 3], [3, 0], [2, 2]]

board, maxmoves, fixed, starts = hidatoconfigure(hopid)
printboard(board, " 0", "  ")
hidatosolve(board, maxmoves, hopidomoves, fixed, starts[1][1], starts[1][2], 1)
printboard(board)


// version 1.2.0

val board = listOf(
    ".00.00.",
    "0000000",
    "0000000",
    ".00000.",
    "..000..",
    "...0..."
)

val moves = listOf(
    -3 to 0, 0 to  3,  3 to 0,  0 to -3,
     2 to 2, 2 to -2, -2 to 2, -2 to -2
)

lateinit var grid: List<IntArray>
var totalToFill = 0

fun solve(r: Int, c: Int, count: Int): Boolean {
    if (count > totalToFill) return true
    val nbrs = neighbors(r, c)
    if (nbrs.isEmpty() && count != totalToFill) return false
    nbrs.sortBy { it[2] }
    for (nb in nbrs) {
        val rr = nb[0]
        val cc = nb[1]
        grid[rr][cc] = count
        if (solve(rr, cc, count + 1)) return true
        grid[rr][cc] = 0
    }
    return false
}

fun neighbors(r: Int, c: Int): MutableList<IntArray> {
    val nbrs = mutableListOf<IntArray>()
    for (m in moves) {
        val x = m.first
        val y = m.second
        if (grid[r + y][c + x] == 0) {
            val num = countNeighbors(r + y, c + x) - 1
            nbrs.add(intArrayOf(r + y, c + x, num))
        }
    }
    return nbrs
}

fun countNeighbors(r: Int, c: Int): Int {
    var num = 0
    for (m in moves)
        if (grid[r + m.second][c + m.first] == 0) num++
    return num
}

fun printResult() {
    for (row in grid) {
        for (i in row) {
            print(if (i == -1) "   " else "%2d ".format(i))
        }
        println()
    }
}

fun main(args: Array<String>) {
    val nRows = board.size + 6
    val nCols = board[0].length + 6
    grid = List(nRows) { IntArray(nCols) { -1} }
    for (r in 0 until nRows) {
        for (c in 3 until nCols - 3) {
            if (r in 3 until nRows - 3) {
                if (board[r - 3][c - 3] == '0') {
                    grid[r][c] = 0
                    totalToFill++
                }
            }
        }
    }
    var pos = -1
    var rr: Int
    var cc: Int
    do {
        do {
            pos++
            rr = pos / nCols
            cc = pos % nCols
        }
        while (grid[rr][cc] == -1)

        grid[rr][cc] = 1
        if (solve(rr, cc, 2)) break
        grid[rr][cc] = 0
    }
    while (pos < nRows * nCols)

    printResult()
}


puzz = ".00.00.\n0000000\n0000000\n.00000.\n..000..\n...0...";
puzz //= StringSplit[#, "\n"] & /* Map[Characters];
puzz //= Transpose /* Map[Reverse];
pos = Position[puzz, "0", {2}];
moves = Select[Select[Tuples[pos, 2], MatchQ[EuclideanDistance @@ #, 2 Sqrt[2] | 3] &], OrderedQ];
g = Graph[UndirectedEdge @@@ moves];
ord = Most[FindShortestTour[g][[2]]];
Graphics[MapThread[Text, {Range[Length[ord]], VertexList[g][[ord]]}]]


import algorithm, sequtils, strformat

const Moves = [(-3, 0), (0,  3), ( 3, 0), ( 0, -3),
               ( 2, 2), (2, -2), (-2, 2), (-2, -2)]

type

  Hopido = object
    grid: seq[seq[int]]
    nRows, nCols: int
    totalToFill : Natural

  Neighbor = (int, int, int)

proc initHopido(board: openArray[string]): Hopido =
  result.nRows = board.len + 6
  result.nCols = board[0].len + 6
  result.grid = newSeqWith(result.nRows, repeat(-1, result.nCols))

  for row in 0..board.high:
    for col in 0..board[0].high:
      if board[row][col] == '0':
        result.grid[row + 3][col + 3] = 0
        inc result.totalToFill

proc countNeighbors(hopido: Hopido; row, col: Natural): int =
  for (x, y) in Moves:
    if hopido.grid[row + y][col + x] == 0:
      inc result

proc neighbors(hopido: Hopido; row, col: Natural): seq[Neighbor] =
  for (x, y) in Moves:
    if hopido.grid[row + y][col + x] == 0:
      let num = hopido.countNeighbors(row + y, col + x) - 1
      result.add (row + y, col + x, num)

proc solve(hopido: var Hopido; row, col, count: Natural): bool =

  if count > hopido.totalTofill: return true

  var nbrs = hopido.neighbors(row, col)
  if nbrs.len == 0 and count != hopido.totalToFill:
    return false

  nbrs.sort(proc(a, b: Neighbor): int = cmp(a[2], b[2]))

  for (row, col, _) in nbrs:
    hopido.grid[row][col] = count
    if hopido.solve(row, col, count + 1):
      return true
    hopido.grid[row][col] = 0

proc findSolution(hopido: var Hopido) =
  var pos = -1
  var row, col: Natural

  while true:
    while true:
      inc pos
      row = pos div hopido.nCols
      col = pos mod hopido.nCols
      if hopido.grid[row][col] != -1:
        break
    hopido.grid[row][col] = 1
    if hopido.solve(row, col, 2):
      break
    hopido.grid[row][col] = 0
    if pos >= hopido.nRows * hopido.nCols:
      break

proc print(hopido: Hopido) =
  for row in hopido.grid:
    for val in row:
      stdout.write if val == -1: "   " else: &"{val:2} "
    echo()

when isMainModule:

  const Board = [".00.00.",
                 "0000000",
                 "0000000",
                 ".00000.",
                 "..000..",
                 "...0..."]

  var hopido = initHopido(Board)
  hopido.findSolution()
  hopido.print()


#!/usr/bin/perl

use strict;             # http://www.rosettacode.org/wiki/Solve_a_Hopido_puzzle
use warnings;

$_ = do { local $/; <DATA> };
s/./$&$&/g;             # double chars
my $w = /\n/ && $-[0];
my $wd = 3 * $w + 1;    # vertical gap
my $wr = 2 * $w + 8;    # down right gap
my $wl = 2 * $w - 8;    # down left gap
place($_, '00');
die "No solution\n";

sub place
  {
  (local $_, my $last) = @_;
  (my $new = $last)++;
  /$last.{10}(?=00)/g   and place( s/\G00/$new/r, $new ); # right
  /(?=00.{10}$last)/g   and place( s/\G00/$new/r, $new ); # left
  /$last.{$wd}(?=00)/gs and place( s/\G00/$new/r, $new ); # down
  /(?=00.{$wd}$last)/gs and place( s/\G00/$new/r, $new ); # up
  /$last.{$wr}(?=00)/gs and place( s/\G00/$new/r, $new ); # down right
  /(?=00.{$wr}$last)/gs and place( s/\G00/$new/r, $new ); # up left
  /$last.{$wl}(?=00)/gs and place( s/\G00/$new/r, $new ); # down left
  /(?=00.{$wl}$last)/gs and place( s/\G00/$new/r, $new ); # up right
  /00/ and return;
  print "Solution\n\n", s/  / /gr =~ s/0\B/ /gr;
  exit;
  }

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


with javascript_semantics
sequence board

integer limit, tries

constant ROW = 1, COL = 2
constant moves = {{-2,-2},{-2,2},{2,-2},{2,2},{-3,0},{3,0},{0,-3},{0,3}}

function solve(integer row, integer col, integer n)
integer nrow, ncol
    tries+= 1
    if n>limit then return 1 end if
    for move=1 to length(moves) do
        nrow = row+moves[move][ROW]
        ncol = col+moves[move][COL]*3
        if nrow>=1 and nrow<=length(board)
        and ncol>=1 and ncol<=length(board[row])
        and board[nrow][ncol]=' ' then
            board[nrow][ncol-1..ncol] = sprintf("%2d",n)
            if solve(nrow,ncol,n+1) then return 1 end if
            board[nrow][ncol-1..ncol] = "  "
        end if
    end for
    return 0
end function

procedure Hopido(sequence s, integer w, integer h)
    integer rx, ry
    atom t0 = time()
    board = split(s,'\n')
    limit = 0
    for x=1 to h do
        for y=3 to w*3 by 3 do
            if board[x][y]='0' then
                board[x][y] = ' '
                limit += 1
            end if
        end for
    end for
    while 1 do
        rx = rand(h)
        ry = rand(w)*3
        if board[rx][ry]=' ' then exit end if
    end while
    board[rx][ry] = '1'
    tries = 0
    if solve(rx,ry,2) then
        puts(1,join(board,"\n"))
        printf(1,"\nsolution found in %d tries (%3.2fs)\n",{tries,time()-t0})
    else
        puts(1,"no solutions found\n")
    end if
end procedure

constant board1 = """
  .  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  .  .  ."""
Hopido(board1,7,6)


import sat.
main =>
    Grid = {{0,1,1,0,1,1,0},
	    {1,1,1,1,1,1,1},
	    {1,1,1,1,1,1,1},
	    {0,1,1,1,1,1,0},
	    {0,0,1,1,1,0,0},
	    {0,0,0,1,0,0,0}},
    NR = len(Grid), NC = len(Grid[1]),
    Es = [{(R,C), (R1,C1), _} : R in 1..NR, C in 1..NC, R1 in 1..NR, C1 in 1..NC, % Edges
          ((R1 = R, abs(C1 - C) = 3); (C1 = C, abs(R1 - R) = 3); % horizontal hop
           (abs(R1 - R) = 2, abs(C1 - C) = 2)),                  % diagonal hop
          Grid[R,C] = 1, Grid[R1,C1] = 1],
    hcp_grid(Grid, Es), % find a Hamiltion cylce on the vertices in Grid through the edges in Es
    solve(vars(Es)),
    % Write the solution as a matrix, starting at the base tile 6|4:
    M = {{0: _ in 1..NC} : _ in 1..NR},
    R1 = 6, C1 = 4, I = 0,
    do
        I := I + 1, M[R1,C1] := I, Stop = 0,
        foreach({(R1,C1), (R2,C2), 1} in Es,  break(Stop == 1))
            R1 := R2, C1 := C2, Stop := 1
        end
    while ((R1,C1) != (6,4)),
    foreach(R in 1..NR, C in 1..NC)
        if M[R,C] = 0 then print("   ") else printf("%2d ", M[R,C]) end,
        if C = NC then nl end
    end.


hopido(Grid,[C|Solved],Xs,Ys) :-
	select(C,Grid,RGrid),
	solve(RGrid,C,Solved,Xs,Ys).

solve([],_,[],_,_).
solve(Grid,p(X,Y),[p(X1,Y1)|R],Xs,Ys) :-
	valid_move(X,Y,Xs,Ys,X1,Y1),
	select(p(X1,Y1),Grid,NextGrid),
	solve(NextGrid,p(X1,Y1),R,Xs,Ys).

valid_move(X,Y,Xs,_,X1,Y)   :- j3(X,X1,Xs).			% right	(3,0)
valid_move(X,Y,Xs,_,X1,Y)   :- j3(X1,X,Xs).			% left (-3,0)
valid_move(X,Y,_,Ys,X,Y1)   :- j3(Y,Y1,Ys).			% up (0,3).
valid_move(X,Y,_,Ys,X,Y1)   :- j3(Y1,Y,Ys).  			% down (0,-3).
valid_move(X,Y,Xs,Ys,X1,Y1) :- j2(X,X1,Xs), j2(Y,Y1,Ys).  	% up-right (2,2).
valid_move(X,Y,Xs,Ys,X1,Y1) :- j2(X1,X,Xs), j2(Y,Y1,Ys).  	% up-left (-2,2).
valid_move(X,Y,Xs,Ys,X1,Y1) :- j2(X1,X,Xs), j2(Y1,Y,Ys).  	% down-left (-2,-2).
valid_move(X,Y,Xs,Ys,X1,Y1) :- j2(X,X1,Xs), j2(Y1,Y,Ys).  	% down-right (2,-2).

j2(O,N,[O,_,N|_]).
j2(O,N,[_|T]) :- j2(O,N,T).

j3(O,N,[O,_,_,N|_]).
j3(O,N,[_|T]) :- j3(O,N,T).


from sys import stdout

neighbours = [[2, 2], [-2, 2], [2, -2], [-2, -2], [3, 0], [0, 3], [-3, 0], [0, -3]]
cnt = 0
pWid = 0
pHei = 0

def is_valid(a, b):
    return -1 < a < pWid and -1 < b < pHei

def iterate(pa, x, y, v):
    if v > cnt:
        return 1

    for i in range(len(neighbours)):
        a = x + neighbours[i][0]
        b = y + neighbours[i][1]
        if is_valid(a, b) and pa[a][b] == 0:
            pa[a][b] = v
            r = iterate(pa, a, b, v + 1)
            if r == 1:
                return r
            pa[a][b] = 0
    return 0

def solve(pz, w, h):
    global cnt, pWid, pHei

    pa = [[-1 for j in range(h)] for i in range(w)]
    f = 0
    pWid = w
    pHei = h
    for j in range(h):
        for i in range(w):
            if pz[f] == "1":
                pa[i][j] = 0
                cnt += 1
            f += 1

    for y in range(h):
        for x in range(w):
            if pa[x][y] == 0:
                pa[x][y] = 1
                if 1 == iterate(pa, x, y, 2):
                    return 1, pa
                pa[x][y] = 0

    return 0, pa

r = solve("011011011111111111111011111000111000001000", 7, 6)
if r[0] == 1:
    for j in range(6):
        for i in range(7):
            if r[1][i][j] == -1:
                stdout.write("   ")
            else:
                stdout.write(" {:0{}d}".format(r[1][i][j], 2))
        print()
else:
    stdout.write("No solution!")


#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 _ _ _)))))


my @adjacent = [3, 0],
      [2, -2],         [2, 2],
   [0, -3],                [0, 3],
      [-2, -2],        [-2, 2],
               [-3, 0];

solveboard q:to/END/;
    . _ _ . _ _ .
    _ _ _ _ _ _ _
    _ _ _ _ _ _ _
    . _ _ _ _ _ .
    . . _ _ _ . .
    . . . 1 . . .
    END

sub solveboard($board) {
    my $max = +$board.comb(/\w+/);
    my $width = $max.chars;

    my @grid;
    my @known;
    my @neigh;
    my @degree;

    @grid = $board.lines.map: -> $line {
        [ $line.words.map: { /^_/ ?? 0 !! /^\./ ?? Rat !! $_ } ]
    }

    sub neighbors($y,$x --> List) {
        eager gather for @adjacent {
            my $y1 = $y + .[0];
            my $x1 = $x + .[1];
            take [$y1,$x1] if defined @grid[$y1][$x1];
        }
    }

    for ^@grid -> $y {
        for ^@grid[$y] -> $x {
            if @grid[$y][$x] -> $v {
                @known[$v] = [$y,$x];
            }
            if @grid[$y][$x].defined {
                @neigh[$y][$x] = neighbors($y,$x);
                @degree[$y][$x] = +@neigh[$y][$x];
            }
        }
    }
    print "\e[0H\e[0J";

    my $tries = 0;

    try_fill 1, @known[1];

    sub try_fill($v, $coord [$y,$x] --> Bool) {
        return True if $v > $max;
        $tries++;

        my $old = @grid[$y][$x];

        return False if +$old and $old != $v;
        return False if @known[$v] and @known[$v] !eqv $coord;

        @grid[$y][$x] = $v;               # conjecture grid value

        print "\e[0H";                    # show conjectured board
        for @grid -> $r {
            say do for @$r {
                when Rat { ' ' x $width }
                when 0   { '_' x $width }
                default  { .fmt("%{$width}d") }
            }
        }

        my @neighbors = @neigh[$y][$x][];

        my @degrees;
        for @neighbors -> \n [$yy,$xx] {
            my $d = --@degree[$yy][$xx];  # conjecture new degrees
            push @degrees[$d], n;         # and categorize by degree
        }

        for @degrees.grep(*.defined) -> @ties {
            for @ties.reverse {           # reverse works better for this hidato anyway
                return True if try_fill $v + 1, $_;
            }
        }

        for @neighbors -> [$yy,$xx] {
            ++@degree[$yy][$xx];          # undo degree conjectures
        }

        @grid[$y][$x] = $old;             # undo grid value conjecture
        return False;
    }

    say "$tries tries";
}


/*REXX program solves a Hopido puzzle,  it also displays the puzzle  and  the solution. */
call time 'Reset'                                /*reset the REXX elapsed timer to zero.*/
maxR=0;    maxC=0;    maxX=0;     minR=9e9;      minC=9e9;    minX=9e9;    cells=0;    @.=
parse arg xxx                                    /*get the cell definitions from the CL.*/
xxx=translate(xxx, , "/\;:_", ',')               /*also allow other characters as comma.*/

               do  while xxx\='';       parse var  xxx    r  c  marks  ','  xxx
                   do  while marks\='';               [email protected].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              /*the START cell. */
                   if _\=='' then call err "cell at"  r  c  'is already occupied with:' _
                   @.r.c=x;   c=c+1;    cells=cells+1                 /*assign a mark.  */
                   if x==.              then iterate                  /*is a hole?  Skip*/
                   if \datatype(x,'W')  then call err 'illegal marker specified:' x
                   minX=min(minX,x);    maxX=max(maxX,x)              /*min and max  X. */
                   end   /*while marks¬='' */
               end       /*while xxx  ¬='' */
call show                                        /* [↓]  is 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'            /*   "      column   "   "    "    "   */
pMoves=words(Nr)                                 /*the number of possible moves.  */
                   do i=1  for pMoves;   Nr.i=word(Nr, i);   Nc.i=word(Nc,i);   end  /*i*/
if \next(2,!r,!c)  then call err  'No solution possible for this Hopido puzzle.'
say 'A solution for the Hopido exists.';      say;               call show
etime= format(time('Elapsed'), , 2)              /*obtain the elapsed time (in seconds).*/
if etime<.1  then say 'and took less than  1/10  of a second.'
             else say 'and took'       etime         "seconds."
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
err:  say;      say '***error*** (from Hopido): '  arg(1);          say;           exit 13
/*──────────────────────────────────────────────────────────────────────────────────────*/
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=#     /*let's try this move.  */
                               if #==cells        then leave    /*is this the last move?*/
                               if next(##,nr,nc)  then return 1
                               @.nr.nc=.                        /*undo the above move.  */
                               iterate                          /*go & try another move.*/
                               end
           if @.nr.nc==#  then do                               /*this a fill-in move ? */
                               if #==cells        then return 1 /*this is the last move.*/
                               if next(##,nr,nc)  then return 1 /*a fill-in move.       */
                               end
           end   /*t*/
return 0                                                        /*This ain't working.   */
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: 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=max(2,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


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"


import "/sort" for Sort
import "/fmt" for Fmt

var board = [
    ".00.00.",
    "0000000",
    "0000000",
    ".00000.",
    "..000..",
    "...0..."
]

var moves = [
    [-3, 0], [0,  3], [ 3, 0], [ 0, -3],
    [ 2, 2], [2, -2], [-2, 2], [-2, -2]
]

var grid = []
var totalToFill = 0

var countNeighbors = Fn.new { |r, c|
    var num = 0
    for (m in moves) if (grid[r + m[1]][c + m[0]] == 0) num = num + 1
    return num
}

var neighbors = Fn.new { |r, c|
    var nbrs = []
    for (m in moves) {
        var x = m[0]
        var y = m[1]
        if (grid[r + y][c + x] == 0) {
            var num = countNeighbors.call(r + y, c + x) - 1
            nbrs.add([r + y, c + x, num])
        }
    }
    return nbrs
}

var solve // recursive
solve = Fn.new { |r, c, count|
    if (count > totalToFill) return true
    var nbrs = neighbors.call(r, c)
    if (nbrs.isEmpty && count != totalToFill) return false
    var cmp = Fn.new { |n1, n2| (n1[2] - n2[2]).sign }
    Sort.insertion(nbrs, cmp) // stable sort
    for (nb in nbrs) {
        var rr = nb[0]
        var cc = nb[1]
        grid[rr][cc] = count
        if (solve.call(rr, cc, count + 1)) return true
        grid[rr][cc] = 0
    }
    return false
}

var printResult = Fn.new {
    for (row in grid) {
        for (i in row) {
            if (i == -1) {
                System.write("   ")
            } else {
                Fmt.write("$2d ", i)
            }
        }
        System.print()
    }
}

var nRows = board.count + 6
var nCols = board[0].count + 6
grid = List.filled(nRows, null)
for (r in 0...nRows) {
    grid[r] = List.filled(nCols, -1)
    for (c in 3...nCols - 3) {
        if (r >= 3 && r < nRows - 3) {
            if (board[r - 3][c - 3] == "0") {
                grid[r][c] = 0
                totalToFill = totalToFill + 1
            }
        }
    }
}
var pos = -1
var r
var c
while (true) {
    while (true) {
        pos = pos + 1
        r = (pos / nCols).truncate
        c = pos % nCols
        if (grid[r][c] != -1) break
    }
    grid[r][c] = 1
    if (solve.call(r, c, 2)) break
    grid[r][c] = 0
    if (pos >= nRows * nCols) break
}
printResult.call()


[[Int]] board
[Int] given
V start = (-1, -1)

F setup(s)
   V lines = s.split("\n")
   V ncols = lines[0].split(‘ ’, group_delimiters' 1B).len
   V nrows = lines.len
   :board = (0 .< nrows + 2).map(_ -> [-1] * (@ncols + 2))

   L(row) lines
      V r = L.index
      L(cell) row.split(‘ ’, group_delimiters' 1B)
         V c = L.index
         I cell == ‘__’
            :board[r + 1][c + 1] = 0
            L.continue
         E I cell == ‘.’
            L.continue
         E
            V val = Int(cell)
            :board[r + 1][c + 1] = val
            :given.append(val)
            I val == 1
               :start = (r + 1, c + 1)
   :given.sort()

F solve(r, c, n, =next = 0)
   I n > :given.last
      R 1B
   I :board[r][c] & :board[r][c] != n
      R 0B
   I :board[r][c] == 0 & :given[next] == n
      R 0B
   V back = 0
   I :board[r][c] == n
      next++
      back = n
   :board[r][c] = n
   L(i) -1 .< 2
      L(j) -1 .< 2
         I solve(r + i, c + j, n + 1, next)
            R 1B
   :board[r][c] = back
   R 0B

F print_board()
   V d = [-1 = ‘  ’, 0 = ‘__’]
   V bmax = max(:board.map(r -> max(r)))
   V lbmax = String(bmax).len + 1
   L(r) :board[1 .< (len)-1]
      print(r[1 .< (len)-1].map(c -> @d.get(c, String(c)).rjust(@lbmax)).join(‘’))

V hi =
|‘__ 33 35 __ __  .  .  .
  __ __ 24 22 __  .  .  .
  __ __ __ 21 __ __  .  .
  __ 26 __ 13 40 11  .  .
  27 __ __ __  9 __  1  .
   .  . __ __ 18 __ __  .
   .  .  .  . __  7 __ __
   .  .  .  .  .  .  5 __’

setup(hi)
print_board()
solve(start[0], start[1], 1)
print()
print_board()


SolveHidato(Grid, Locked, Max, row, col, num:=1, R:="", C:=""){
	if (R&&C)							; if neighbors (not first iteration)
	{
		Grid[R, C] := ">" num 					; place num in current neighbor and mark it visited ">"
		row:=R, col:=C						; move to current neighbor
	}

	num++								; increment num
	if (num=max)							; if reached end
		return map(Grid)					; return solution

	if locked[num]							; if current num is a locked value
	{
		row := StrSplit((StrSplit(locked[num], ",").1) , ":").1	; find row of num
		col := StrSplit((StrSplit(locked[num], ",").1) , ":").2	; find col of num
		if SolveHidato(Grid, Locked, Max, row, col, num)	; solve for current location and value
			return map(Grid)				; if solved, return solution
	}
	else
	{
		for each, value in StrSplit(Neighbor(row,col), ",")
		{
			R := StrSplit(value, ":").1
			C := StrSplit(value, ":").2

			if (Grid[R,C] = "")				; a hole or out of bounds
			|| InStr(Grid[R, C], ">")			; visited
			|| Locked[num+1] && !(Locked[num+1]~= "\b" R ":" C "\b") ; not neighbor of locked[num+1]
			|| Locked[num-1] && !(Locked[num-1]~= "\b" R ":" C "\b") ; not neighbor of locked[num-1]
			|| Locked[num]					; locked value
			|| Locked[Grid[R, C]]				; locked cell
				continue

			if SolveHidato(Grid, Locked, Max, row, col, num, R, C)	; solve for current location, neighbor and value
				return map(Grid)			; if solved, return solution
		}
	}
	num--								; step back
	for i, line in Grid
		for j, element in line
			if InStr(element, ">") && (StrReplace(element, ">") >= num)
				Grid[i, j] := "Y"
}
;--------------------------------
;--------------------------------
;--------------------------------
Neighbor(row,col){
	R := row-1
	loop, 9
	{
		DeltaC := Mod(A_Index, 3) ? Mod(A_Index, 3)-2	: 1
		res .= (R=row && !DeltaC) ? "" : R ":" col+DeltaC ","
		R := Mod(A_Index, 3) ? R : R+1
	}
	return Trim(res, ",")
}
;--------------------------------
map(Grid){
	for i, row in Grid
	{
		for j, element in row
			line .= (A_Index > 1 ? "`t" : "") . element
		map .= (map<>""?"`n":"") line
		line := ""
	}
	return StrReplace(map, ">")
}


(
  ( hidato
  =     Line solve lowest Ncells row column rpad
      , Board colWidth maxDigits start curCol curRow
      , range head line cellN solution output tail
    .   out$!arg
      & @(!arg:? ((%@:>" ") ?:?arg))
      & 0:?row:?column
      & :?Board
      & ( Line
        =   token
          .   whl
            ' ( @(!arg:?token [3 ?arg)
              & (   (   @(!token:? "_" ?)
                      & :?token
                    | @(!token:? #?token (|" " ?))
                    )
                  & (!token.!row.!column) !Board:?Board
                |
                )
              & 1+!column:?column
              )
        )
      &   whl
        ' ( @(!arg:?line \n ?arg)
          & Line$!line
          & 1+!row:?row
          & 0:?column
          )
      & Line$!arg
      & ( range
        =   hi lo
          .   (!arg+1:?hi)+-2:?lo
            & '($lo|$arg|$hi)
        )
      & ( solve
        =     ToDo cellN row column head tail remainder
            , candCell Solved rowCand colCand pattern recurse
          .   !arg:(?ToDo.?cellN.?row.?column)
            & range$!row:(=?row)
            & range$!column:(=?column)
            &
                ' (     ?head ($cellN.?rowCand.?colCand) ?tail
                      & (!rowCand.!colCand):($row.$column)
                      & !recurse
                    |   ?head
                        (.($row.$column):(?rowCand.?colCand))
                        (?tail&!recurse)
                  .     ((!rowCand.!colCand).$cellN)
                      : ?candCell
                    &   (   !head !tail:
                          & out$found!
                          & !candCell
                        |       solve
                              $ ( !head !tail
                                . $cellN+1
                                . !rowCand
                                . !colCand
                                )
                            : ?remainder
                          & !candCell+!remainder
                        )
                      : ?Solved
                  )
              : (=?pattern.?recurse)
            & !ToDo:!pattern
            & !Solved
        )
      & infinity:?lowest
      & (   !Board
          : ? (<!lowest:#%?lowest.?start) (?&~)
        | solve$(!Board.!lowest.!start):?solution
        )
      & :?output
      & 0:?curCol
      & !solution:((?curRow.?).?)+?+[?Ncells
      & @(!Ncells:? [?maxDigits)
      & 1+!maxDigits:?colWidth
      & ( rpad
        =   len
          .   !arg:(?arg.?len)
            & @(str$(!arg "    "):?arg [!len ?)
            & !arg
        )
      &   whl
        ' ( !solution:((?row.?column).?cellN)+?solution
          & (   !row:>!curRow:?curRow
              & !output \n:?output
              & 0:?curCol
            |
            )
          &   whl
            ' ( !curCol+1:~>!column:?curCol
              & !output rpad$(.!colWidth):?output
              )
          &   !output rev$(rpad$(rev$(str$(!cellN " ")).!colWidth))
            : ?output
          & !curCol+1:?curCol
          )
      & str$!output
  )
&   "
 __ 33 35 __ __
 __ __ 24 22 __
 __ __ __ 21 __ __
 __ 26 __ 13 40 11
 27 __ __ __  9 __  1
       __ __ 18 __ __
             __  7 __ __
                    5 __"
  : ?board
& out$(hidato$!board)
);


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int *board, *flood, *known, top = 0, w, h;

static inline int idx(int y, int x) { return y * w + x; }

int neighbors(int c, int *p)
/*
@c cell
@p list of neighbours
@return amount of neighbours
*/
{
	int i, j, n = 0;
	int y = c / w, x = c % w;

	for (i = y - 1; i <= y + 1; i++) {
		if (i < 0 || i >= h) continue;
		for (j = x - 1; j <= x + 1; j++)
			if (!(j < 0 || j >= w
				|| (j == x && i == y)
				|| board[ p[n] = idx(i,j) ] == -1))
				n++;
	}

	return n;
}

void flood_fill(int c)
/*
fill all free cells around @c with “1” and write output to variable “flood”
@c cell
*/
{
	int i, n[8], nei;

	nei = neighbors(c, n);
	for (i = 0; i < nei; i++) { // for all neighbours
		if (board[n[i]] || flood[n[i]]) continue; // if cell is not free, choose another neighbour

		flood[n[i]] = 1;
		flood_fill(n[i]);
	}
}

/* Check all empty cells are reachable from higher known cells.
   Should really do more checks to make sure cell_x and cell_x+1
   share enough reachable empty cells; I'm lazy. Will implement
   if a good counter example is presented. */
int check_connectity(int lowerbound)
{
	int c;
	memset(flood, 0, sizeof(flood[0]) * w * h);
	for (c = lowerbound + 1; c <= top; c++)
		if (known[c]) flood_fill(known[c]); // mark all free cells around known cells

	for (c = 0; c < w * h; c++)
		if (!board[c] && !flood[c]) // if there are free cells which could not be reached from flood_fill
			return 0;

	return 1;
}

void make_board(int x, int y, const char *s)
{
	int i;

	w = x, h = y;
        top = 0;
	x = w * h;

        known = calloc(x + 1, sizeof(int));
        board = calloc(x,     sizeof(int));
        flood = calloc(x,     sizeof(int));

	while (x--) board[x] = -1;

	for (y = 0; y < h; y++)
	for (x = 0; x < w; x++) {
		i = idx(y, x);

		while (isspace(*s)) s++;

		switch (*s) {
		case '_':	board[i] = 0;
		case '.':	break;
		default:
			known[ board[i] = strtol(s, 0, 10) ] = i;
			if (board[i] > top) top = board[i];
		}

		while (*s && !isspace(*s)) s++;
	}
}

void show_board(const char *s)
{
	int i, j, c;

	printf("\n%s:\n", s);

	for (i = 0; i < h; i++, putchar('\n'))
	for (j = 0; j < w; j++) {
		c = board[ idx(i, j) ];
		printf(!c ? " __" : c == -1 ? "   " : " %2d", c);
	}
}

int fill(int c, int n)
{
	int i, nei, p[8], ko, bo;

	if ((board[c] && board[c] != n) || (known[n] && known[n] != c))
		return 0;

	if (n == top) return 1;

	ko = known[n];
	bo = board[c];
	board[c] = n;

	if (check_connectity(n)) {
		nei = neighbors(c, p);
		for (i = 0; i < nei; i++)
			if (fill(p[i], n + 1))
				return 1;
	}

	board[c] = bo;
	known[n] = ko;
	return 0;
}

int main()
{
	make_board(
#define USE_E 0
#if (USE_E == 0)
		8,8,	" __ 33 35 __ __ .. .. .."
			" __ __ 24 22 __ .. .. .."
			" __ __ __ 21 __ __ .. .."
			" __ 26 __ 13 40 11 .. .."
			" 27 __ __ __  9 __  1 .."
			" .   . __ __ 18 __ __ .."
			" .  ..  .  . __  7 __ __"
			" .  .. .. ..  .  .  5 __"
#elif (USE_E == 1)
	3, 3,	" . 4 ."
		" _ 7 _"
		" 1 _ _"
#else
	50, 3,
	" 1 _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . 74"
	" . . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ . _ ."
	" . . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ . . _ _ ."
#endif
	);

	show_board("Before");
	fill(known[1], 1);
	show_board("After"); /* "40 lbs in two weeks!" */

	return 0;
}


using System.Collections;
using System.Collections.Generic;
using static System.Console;
using static System.Math;
using static System.Linq.Enumerable;

public class Solver
{
    private static readonly (int dx, int dy)[]
        //other puzzle types elided
        hidatoMoves = {(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1)};

    private (int dx, int dy)[] moves;

    public static void Main()
    {
        Print(new Solver(hidatoMoves).Solve(false, new [,] {
            {  0, 33, 35,  0,  0, -1, -1, -1 },
            {  0,  0, 24, 22,  0, -1, -1, -1 },
            {  0,  0,  0, 21,  0,  0, -1, -1 },
            {  0, 26,  0, 13, 40, 11, -1, -1 },
            { 27,  0,  0,  0,  9,  0,  1, -1 },
            { -1, -1,  0,  0, 18,  0,  0, -1 },
            { -1, -1, -1, -1,  0,  7,  0,  0 },
            { -1, -1, -1, -1, -1, -1,  5,  0 }
        }));
    }

    public Solver(params (int dx, int dy)[] moves) => this.moves = moves;

    public int[,] Solve(bool circular, params string[] puzzle)
    {
        var (board, given, count) = Parse(puzzle);
        return Solve(board, given, count, circular);
    }

    public int[,] Solve(bool circular, int[,] puzzle)
    {
        var (board, given, count) = Parse(puzzle);
        return Solve(board, given, count, circular);
    }

    private int[,] Solve(int[,] board, BitArray given, int count, bool circular)
    {
        var (height, width) = (board.GetLength(0), board.GetLength(1));
        bool solved = false;
        for (int x = 0; x < height && !solved; x++) {
            solved = Range(0, width).Any(y => Solve(board, given, circular, (height, width), (x, y), count, (x, y), 1));
            if (solved) return board;
        }
        return null;
    }

    private bool Solve(int[,] board, BitArray given, bool circular,
        (int h, int w) size, (int x, int y) start, int last, (int x, int y) current, int n)
    {
        var (x, y) = current;
        if (x < 0 || x >= size.h || y < 0 || y >= size.w) return false;
        if (board[x, y] < 0) return false;
        if (given[n - 1]) {
            if (board[x, y] != n) return false;
        } else if (board[x, y] > 0) return false;
        board[x, y] = n;
        if (n == last) {
            if (!circular || AreNeighbors(start, current)) return true;
        }
        for (int i = 0; i < moves.Length; i++) {
            var move = moves[i];
            if (Solve(board, given, circular, size, start, last, (x + move.dx, y + move.dy), n + 1)) return true;
        }
        if (!given[n - 1]) board[x, y] = 0;
        return false;

        bool AreNeighbors((int x, int y) p1, (int x, int y) p2) => moves.Any(m => (p2.x + m.dx, p2.y + m.dy).Equals(p1));
    }

    private static (int[,] board, BitArray given, int count) Parse(string[] input)
    {
        (int height, int width) = (input.Length, input[0].Length);
        int[,] board = new int[height, width];
        int count = 0;
        for (int x = 0; x < height; x++) {
            string line = input[x];
            for (int y = 0; y < width; y++) {
                board[x, y] = y < line.Length && char.IsDigit(line[y]) ? line[y] - '0' : -1;
                if (board[x, y] >= 0) count++;
            }
        }
        BitArray given = Scan(board, count, height, width);
        return (board, given, count);
    }

    private static (int[,] board, BitArray given, int count) Parse(int[,] input)
    {
        (int height, int width) = (input.GetLength(0), input.GetLength(1));
        int[,] board = new int[height, width];
        int count = 0;
        for (int x = 0; x < height; x++)
            for (int y = 0; y < width; y++)
                if ((board[x, y] = input[x, y]) >= 0) count++;
        BitArray given = Scan(board, count, height, width);
        return (board, given, count);
    }

    private static BitArray Scan(int[,] board, int count, int height, int width)
    {
        var given = new BitArray(count + 1);
        for (int x = 0; x < height; x++)
            for (int y = 0; y < width; y++)
                if (board[x, y] > 0) given[board[x, y] - 1] = true;
        return given;
    }

    private static void Print(int[,] board)
    {
        if (board == null) {
            WriteLine("No solution");
        } else {
            int w = board.Cast<int>().Where(i => i > 0).Max(i => (int?)Ceiling(Log10(i+1))) ?? 1;
            string e = new string('-', w);
            foreach (int x in Range(0, board.GetLength(0)))
                WriteLine(string.Join(" ", Range(0, board.GetLength(1))
                    .Select(y => board[x, y] < 0 ? e : board[x, y].ToString().PadLeft(w, ' '))));
        }
        WriteLine();
    }

}