Rebol3 Code Examplex


Solve a Hidato puzzle

Fill a grid with consecutive numbers so each number touches the next horizontally, vertically, or diagonally.

Rebol [
    title: "Rosetta code: Solve a Hidato puzzle"
    file:  %Solve_a_Hidato_puzzle.r3
    url:   https://rosettacode.org/wiki/Solve_a_Hidato_puzzle
]

solve-hidato: function/with [
    "Solve and print a Hidato puzzle from a multiline string"
    puzzle [string!]
][
    setup puzzle
    print-board
    either solve start/1 start/2 1 1 [
        print as-yellow "Solved:"
        print-board
    ][  print as-red "No solution found!" ]
    board
][
    board: copy []  ;; 2D block of rows; 0=free, none=wall, N=given
    given: copy []  ;; sorted list of pre-filled numbers
    start: none     ;; [row col] of the cell containing 1
    digit: charset [#"0" - #"9"]

    setup: func [
        "Parse puzzle string into board, given, and start position"
        puzzle [string!]
        /local r c n row
    ][
        clear board
        clear given
        start: none
        r: 1 c: 0
        parse puzzle [
            opt CR opt LF
            some [
                (c: 0 row: copy [])
                some [
                    (++ c)
                      "__" opt SP (append row 0)          ;; free cell
                    | " ." opt SP (append row _)          ;; wall/gap
                    | opt SP  copy n: 1 2 digit opt SP (  ;; given number
                        append row n: to integer! n
                        append given n
                        if n = 1 [ start: reduce [r c] ]
                    )
                ]
                [opt CR LF | end] (
                    append/only board row
                    ++ r
                )
            ]
        ]
        sort given
    ]

    solve: function/extern [
        "Recursively place n at (r,c) and search all 8 neighbors for n+1"
        r [integer!] c [integer!]
        n [integer!]    ;; value to place at current cell
        next [integer!] ;; index into given[] for next expected fixed value
    ][
        if n > last given [ return true ]   ;; all numbers placed
        if any [
            none? row: board/:r             ;; out of bounds row
            none? val: row/:c               ;; wall cell
            all [val > 0  val != n]         ;; conflicts with a different given
            all [val = 0  given/:next = n]  ;; n is a given but cell is free
        ][  return false ]

        back: 0
        if val = n [        ;; landing on the correct given cell
            next: next + 1
            back: n         ;; remember to restore if we backtrack
        ]
        row/:c: n
        for i -1 1 1 [
            for j -1 1 1 [
                if solve r + i c + j n + 1 next [
                    return true
                ]
            ]
        ]
        row/:c: back  ;; backtrack
        false
    ][  board ]

    print-board: function [
        "Print the current board state; free cells as __, walls as spaces"
    ][
        buff: clear ""
        foreach row board [
            foreach v row [
                append buff any [
                    select [_ "  " 0 "__"] v
                    pad v -2
                ]
                append buff SP
            ]
            append buff LF
        ]
        print buff
    ]
]

solve-hidato {
__ 33 35 __ __  .  .  .
__ __ 24 22 __  .  .  .
__ __ __ 21 __ __  .  .
__ 26 __ 13 40 11  .  .
27 __ __ __  9 __  1  .
 .  . __ __ 18 __ __  .
 .  .  .  . __  7 __ __
 .  .  .  .  .  .  5 __}

Output:

__ 33 35 __ __          
__ __ 24 22 __          
__ __ __ 21 __ __       
__ 26 __ 13 40 11       
27 __ __ __  9 __  1    
      __ __ 18 __ __    
            __  7 __ __ 
                   5 __ 

Solved:
32 33 35 36 37          
31 34 24 22 38          
30 25 23 21 12 39       
29 26 20 13 40 11       
27 28 14 19  9 10  1    
      15 16 18  8  2    
            17  7  6  3 
                   5  4