How can I optimize my sudoku solver? - prolog

So I have been trying to write a sudoku solver I wrote this:
permutation([], []).
permutation([X|L], P) :-
permutation(L, L1),
insert(X, L1, P).
del(X, [X|Xs], Xs).
del(X, [Y|Ys], [Y|Zs]) :-
del(X, Ys, Zs).
insert(X, List, BiggerList) :-
del(X, BiggerList, List).
block(X1,X2,X3,X4,X5,X6,X7,X8,X9) :-
permutation([1,2,3,4,5,6,7,8,9],[X1,X2,X3,X4,X5,X6,X7,X8,X9]).
solveSudoku(X11,X12,X13,X14,X15,X16,X17,X18,X19,X21,X22,X23,X24,X25,X26,X27,X28,X29,X31,X32,X33,X34,X35,X36,X37,X38,X39,X41,X42,X43,X44,X45,X46,X47,X48,X49,X51,X52,X53,X54,X55,X56,X57,X58,X59,X61,X62,X63,X64,X65,X66,X67,X68,X69,X71,X72,X73,X74,X75,X76,X77,X78,X79,X81,X82,X83,X84,X85,X86,X87,X88,X89,X91,X92,X93,X94,X95,X96,X97,X98,X99) :-
block(X11,X12,X13,X14,X15,X16,X17,X18,X19) ,
block(X21,X22,X23,X24,X25,X26,X27,X28,X29) ,
block(X31,X32,X33,X34,X35,X36,X37,X38,X39) ,
block(X41,X42,X43,X44,X45,X46,X47,X48,X49) ,
block(X51,X52,X53,X54,X55,X56,X57,X58,X59) ,
block(X61,X62,X63,X64,X65,X66,X67,X68,X69) ,
block(X71,X72,X73,X74,X75,X76,X77,X78,X79) ,
block(X81,X82,X83,X84,X85,X86,X87,X88,X89) ,
block(X91,X92,X93,X94,X95,X96,X97,X98,X99) ,
... 27 blockes
the only problem is that for a normal input it never finishes (takes a lot of time), how can I optimize it?
It seems to be working because when I copied it for 4x4 It worked well. And for failure cases that are reviewed at the beginning (the lines) it returns false.
the full code
Or in another way

