Task: Solve a Hidato puzzle



C#: 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)[]
        
        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();
    }

}


Go: package main

import (
    "fmt"
    "sort"
    "strconv"
    "strings"
)

var board [][]int
var start, given []int

func setup(input []string) {
    
    puzzle := make([][]string, len(input))
    for i := 0; i < len(input); i++ {
        puzzle[i] = strings.Fields(input[i])
    }
    nCols := len(puzzle[0])
    nRows := len(puzzle)
    list := make([]int, nRows*nCols)
    board = make([][]int, nRows+2)
    for i := 0; i < nRows+2; i++ {
        board[i] = make([]int, nCols+2)
        for j := 0; j < nCols+2; j++ {
            board[i][j] = -1
        }
    }
    for r := 0; r < nRows; r++ {
        row := puzzle[r]
        for c := 0; c < nCols; c++ {
            switch cell := row[c]; cell {
            case "_":
                board[r+1][c+1] = 0
            case ".":
                break
            default:
                val, _ := strconv.Atoi(cell)
                board[r+1][c+1] = val
                list = append(list, val)
                if val == 1 {
                    start = append(start, r+1, c+1)
                }
            }
        }
    }
    sort.Ints(list)
    given = make([]int, len(list))
    for i := 0; i < len(given); i++ {
        given[i] = list[i]
    }
}

func solve(r, c, n, next int) bool {
    if n > given[len(given)-1] {
        return true
    }

    back := board[r][c]
    if back != 0 && back != n {
        return false
    }

    if back == 0 && given[next] == n {
        return false
    }

    if back == n {
        next++
    }

    board[r][c] = n
    for i := -1; i < 2; i++ {
        for j := -1; j < 2; j++ {
            if solve(r+i, c+j, n+1, next) {
                return true
            }
        }
    }

    board[r][c] = back
    return false
}

func printBoard() {
    for _, row := range board {
        for _, c := range row {
            switch {
            case c == -1:
                fmt.Print(" . ")
            case c > 0:
                fmt.Printf("%2d ", c)
            default:
                fmt.Print("__ ")
            }
        }
        fmt.Println()
    }
}

func main() {
    input := []string{
        "_ 33 35 _ _ . . .",
        "_ _ 24 22 _ . . .",
        "_ _ _ 21 _ _ . .",
        "_ 26 _ 13 40 11 . .",
        "27 _ _ _ 9 _ 1 .",
        ". . _ _ 18 _ _ .",
        ". . . . _ 7 _ _",
        ". . . . . . 5 _",
    }
    setup(input)
    printBoard()
    fmt.Println("\nFound:")
    solve(start[0], start[1], 1, 0)
    printBoard()
}