Download
:- use_module(library(clpfd)).
% magic_square(+N, -Square)
magic_square(N, Square) :-
% Matrix (Square): N rows
length(Square, N), % Square has N rows
maplist(length_(N), Square), % each row is a list of N variables
% Collect all cells, set domain and uniqueness
append(Square, Vars), % creates a single list from the matrix
Max is N * N, % highest value (n squared)
Vars ins 1..Max, % every cell must be between 1 and Max
all_distinct(Vars), % every value must appear exactly once
% Compute magic sum
Sum is N * (N * N + 1) // 2,
% Enforce row sums
maplist(row_sum(Sum), Square), % each row must add up to Sum: maplist calls row_sum for every row in Square
% Enforce column sums
transpose(Square, Columns), % first transpose the matrix
maplist(row_sum(Sum), Columns), % now check column sums like row sums
% Enforce main diagonal sums
Last is N - 1,
numlist(0, Last, Indices), % creates a list [0,1,2,...,N-1]
maplist(diag1_cell(Square), Indices, Diag1), % collect the main-diagonal cell for every index into Diag1
row_sum(Sum, Diag1), % diagonal must add up to Sum
% Enforce anti-diagonal
maplist(diag2_cell(Square, N), Indices, Diag2), % collect the anti-diagonal cell for every index into Diag2
row_sum(Sum, Diag2), % diagonal must add up to Sum
% Search for concrete values satisfying all constraints
label(Vars). % assign actual numbers to all variables
% ----------------
% HELPER FUNCTIONS
% Swap the parameters for the maplist function.
% length_(+N, ?List): List is a list of N elements
length_(N, List) :- length(List, N).
% Enforce that a list sums to Sum
% row_sum(+Sum, +Row)
row_sum(Sum, Row) :- sum(Row, #=, Sum).
% gets cells with index (I,I)
% nth0 gives us the element at position I
% diag1_cell(+Square, +I, -Cell)
diag1_cell(Square, I, Cell) :-
nth0(I, Square, Row), % get row number I
nth0(I, Row, Cell). % get element number I from that row
% Get cell (I, N-1-I) for anti-diagonal
% diag2_cell(+Square, +N, +I, -Cell)
diag2_cell(Square, N, I, Cell) :-
nth0(I, Square, Row), % get row number I
J is N - 1 - I, % column index
nth0(J, Row, Cell). % get element number J from that row