empty(e).
queen(q).

noqueens([]).
noqueens([H|R]) :- empty(H), noqueens(R).

onequeen([H|R]) :- queen(H), noqueens(R).
onequeen([H|R]) :- empty(H), onequeen(R).

valid(X) :- noqueens(X).
valid(X) :- onequeen(X).

countqueens([],0).
countqueens([H|R],N) :- queen(H), countqueens(R,I), N is I+1.
countqueens([H|R],N) :- empty(H), countqueens(R,N).

nqueen([[X11,X12,X13,X14],[X21,X22,X23,X24],[X31,X32,X33,X34],[X41,X42,X43,X44]]) :-
% Make sure all of the rows are valid:
    valid([X11,X12,X13,X14]),
    valid([X21,X22,X23,X24]),
    valid([X31,X32,X33,X34]),
    valid([X41,X42,X43,X44]),
% Now check the columns:
    valid([X11,X21,X31,X41]),
    valid([X12,X22,X32,X42]),
    valid([X13,X23,X33,X43]),
    valid([X14,X24,X34,X44]),
% Make sure all of the diagonals are valid
    valid([X21,X12]),
    valid([X31,X22,X13]),
    valid([X41,X32,X23,X14]),
    valid([X42,X33,X24]),
    valid([X43,X34]),
    valid([X24,X13]),
    valid([X12,X23,X34]),
    valid([X11,X22,X33,X44]),
    valid([X21,X32,X43]),
    valid([X31,X42]),
% Finally, make sure the board has exactly 4 queens:
    countqueens([X11,X12,X13,X14,X21,X22,X23,X24,X31,X32,X33,X34,X41,X42,X43,X44],4).

boardpos(X,'Q') :- queen(X).
boardpos(_,' ').

printrow([COL1,COL2,COL3,COL4]) :-
    nl,
    write('| '),
    boardpos(COL1,CHAR1),
    boardpos(COL2,CHAR2),
    boardpos(COL3,CHAR3),
    boardpos(COL4,CHAR4),
    write(CHAR1),
    write(' | '),
    write(CHAR2),
    write(' | '),
    write(CHAR3),
    write(' | '),
    write(CHAR4),
    write(' | '),
    nl.

printboard([ROW1,ROW2,ROW3,ROW4]) :-
    nl,
    write('+---+---+---+---+'),
    printrow(ROW1),
    write('+---+---+---+---+'),
    printrow(ROW2),
    write('+---+---+---+---+'),
    printrow(ROW3),
    write('+---+---+---+---+'),
    printrow(ROW4),
    write('+---+---+---+---+').

solve4queens :-
    nqueen(X),
    printboard(X).