As you have observed, your program works fine with smaller instances of the problem, e.g. 4x4. What you see is the combinatorial explosion of the search space. To see the difference, compare 4x4 variables with 4 values each (4^16 = 4.29e+9 combinations) with 9x9 variables with 9 values each (9^81 = 1.97e+77 combinations).
The first 9 calls of your block/9 predicate build a search tree with a depth of 81 levels, while only ensuring the "no duplicates in a row" constraints. The following 18 calls of block/9 check the "column" and "block" constraints, and force backtracking into the huge search tree every time they find a violation. This is hopeless.
The way to improve this behaviour is to check immediately after a variable was set to a new value, that all the constraints are still satisfiable. This is actually one of the key techniques in constraint logic programming. Several Prolog systems support corresponding extensions (see e.g. the dif/2 predicate or the alldifferent/1 constraint).
However, I'd like to show here a program in standard Prolog that implements the same idea. Although it does so in a somewhat brute force way, it is still very effective:
?- sudoku.
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[4, 5, 6, 7, 8, 9, 1, 2, 3]
[7, 8, 9, 1, 2, 3, 4, 5, 6]
[2, 1, 4, 3, 6, 5, 8, 9, 7]
[3, 6, 5, 8, 9, 7, 2, 1, 4]
[8, 9, 7, 2, 1, 4, 3, 6, 5]
[5, 3, 1, 6, 4, 2, 9, 7, 8]
[6, 4, 2, 9, 7, 8, 5, 3, 1]
[9, 7, 8, 5, 3, 1, 6, 4, 2]
Yes (0.08s cpu, solution 1, maybe more)
The code consists of a predicate check/1 that makes sure that the current variable assignments do not already violate any sudoku constraint. This check is called by checked_between/4 every time a value is assigned to a variable.
sudoku :-
Grid = [X11,X12,X13,X14,X15,X16,X17,X18,X19,
X21,X22,X23,X24,X25,X26,X27,X28,X29,
X31,X32,X33,X34,X35,X36,X37,X38,X39,
X41,X42,X43,X44,X45,X46,X47,X48,X49,
X51,X52,X53,X54,X55,X56,X57,X58,X59,
X61,X62,X63,X64,X65,X66,X67,X68,X69,
X71,X72,X73,X74,X75,X76,X77,X78,X79,
X81,X82,X83,X84,X85,X86,X87,X88,X89,
X91,X92,X93,X94,X95,X96,X97,X98,X99],
checked_between(Grid, 1, 9, check(Grid)),
write([X11,X12,X13,X14,X15,X16,X17,X18,X19]), nl,
write([X21,X22,X23,X24,X25,X26,X27,X28,X29]), nl,
write([X31,X32,X33,X34,X35,X36,X37,X38,X39]), nl,
write([X41,X42,X43,X44,X45,X46,X47,X48,X49]), nl,
write([X51,X52,X53,X54,X55,X56,X57,X58,X59]), nl,
write([X61,X62,X63,X64,X65,X66,X67,X68,X69]), nl,
write([X71,X72,X73,X74,X75,X76,X77,X78,X79]), nl,
write([X81,X82,X83,X84,X85,X86,X87,X88,X89]), nl,
write([X91,X92,X93,X94,X95,X96,X97,X98,X99]), nl.
% check whether any of the values chosen so far violate a sudoku constraint
check([ X11,X12,X13,X14,X15,X16,X17,X18,X19,
X21,X22,X23,X24,X25,X26,X27,X28,X29,
X31,X32,X33,X34,X35,X36,X37,X38,X39,
X41,X42,X43,X44,X45,X46,X47,X48,X49,
X51,X52,X53,X54,X55,X56,X57,X58,X59,
X61,X62,X63,X64,X65,X66,X67,X68,X69,
X71,X72,X73,X74,X75,X76,X77,X78,X79,
X81,X82,X83,X84,X85,X86,X87,X88,X89,
X91,X92,X93,X94,X95,X96,X97,X98,X99]) :-
nodups([X11,X12,X13,X14,X15,X16,X17,X18,X19]),
nodups([X21,X22,X23,X24,X25,X26,X27,X28,X29]),
nodups([X31,X32,X33,X34,X35,X36,X37,X38,X39]),
nodups([X41,X42,X43,X44,X45,X46,X47,X48,X49]),
nodups([X51,X52,X53,X54,X55,X56,X57,X58,X59]),
nodups([X61,X62,X63,X64,X65,X66,X67,X68,X69]),
nodups([X71,X72,X73,X74,X75,X76,X77,X78,X79]),
nodups([X81,X82,X83,X84,X85,X86,X87,X88,X89]),
nodups([X91,X92,X93,X94,X95,X96,X97,X98,X99]),
nodups([X11,X21,X31,X41,X51,X61,X71,X81,X91]),
nodups([X12,X22,X32,X42,X52,X62,X72,X82,X92]),
nodups([X13,X23,X33,X43,X53,X63,X73,X83,X93]),
nodups([X14,X24,X34,X44,X54,X64,X74,X84,X94]),
nodups([X15,X25,X35,X45,X55,X65,X75,X85,X95]),
nodups([X16,X26,X36,X46,X56,X66,X76,X86,X96]),
nodups([X17,X27,X37,X47,X57,X67,X77,X87,X97]),
nodups([X18,X28,X38,X48,X58,X68,X78,X88,X98]),
nodups([X19,X29,X39,X49,X59,X69,X79,X89,X99]),
nodups([X11,X12,X13,X21,X22,X23,X31,X32,X33]),
nodups([X41,X42,X43,X51,X52,X53,X61,X62,X63]),
nodups([X71,X72,X73,X81,X82,X83,X91,X92,X93]),
nodups([X14,X15,X16,X24,X25,X26,X34,X35,X36]),
nodups([X44,X45,X46,X54,X55,X56,X64,X65,X66]),
nodups([X74,X75,X76,X84,X85,X86,X94,X95,X96]),
nodups([X17,X18,X19,X27,X28,X29,X37,X38,X39]),
nodups([X47,X48,X49,X57,X58,X59,X67,X68,X69]),
nodups([X77,X78,X79,X87,X88,X89,X97,X98,X99]).
nodups([]).
nodups([X|Xs]) :-
not_contains(Xs, X),
nodups(Xs).
not_contains([], _).
not_contains([Y|Ys], X) :-
X \== Y,
not_contains(Ys, X).
checked_between([], _, _, _).
checked_between([X|Xs], L, H, Check) :-
between(L, H, X),
call(Check),
checked_between(Xs, L, H, Check).
between(L, H, L) :- L =< H.
between(L, H, X) :-
L < H,
L1 is L+1,
between(L1, H, X).

