Reputation: 23
This is a follow-up Question of: Restrict search in Prolog - Magic Sqare
Thanks to Isabelle Newbie for the help so far.
With the help of Isabelle Newbie I got my code working, but sadly only for 4x4 Squares.
I'm quite new to Prolog, so maybe I miss something obvious.
The following code generates a 4x4 magic square in basically no time. I implemented all the rules in a way that they also should work for squares of higher dimensions like 8x8 or 12x12, but for some reason it does not work.
:- use_module(library(clpfd)).
diag2_sum(0, _, _, _, _).
diag2_sum(I0, N, C1, Row1, Row3) :-
I0 > 0,
nth1(I0,Row1,A),
V1 is N - 2,
(I0 =:= V1 -> I2 = N ; I2 is mod(I0 + 2,N)),
nth1(I2,Row3,B),
C1 #= A + B,
I1 is I0 - 1,
diag2_sum(I1, N, C1, Row1, Row3).
diag_sum([_,_], _, _).
diag_sum([Row1|Tail], C1, N) :-
nth1(2,Tail,Row3),
diag2_sum(N, N, C1, Row1,Row3),
diag_sum(Tail, C1, N).
square_sum_x(_, _, _, 0, _).
square_sum_x(Row1, Row2, C2, I0, N) :-
V1 is N - 1,
(I0 =:= V1 -> I2 = N ; I2 is mod(I0 + 1,N)),
nth1(I0,Row1,Elem1),
nth1(I2,Row1,Elem2),
nth1(I0,Row2,Elem3),
nth1(I2,Row2,Elem4),
C2 #= Elem1 + Elem2 + Elem3 + Elem4,
I1 is I0 - 1,
square_sum_x(Row1, Row2, C2, I1, N).
square_sum_y(_, _, 0, _).
square_sum_y(Matrix, C2, I0, N) :-
V1 is N - 1,
(I0 =:= V1 -> I2 = N ; I2 is mod(I0 + 1,N)),
nth1(I0,Matrix,Row1),
nth1(I2,Matrix,Row2),
square_sum_x(Row1,Row2, C2, N, N),
I1 is I0 - 1,
square_sum_y(Matrix, C2, I1, N).
magic_square_(N, Matrix) :-
Nmax is N * N,
C1 is Nmax + 1,
C2 is C1 * 2,
write(C1),nl,write(C2),nl,
length(Matrix, N),
maplist(same_length(Matrix), Matrix),
append(Matrix, Vs),
Vs ins 1..Nmax, all_different(Vs),
diag_sum(Matrix, C1, N),
square_sum_y(Matrix, C2, N, N).
magic_square(N, Matrix) :-
magic_square_(N, Matrix),
maplist(label, Matrix).
4x4 magic square(works):
?- magic_square(4, Matrix).
17
34
Matrix = [[1, 8, 10, 15], [12, 13, 3, 6], [7, 2, 16, 9], [14, 11, 5, 4]]
8x8 magic square(doesnt work):
?- magic_square(8, Matrix).
65
130
false.
Upvotes: 0
Views: 179
Reputation: 29048
I implemented all the rules in a way that they also should work for squares of higher dimensions
my program returns true. So I assume my rule basis is correct.
diag_sum(Matrix, C1, N),
square_sum_y(Matrix, C2, N, N).
I commented out these last two lines, and verified that it does generate an 8x8 matrix and 12x12 matrix of increasing values. Adding either of these lines back in works for a 4x4 matrix but not 8x8 or 12x12, which says both of them do not work for squares of higher dimensions.
Generate a non-magic square of 8x8 and test diag_sum() on it and the topright to lowerleft /
diagonal check picks these two values, the eight and the eighteen:
diag_sum([[ 1, 2, 3, 4, 5, 6, 7, *8],
[ 9, 10, 11, 12, 13, 14, 15, 16],
[17,*18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
[49, 50, 51, 52, 53, 54, 55, 56],
[57, 58, 59, 60, 61, 62, 63, 64]], 65, 8).
I think it should pick these two, the eight and the twenty two:
[ 1, 2, 3, 4, 5, 6, 7, *8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21,*22, 23, 24],
Because this line:
(I0 =:= V1 -> I2 = N ; I2 is mod(I0 + 2,N)),
rolls around to the right using mod
in a way that only works where 4-2 =:= (4+2) mod 4
but 8-2 =\= (8+2) mod 8
so it fails here.
I used SWISH online tracing (click to the left of a line to set a breakpoint and step through the code as it runs) to locate this.
NB. you didn't ask a question, you only stated "for some reason it does not work". This is the first reason it does not work, it looks like the same design using mod
is in the square_sum_x which may fail in the same way. What to do about it is a different question; you might find some ideas on https://www.metalevel.at/sudoku/ and in the video explanation there for how that represents the smaller squares in a Sudoku grid more directly.
Perhaps build and test a way to extract the diagonals and test that on a matrix of numbers to see that it gives the correct diagonal positions for 8x8 and 12x12, and then to split those results into spaced pairs, and test that, and then build up from there?
Upvotes: 1