/* File slow_n_queens.pl

Suppose that we have a chessboard of N * N positions,
and that we want to place on it N queens so that no
queen can attack any other queen. Remember that two queens 
can attach each other if and only if they are either in the same
row, or in the same column, or in the same diagonal. 
A solution to the problem can be represented as a list 
   Pos = [J1,J2,...,JN],
where each Ji represents an index of column, and means that a queen
is placed in the position (i,Ji) in the chessboard. 

The following program is a solution to the N queens problem:
The predicate queens(N,Pos) holds if Pos is a list which represents a solution.
The definition is based on the "generate and test" technique.
*/

queens(N,Pos) :- generate(N,Pos), safe(Pos).

/* generate(N,Pos) generates an arbitrary permutation of the list of integers fron 1 to N */

generate(N,Pos) :- list_from_1_to_N(N,L), permutation(L,Pos).

list_from_1_to_N(N,L) :- gen(1,N,L).
gen(J,N,[J|L]) :- J<N, J1 is J+1, gen(J1,N,L).
gen(N,N,[N]).

permutation([],[]).
permutation(L,[X|K]) :- append(L1,[X|L2],L), append(L1,L2,M), permutation(M,K).

/*
safe(Pos) checks that the positions are safe, i.e. that the queens are not attacking each other. 
Because of the representation of the postions, it is sufficient to check that there is no check 
along the diagonals. 
*/

safe(Pos) :- diagonal_safe(1,Pos). 
diagonal_safe(_,[_]).
diagonal_safe(I,[J|L]) :- not(diag_attack(I,J,L)), I1 is I+1,
                          diagonal_safe(I1,L).

diag_attack(I,J,L) :- I2 is I+1, occurs(J1,L,I2,I1), 
                      (J1+I1=:=J+I; J1-I1=:=J-I).

occurs(J1,[J1|_],I,I).
occurs(J1,[_|L],I,I1) :- I2 is I+1, occurs(J1,L,I2,I1).

/*
Sample session:

?- consult('slow_n_queens.pl').
slow_n_queens.pl compiled, 0.00 sec, 3,668 bytes.

Yes
?- queens(4,P).

P = [2, 4, 1, 3] ;

P = [3, 1, 4, 2] ;

No
?- queens(5,P).

P = [1, 3, 5, 2, 4] ;

P = [1, 4, 2, 5, 3] ;

P = [2, 4, 1, 3, 5] ;

P = [2, 5, 3, 1, 4] ;

P = [3, 1, 4, 2, 5] ;

P = [3, 5, 2, 4, 1] ;

P = [4, 1, 3, 5, 2] ;

P = [4, 2, 5, 3, 1] ;

P = [5, 2, 4, 1, 3] ;

P = [5, 3, 1, 4, 2] ;

No
?- 
*/