Related

Problem with mathematical operation with findall/3 in a List (Prolog)

I want to multiply elements in a List with findall/3. Specifically I have two functions double(X,Y) which doubles X and square(X,Y) that returns the squared value of X. My problem is that it the operation works only for the first element of the list.
double(X,Y) :- Y is X*2.
square(X,Y) :- Y is X*X.
map_f(Operation,[H|List],[R|Results]) :-
Predicate=..[Operation,H,R],
call(Predicate),
findall(X,( member(X,List) ), Results).
For example, if I type map_f(double,[3,1,2,6,3,1,6],L). ,
I expect the output: L = [6,2,4,12,6,2,12],
but instead it shows:
?- map_f(double, [3, 1, 2, 6, 3, 1, 6], List).
List = [6, 1, 2, 6, 3, 1, 6]
Yes (0.00s cpu)
Any help will be very appreciated.
If you want to use findall/3, you'd have to write it like this:
?- Xs = [3,1,2,6,3,1,6], findall(Y, ( member(X, Xs), double(X, Y) ), Ys).
Xs = [3, 1, 2, 6, 3, 1, 6],
Ys = [6, 2, 4, 12, 6, 2, 12].
If you really want to pass the predicate as an argument and use =.., the logic is still the same, you'd just have to re-write your definition so that it does the right thing:
map_f(Pred_name, L1, L2) :-
Goal =.. [Pred_name, X, Y],
findall(Y, ( member(X, L1), Goal ), L2).
Then:
?- map_f(double, [3,1,2,6,3,1,6], R).
R = [6, 2, 4, 12, 6, 2, 12].
?- map_f(square, [3,1,2,6,3,1,6], R).
R = [9, 1, 4, 36, 9, 1, 36].
But, instead of:
Goal =.. [Pred_name, Arg1, Arg2], Goal
it is easier to use call/N+1:
call(Pred_name, Arg1, Arg2)
So your definition will become:
map_f(Pred_name, L1, L2) :-
findall(Y, ( member(X, L1), call(Pred_name, X, Y) ), L2).
But really, all of this is completely unnecessary if you only have lists. You can just use maplist/N+1, like that:
?- maplist(double, [3,1,2,6,3,1,6], R).
R = [6, 2, 4, 12, 6, 2, 12].
... which iterates over the lists instead of backtracking over them. You can see a maplist implementation for example here:
https://github.com/SWI-Prolog/swipl-devel/blob/2d20d4e8ac28adfcede7a9bd231ea0d9d12d0bbb/library/apply.pl#L195-L205
If your predicate is a real relation (so if it works both ways), you can also use maplist both ways. findall cannot do that! Here is one silly example:
?- maplist(succ, [1,2,3], R).
R = [2, 3, 4].
?- maplist(succ, R, [1,2,3]).
R = [0, 1, 2].
?- map_f(succ, [1,2,3], R).
R = [2, 3, 4].
?- map_f(succ, R, [1,2,3]).
ERROR: Arguments are not sufficiently instantiated

Manipulating Prolog code output

