% n_queens2(+N, -Solution): Solution lists the row of the queen in each column
n_queens2(N, Solution) :-
    numlist(1, N, AllRows),
    numlist(1, N, AllCols),
    place_queens(AllCols, AllRows, [], Solution).

% place_queens(+Cols, +AvailableRows, +PrevQueens, -Solution): place one queen per column
% Base case: No more columns/rows available. Terminate the solution list with [].
place_queens([], [], _, []).
place_queens([Col|RestCols], AvailableRows, PrevQueens, [Row|RestSolution]) :-
    % 1. Select a row from available rows and store the remaining in RestRows
    select(Row, AvailableRows, RestRows),
    % 2. Check immediately if this new position is safe from previous queens
    check_position(Col, Row, PrevQueens),
    % 3. If safe, store the queen
    NewPrevQueens = [dame(Col, Row) | PrevQueens],
    %Recursion: Process next queen
    place_queens(RestCols, RestRows, NewPrevQueens, RestSolution).

% Check if the new queen is safe from the list of already placed queens
% check_position(+Col, +Row, +PrevQueens): the queen at (Col,Row) attacks no previous queen
check_position(_, _, []).
check_position(Col, Row, [dame(Col2, Row2) | OtherQueens]) :-
    diagonal_desc(Col, Row, Col2, Row2),
    diagonal_asc(Col, Row, Col2, Row2),
    check_position(Col, Row, OtherQueens).

% Diagonal checks: return true if not on the same diagonal
% diagonal_desc(+C1, +R1, +C2, +R2): the two queens are not on the same descending diagonal
diagonal_desc(C1, R1, C2, R2) :- C1 - R1 =\= C2 - R2.
% diagonal_asc(+C1, +R1, +C2, +R2): the two queens are not on the same ascending diagonal
diagonal_asc(C1, R1, C2, R2) :- C1 + R1 =\= C2 + R2.
