# prolog - How to create rule that returns each value as unique

I'm following along with Learn Prolog Now! and was looking at Exercise 2.4.

A solution I found here seems to solve, but not completely:

``word(astante, a,s,t,a,n,t,e).word(astoria, a,s,t,o,r,i,a).word(baratto, b,a,r,a,t,t,o).word(cobalto, c,o,b,a,l,t,o).word(pistola, p,i,s,t,o,l,a).word(statale, s,t,a,t,a,l,e).crossword(V1,V2,V3,H1,H2,H3) :-word(V1, _, V1H1, _, V1H2, _, V1H3, _),word(V2, _, V2H1, _, V2H2, _, V2H3, _),word(V3, _, V3H1, _, V3H2, _, V3H3, _),word(H1, _, V1H1, _, V2H1, _, V3H1, _),word(H2, _, V1H2, _, V2H2, _, V3H2, _),word(H3, _, V1H3, _, V2H3, _, V3H3, _).``

This produces the following results:

``H1 = astoriaH2 = barattoH3 = stataleV1 = astanteV2 = cobaltoV3 = pistola ? ;H1 = astanteH2 = cobaltoH3 = pistolaV1 = astoriaV2 = barattoV3 = statale ? ;H1 = astoriaH2 = cobaltoH3 = pistolaV1 = astoriaV2 = cobaltoV3 = pistola ? ;H1 = barattoH2 = barattoH3 = stataleV1 = barattoV2 = barattoV3 = statale ? ;H1 = cobaltoH2 = barattoH3 = stataleV1 = cobaltoV2 = barattoV3 = statale ? ;H1 = astanteH2 = barattoH3 = stataleV1 = astanteV2 = barattoV3 = statale ? ;``

Of these, only 2 are practical:

``H1 = astoriaH2 = barattoH3 = stataleV1 = astanteV2 = cobaltoV3 = pistola ? ;H1 = astanteH2 = cobaltoH3 = pistolaV1 = astoriaV2 = barattoV3 = statale ? ;``

Because the other 3 solutions contain duplicates, they are not viable answers for the problem.

How can I add to the crossword rule to have it only return results where V1,V2,V3,H1,H2,H3 are all unique?

You could use a procedure like this to ensure values are all different:

``````all_dif([]).
all_dif([A|Tail]):-
all_dif(Tail, A),
all_dif(Tail).

all_dif([], _).
all_dif([B|Tail], A):-
dif(A,B),
all_dif(Tail, A).
``````

and call it with `all_dif([V1,V2,V3,H1,H2,H3])`

a common technique make use of select/3, to get unique alternative elements on backtracking:

``````crossword(V1,V2,V3,H1,H2,H3) :-
selects(
[[V1, _, V1H1, _, V1H2, _, V1H3, _],
[V2, _, V2H1, _, V2H2, _, V2H3, _],
[V3, _, V3H1, _, V3H2, _, V3H3, _],
[H1, _, V1H1, _, V2H1, _, V3H1, _],
[H2, _, V1H2, _, V2H2, _, V3H2, _],
[H3, _, V1H3, _, V2H3, _, V3H3, _]
],
[[a,s,t,a,n,t,e],
[a,s,t,o,r,i,a],
[b,a,r,a,t,t,o],
[c,o,b,a,l,t,o],
[p,i,s,t,o,l,a],
[s,t,a,t,a,l,e]
]).

selects([], []).
selects([[W|Cs]|Ws], L) :-
select(Cs, L, R),
selects(Ws, R),
atom_chars(W, Cs).
``````

This is clearly an alternative method to the very simple solution you already found.

select/3 can also be used to check that there are no duplicates in list:

``````crossword(V1,V2,V3,H1,H2,H3) :-
word(V1, _, V1H1, _, V1H2, _, V1H3, _),
word(V2, _, V2H1, _, V2H2, _, V2H3, _),
word(V3, _, V3H1, _, V3H2, _, V3H3, _),
word(H1, _, V1H1, _, V2H1, _, V3H1, _),
word(H2, _, V1H2, _, V2H2, _, V3H2, _),
word(H3, _, V1H3, _, V2H3, _, V3H3, _),
maplist(nodup([V1,V2,V3,H1,H2,H3]), [V1,V2,V3,H1,H2,H3]).

nodup(L, E) :- select(E, L, R), \+ memberchk(E, R).
``````

At last, since sort/2 removes duplicates, the easiest check could be

``````crossword(V1,V2,V3,H1,H2,H3) :-
word(V1, _, V1H1, _, V1H2, _, V1H3, _),
word(V2, _, V2H1, _, V2H2, _, V2H3, _),
word(V3, _, V3H1, _, V3H2, _, V3H3, _),
word(H1, _, V1H1, _, V2H1, _, V3H1, _),
word(H2, _, V1H2, _, V2H2, _, V3H2, _),
word(H3, _, V1H3, _, V2H3, _, V3H3, _),
sort([V1,V2,V3,H1,H2,H3], [_,_,_,_,_,_]).
``````