I am trying to run code on this page: https://swish.swi-prolog.org/example/clpfd_queens.pl in swipl on a Linux terminal.
:- use_module(library(clpfd)).
n_queens(N, Qs) :-
length(Qs, N),
Qs ins 1..N,
safe_queens(Qs).
safe_queens([]).
safe_queens([Q|Qs]) :-
safe_queens(Qs, Q, 1),
safe_queens(Qs).
safe_queens([], _, _).
safe_queens([Q|Qs], Q0, D0) :-
Q0 #\= Q,
abs(Q0 - Q) #\= D0,
D1 #= D0 + 1,
safe_queens(Qs, Q0, D1).
Following command works:
?- n_queens(4, Qs), labeling([ff], Qs).
But not just n_queens(4, Qs):
?- n_queens(4, Qs).
Qs = [_G1470, _G1473, _G1476, _G1479],
_G1470 in 1..4,
abs(_G1470-_G1479)#\=3,
_G1470#\=_G1479,
abs(_G1470-_G1476)#\=2,
_G1470#\=_G1476,
abs(_G1470-_G1473)#\=1,
_G1470#\=_G1473,
_G1479 in 1..4,
abs(_G1476-_G1479)#\=1,
_G1476#\=_G1479,
abs(_G1473-_G1479)#\=2,
_G1473#\=_G1479,
_G1476 in 1..4,
abs(_G1473-_G1476)#\=1,
_G1473#\=_G1476,
_G1473 in 1..4.
Why is labeling part needed here? Can one get proper output without labeling part?
For larger numbers, one gets only initial part of the solution:
?- n_queens(20, Qs), labeling([ff], Qs).
Qs = [1, 3, 5, 14, 17, 4, 16, 7, 12|...] ;
Qs = [1, 3, 5, 18, 16, 4, 10, 7, 14|...] ;
...
How can one get full list output for larger numbers? Also, how can one get all numbers together, without having to press spacebar for each solution? Thanks for your help.
n_queens/2 does not solves the N-queens problem for N queens: it constructs the constraint programming problem: it constructs N variables (the columns of the queens), and adds constraints between these queens: for instance that two queens can not be placed on the same row, nor on the same diagonal. We see this if we rewrite the problem output to more convenient output:
A in 1..4,
abs(A-D)#\=3,
A#\=D,
abs(A-C)#\=2,
A#\=C,
abs(A-B)#\=1,
A#\=B,
D in 1..4,
abs(C-D)#\=1,
C#\=D,
abs(B-D)#\=2,
B#\=D,
C in 1..4,
abs(B-C)#\=1,
B#\=C,
B in 1..4.
So we see four queens (A, B, C and D). Each of the queens should be in the domain 1..4, furthermore we see non equal constraints like A #\= D to prevent the first queen A sharing a column with the last queen D. We finally see constraints like abs(A-C) #\= 2 to prevent the first queen A and the third queen C to differ two columns (diagnal attack).
Next labeling/2 will actually solve the problem: it performs relaxation (reducing the domains) as well as branching (picking a value or a subrange of values for variables) and backtracking in case the constraints fail. It will continue until it finds a solution, and we can use Prolog's backtracking mechanism to let labeling/2 come up with more solutions.
labeling thus is given a list of variables and aims to label them: assign them a value out of the range such that all constraints are satisfied.
Therefore the problem construction part is usually very fast compared to the actually solving part: it is easy to generate O(N) variables and O(N2) constraints, but it can take an exponential amount of time O(DN) to come up with a solution satisfying all constraints.
Also, how can one get all numbers together, without having to press spacebar for each solution?
You can use the meta-predicate findall/3 for that:
all_n_queens(N,LL) :-
findall(L,(n_queens(N,L), labeling([ff], L)),LL).
Which generates:
?- all_n_queens(5,LL).
LL = [[1, 3, 5, 2, 4], [1, 4, 2, 5, 3], [2, 4, 1, 3, 5], [2, 5, 3, 1, 4], [3, 1, 4, 2|...], [3, 5, 2|...], [4, 1|...], [4|...], [...|...]|...].
How can one get full list output for larger numbers?
You can set the answer_write_options flag:
?- set_prolog_flag(answer_write_options,[max_depth(0)]).
true.
?- all_n_queens(5,LL).
LL = [[1,3,5,2,4],[1,4,2,5,3],[2,4,1,3,5],[2,5,3,1,4],[3,1,4,2,5],[3,5,2,4,1],[4,1,3,5,2],[4,2,5,3,1],[5,2,4,1,3],[5,3,1,4,2]].

