-4

I'm trying to figure how to approach the following puzzle in prolog.

I need to create a predicate eight_digit_puzzle(Result). that suggests all possible solutions for the eight-digit puzzle which follows these rules:

  • Each cell contains a number between 1 and 8.
  • No repetitions.
  • In each cell in four directions you can't put consecutive digits(digits that their difference is 1).

    Example, visually and the result in prolog for the predicate:

example

Result = [7,4,1,3,6,8,5,2]

false
  • 10,264
  • 13
  • 101
  • 209
  • What are your ideas to solve the problem? – lambda.xy.x Jul 31 '18 at 08:50
  • The only thing I thought of is that I need to run some restriction on the permutations of list containing 1 to 8. – Daniel Lewinski Jul 31 '18 at 08:55
  • I think the original riddle is with eight directions, you then get more constraints, a less many solutions. See also https://www.cut-the-knot.org/SimpleGames/EightDigitPuzzle.shtml#solution –  Jul 31 '18 at 17:28

2 Answers2

2

You can use CLP(FD) to solve the problem. The condition that two neighbouring cells do not have a distance of one can be expressed even without reification:

ns(X, Y) :- 
    X #\= Y+1, 
    X+1 #\= Y.

Reification is not needed since in the present situation a (#/\)/2 can be replaced by a (,)/2. Together with all_different/1 the problem can be expressed as follows:

riddle(L) :-
    L = [A,B,C,D,E,F,G,H],
    L ins 1..8,
    all_different(L),
    /* horizontal */
    ns(B, E),
    ns(A, C),
    ns(C, F),
    ns(F, H),
    ns(D, G),
    /* vertical */
    ns(B, C),
    ns(C, D),
    ns(E, F),
    ns(F, G),
    /* search solution */
    label(L).

Running it I get the following number of solutions:

Jekejeke Prolog 3, Runtime Library 1.3.0
(c) 1985-2018, XLOG Technologies GmbH, Switzerland

?- riddle([7,4,1,3,6,8,5,2]).
Yes

?- findall(hit,riddle(_),L), length(L,N), 
   write(N), nl, fail; true.
1656
Yes

Edit 31.07.2018:

This is left as a homework. If you add constraints for the diagonals from bottomleft to topright, and the diagonals from topleft to bottomright, the system gets more tight and only generates the following four solutions:

?- riddle(L).
L = [2,5,8,6,3,1,4,7] ;
L = [2,6,8,5,4,1,3,7] ;
L = [7,3,1,4,5,8,6,2] ;
L = [7,4,1,3,6,8,5,2] ;
No 
  • In many systems it is preferable to write `ns(X, Y) :- abs(X-Y) #> 1`. – false Aug 01 '18 at 09:13
  • abs(X-Y) #> 1 is not logically equivalent to X #\= Y+1, X+1 #\= Y. The abs/1 also requires X #\= Y, which already covered by all_distinct/1. So I doubt its faster. –  Aug 01 '18 at 13:24
  • 1
    You can test yourself: Without abs/1: 0.145 seconds, with abs/1: 0.203 seconds, so I wouldn't encourage it in SWI7, also observable in YAP. –  Aug 01 '18 at 13:27
0
/*
      2,5,
    1,3,6,8
      4,7,
*/

riddle_p(R) :-
    permutation([1,2,3,4,5,6,7,8],R),
    maplist(no_diff_1(R), [[2,5],[1,3,6,8],[4,7],[2,3,4],[5,6,7]]).

no_diff_1(R,[A,B|Is]) :-
    nth1(A,R,X), nth1(B,R,Y), abs(X-Y) > 1,
    !, no_diff_1(R,[B|Is]).
no_diff_1(_,[_]).

Running in SWI-Prolog, it's fairly faster than the clp(fd) solution.

CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • Using CLP(FD) pays off when the original riddle with eight directions (see cut the knot) is used. We then get for the brute force solution 124 ms and for the CLP(FD) solution only 16 ms. https://gist.github.com/jburse/fd538dd12b2d206fd653231cc5130968#gistcomment-2669042 –  Aug 04 '18 at 14:05