Learning Prolog, Sudoku Solver

my problem is:
While learning Prolog i wanted to make a NxN Sudoku solver.
This solver will get the input like
[[1,2,3,4],[3,4,1,2],[2,3,4,1],[4,1,2,3]]
Where some of them might be variables. The solver has to solve that Sudoku.
The problem is way smaller:
firstElementsOf([],_).
firstElementsOf([[X|_]|Rest2],Y) :-
firstElementsOf(Rest2,Y2),
append([X],[Y2],NotFlat),
flatten(NotFlat,Y).
This should be the beginning of checking, if every column has distinct numbers. The Y from firstElementsOf should contain only the first elements of the given rows. In the Example:
[1,3,2,4]
Sadly, thanks to append, it always adds another empty space to the Y list.
It gives:
[1,3,2,4,_1320]
Question1: Is there a way to get rid of that _1320?
Question2: Is this even right? Will there be a way to get the 2nd and 3rd elements of the Input with that?
For question 1: I suppose the error is in
firstElementsOf([],_).
I think should be
firstElementsOf([],[]).
Off topic: are you sure that you can't simply write the other clause as follows?
firstElementsOf([[X|_]|Rest2],[X|Y]) :-
firstElementsOf(Rest2,Y).
For question 2: I propose a more general predicate: the following getPosList/3 with support of getPosElem/3
getPosElem([H | _], 1, H).
getPosElem([_ | T], Pos, H) :-
Pos > 1,
Pm1 is Pos - 1,
getPosElem(T, Pm1, H).
getPosList([], _, []).
getPosList([H | T], Pos, [E | L]) :-
getPosElem(H, Pos, E),
getPosList(T, Pos, L).
It extract a list of all elements in position Pos, so
getPosList([[1,2,3,4],[3,4,1,2],[2,3,4,1],[4,1,2,3]], 1, L),
it's equivalent to firstElementOf([[1,2,3,4],[3,4,1,2],[2,3,4,1],[4,1,2,3]], L) and extract [1, 3, 2, 4],
getPosList([[1,2,3,4],[3,4,1,2],[2,3,4,1],[4,1,2,3]], 2, L),
extract [2, 4, 3, 1],
getPosList([[1,2,3,4],[3,4,1,2],[2,3,4,1],[4,1,2,3]], 3, L),
extract [3, 1, 4, 2],
getPosList([[1,2,3,4],[3,4,1,2],[2,3,4,1],[4,1,2,3]], 4, L),
extract [4, 2, 1, 3] and
getPosList([[1,2,3,4],[3,4,1,2],[2,3,4,1],[4,1,2,3]], 5, L),
or a number greather than 5, return false

How to write a program that is generating a list of operational signs that have an arithmetical meaning with a given number?

I need to write a progam which is getting arguments:
list_of_numbers,
result_number,
Result_list (for generating list of signs).
And is generating a list of operation sings + and - that in arithmetical meaning have a result of result_number. Also, it does concatenation of numbers to make new ones so the arithmetical meaning would be right.
So, for example, if we have a method arrange_signs(list_of_numbers, result_number, Result_List), here's how it would work:
?- arrange_signs([1, 2, 3, 4, 5, 6, 7, 8, 9], 4, Result_List).
12-3+45-67+8-9 = 4
?- arrange_signs([1, 2, 3, 4, 5, 6, 7, 8, 9], 15, Result_List).
1+2-34+56+7-8-9 = 15
How to write a program which is doing that?
I've written a program which is doing this:
?- arrange_signs([12, 3, 45, 67, 8, 9], 4, Result_List).
12-3+45-67+8-9 = 4
?- arrange_signs([1, 2, 34, 56, 7, 8, 9], 15, Result_List).
1+2-34+56+7-8-9 = 15
But I'm not sure how to write a program that is working with [1, 2, 3, 4, 5, 6, 7, 8, 9].
Here's my code:
arrange_signs([Number|Number_List],Result,Result_List) :-
generateOperationList(Number_List, [], OperationList),
find_result(Number,Number_List,OperationList,Result),
getResult([Number|Number_List], OperationList, Result_List),
show_result(Result_List, Result).
generateOperationList([_Head|Tail], Temp_List, [First_Operation|OperationList]) :-
getSign(First_Operation),
generateOperationList(Tail, Temp_List, OperationList).
generateOperationList([],Temp_List,Temp_List).
getSign('-').
getSign('+').
getOperation(Number1, '-', Number2, Answer) :-
Answer is Number1 - Number2.
getOperation(Number1, '+', Number2, Answer) :-
Answer is Number1 + Number2.
getResult([Number|Number_List], [Operation|OperationList], [Number,Operation|Result_List]) :-
getResult(Number_List, OperationList, Result_List).
getResult(Number_List, [], Number_List).
find_result(Temp_Answer,[Number|Number_List],[Operation|OperationList],Result) :-
getOperation(Temp_Answer, Operation, Number, New_Temp_Result),
find_result(New_Temp_Result,Number_List,OperationList,Result).
find_result(New_Temp_Result,[],[],New_Temp_Result).
print_result(Ready_Result) :-
write(Ready_Result).
show_result([First_Element|Result_List],Result) :-
generate_result([First_Element|Result_List],Result,'',Ready_Result),
print_result(Ready_Result).
generate_result([First_Element|Result_List],Result,Formatting_Result,Ready_Result) :-
atom_concat(Formatting_Result, First_Element, New_Formatting_Result),
generate_result(Result_List,Result,New_Formatting_Result,Ready_Result).
generate_result([],Result,Formatting_Result,Ready_Result) :-
atom_concat(Formatting_Result, '=', Temp_Variable),
atom_concat(Temp_Variable, Result, New_Formatting_Result),
generate_result(New_Formatting_Result,Ready_Result).
generate_result(New_Formatting_Result,New_Formatting_Result).
Sorry but I find difficult to understand your code.
I propose the following solution
atomL_concat([], '').
atomL_concat([A | T], C1) :-
atomL_concat(T, C0),
atom_concat(A, C0, C1).
arrangeS([], Target, Target, [' = ', ATarget]) :-
number_atom(Target, ATarget).
arrangeS([NH | NT], Target, Sum0, ['+', ANH | ST]) :-
Sum1 is Sum0 + NH,
arrangeS(NT, Target, Sum1, ST),
number_atom(NH, ANH).
arrangeS([NH | NT], Target, Sum0, ['-', ANH | ST]) :-
Sum1 is Sum0 - NH,
arrangeS(NT, Target, Sum1, ST),
number_atom(NH, ANH).
arrangeS([NH1, NH2 | NT], Target, Sum, ResList) :-
NH is NH1 * 10 + NH2,
arrangeS([NH | NT], Target, Sum, ResList).
arrange_signs(NumList, Target, ResList) :-
arrangeS(NumList, Target, 0, ['+'|ResList]),
atomL_concat(ResList, PrintList),
write(PrintList), nl.
If you want accept solutions starting with a negative number (by example: "-12-3-4+5-6+7+8+9 = 4") you can remove the first + removal and write arrange_signs/2 as
arrange_signs(NumList, Target, ResList) :-
arrangeS(NumList, Target, 0, ResList),
atomL_concat(ResList, PrintList),
write(PrintList), nl.
but, in this case, the solutions starting with a positive number are preceded by a + sign (so "+1+2-34+5+6+7+8+9 = 4" instead of "1+2-34+5+6+7+8+9 = 4").

Solving the Numberlink puzzle with prolog

I have an assignment that seems out of scope of my class (I say this because they barely taught us anything about prolog), I have to write a prolog program to solve the game "Flow Free" on android. In the assignment it is called Numberlink. I could solve this in C++ in a hour but because I'm not too familiar with prolog it is giving me trouble. Here's what I would like to do:
Make a list that holds a boolean to indicate whether it has been visited or used.
Recursively search all possible paths from a given starting point to
the end point using a breadth first search to find the shortest
paths.
Go from there I guess.
My attempt included searching the web on how to make a list. Of course prolog is not documented well at all anywhere so I came up blank and gave up. A friend told me to use maplist which I don't understand how I would use it to make a list including what I need.
Thanks in advance.
EDIT:
Thanks for the link, but I was looking to make a 2D list to represent the board being played on. Function would look like this:
makeList(size, list):-
Where size is an integer representing the size of one dimension in the square list ex. (7x7).
Here's an implementation of #CapelliC's solution. The code is self-explanatory. 2 blocks are connected if they are adjacent and have the same color, or adjacent to another connected block of the same color. (I used X and Y instead of row and column, it made writing the conditions at the end a little confusing.)
Solving in SWI-Prolog
https://flowfreesolutions.com/solution/?game=flow&pack=green&set=5&level=1
connected(P1, P2, M, Visited) :-
adjacent(P1, P2),
maplist(dif(P2), Visited),
color(P1, C, M),
color(P2, C, M).
connected(P1, P2, M, Visited) :-
adjacent(P1, P3),
maplist(dif(P3), Visited),
color(P1, C, M),
color(P3, C, M),
connected(P3, P2, M, [P3|Visited]).
adjacent(p(X,Y1), p(X,Y2)) :- Y2 is Y1+1.
adjacent(p(X,Y1), p(X,Y2)) :- Y2 is Y1-1.
adjacent(p(X1,Y), p(X2,Y)) :- X2 is X1+1.
adjacent(p(X1,Y), p(X2,Y)) :- X2 is X1-1.
color(p(X,Y), C, M) :-
nth1(Y, M, R),
nth1(X, R, C).
sol(M) :-
M = [[1,_,_,_,1],
[2,_,_,_,_],
[3,4,_,4,_],
[_,_,_,_,_],
[3,2,5,_,5]],
connected(p(1,1), p(5,1), M, [p(1,1)]),
connected(p(1,2), p(2,5), M, [p(1,2)]),
connected(p(1,3), p(1,5), M, [p(1,3)]),
connected(p(2,3), p(4,3), M, [p(2,3)]),
connected(p(3,5), p(5,5), M, [p(3,5)]).
Sample query:
?- sol(M).
M = [[1, 1, 1, 1, 1],
[2, 2, 2, 2, 2],
[3, 4, 4, 4, 2],
[3, 2, 2, 2, 2],
[3, 2, 5, 5, 5]].
The declarative Prolog 'modus operandi' is based on non determinism, implemented by depth first search. Let's apply to this puzzle: M is the playground, a list of lists of free cells (variables) or integers (colors)
one_step(M) :-
cell(M, X,Y, C),
integer(C), % the selected cell is a color
delta(X,Y,X1,Y1),
cell(M, X1,Y1, C). % bind adjacent to same color - must be free
cell(M, X,Y, C) :- nth1(Y,M,R), nth1(X,R,C).
% moves
delta(X,Y,X1,Y) :- X1 is X+1. % right
delta(X,Y,X1,Y) :- X1 is X-1. % left
delta(X,Y,X,Y1) :- Y1 is Y-1. % up
delta(X,Y,X,Y1) :- Y1 is Y+1. % down
what this does ? let's try on a 3x3 playground
?- M=[[_,9,_],[_,0,_],[_,_,9]],one_step(M).
M = [[_G1824, 9, 9], [_G1836, 0, _G1842], [_G1848, _G1851, 9]] ;
M = [[9, 9, _G1830], [_G1836, 0, _G1842], [_G1848, _G1851, 9]] ;
M = [[_G1824, 9, _G1830], [_G1836, 0, 0], [_G1848, _G1851, 9]] ;
M = [[_G1824, 9, _G1830], [0, 0, _G1842], [_G1848, _G1851, 9]] ;
M = [[_G1824, 9, _G1830], [_G1836, 0, _G1842], [_G1848, 0, 9]] ;
M = [[_G1824, 9, _G1830], [_G1836, 0, _G1842], [_G1848, 9, 9]] ;
M = [[_G1824, 9, _G1830], [_G1836, 0, 9], [_G1848, _G1851, 9]] ;
false.
No need to declare grid size, check index boundaries, etc... when one_step/1 succeeds it has instantiated a free cell to an adjacent same color...

Resources