Prolog project. Labyrinth. Checking if next move is possible - prolog

Noob at prolog.
I need to do a school project related to a labyrinth.
My question is:
In the project I need to make a function "possible moves".
It gets a labyrinth, a current position and previous moves
Lab is represented by (these are the walls positions):
[[[down,left,up],[left,down,up],[right,up],[up],[,up],[right,left,up]],
[[left,down],[down,up],[down,up],[],[down],[right,down]],
[[left,up],[down,up],[down,up],[down],[down,up],[right,down,up]],
[[right,left],[left,up],[up],[up],[up],[right,up]],
[[left,down],[right,down],[left,down],[down],[down],[right,down]]]
And Poss_moves like:
Poss_moves(Lab, current_poss, previous_moves, possible_moves)
which is called as follows.
?- ..., poss_moves(Lab1, (2,5),
[(beginning, 1, 6), (down, 2, 6), (left, 2, 5)], possible_moves).
Lab1 = ...,
Poss = [ (up, 1, 5), (left, 2, 4)].
Important:
--- You can only move up, down, left or right.
PS: Sorry for my bad english.
PS: Edited.
PS: Can I do in prolog:
distance((Line1, Column1), (Line2, Column2), Dist) :-
Dist is abs(Line1 - Line2) + abs(Column1 - Column2).
PS: The lab that matches the picture.
[[[right,left,up],[left,down,up],[down,up],[up],[right,up],[right,left,up]],
[[left,down],[down,up],[b,up],[],[down],[right,down]],
[[left,up],[down,up],[down,up],[down],[down,up],[right,down,up]],
[[right,left],[left,up],[up],[up],[up],[right,up]],
[[left,down],[right,down],[left,down],[down],[down],[right,down]]]
By the way, the lab can change.
Thanks
EDIT 2:
I made this changes:
% predicates
lookup(Lab,(X,Y),Walls)
calc(Direction,(X1,Y1),(X2,Y2)
map_calc((X,Y),L,R)
poss_moves(Lab, (X,Y), PreviousMoves, PossibleMoves)
% clauses
nth(1, [H|T], H).
nth(N,[_|T],R) :-
M is N-1,
nth(M,T,R).
lookup(Lab, (X, Y), Walls) :-
nth(N,Lab,R),
Y == R,
X == Walls.
calc(up,(X,Y1),(X,Y2)) :-
Y2 is Y1-1.
calc(down,(X,Y1),(X,Y2)) :-
Y2 is Y1+1.
calc(left,(X,Y1),(X,Y2)) :-
X2 is X1-1.
calc(right,(X,Y1),(X,Y2)) :-
X2 is X1+1.
map_calc(_,[],[]).
map_calc((X,Y),[H|T],[(H,X1,Y1)|S]) :-
calc(H,(X,Y),(X1,Y1)),
map_calc((X,Y),T,S).
% main predicates
poss_moves(Lab, (X,Y), PreviousMoves, PossibleMoves) :-
lookup(Lab, (X,Y), Walls),
map_calc((X,Y), Lab, PossibleMoves).
I'm almost 100% sure that the lookup is incorrect.
Thanks

The first thing you need to do is define a way of looking up a cell in the Labryinth datastructure. You need something like:
lookup(Lab,(X,Y),Walls)
which is true if Walls is the list of walls present at cell (X,Y) in Lab. To do this you'll need an 'nth' predicate which finds the nth element of the list.
nth(1,[H|T],H).
nth(N,[_|T],R) :- M is N-1, nth(M,T,R).
Normally one would use 0 to return the first element of a list but your maze co-ordinates start at (1,1) so I've made nth do the same.
Now you can build lookup(Lab,(X,Y),Walls) which is true if the Yth element of Lab is Row and the Xth element of Row is Walls.
Next you need a way of turning the list of Walls in to a list of possible moves. A move consists of a direction and the co-ordinates of the new position so first write some helpers to calculate the new co-ordinates:
calc(Direction,(X1,Y1),(X2,Y2)
should be true if (X2,Y2) is the co-ordinate you get to if you move in Direction from (X1,Y1). Here is an example clause of calc, the others are similar:
calc(up,(X,Y1),(X,Y2)) :- Y2 is Y1-1.
But we need to apply calc to every element of the list of Walls to get the list of moves (this is called 'map' in functional programming)
map_calc((X,Y),L,R)
should be true if R is the result of applying calc to every direction in L starting from co-ordinate (X,Y).
map_calc(_,[],[]).
map_calc((X,Y),[H|T],[(H,X1,Y1)|S]) :- calc(H,(X,Y),(X1,Y1)),
map_calc((X,Y),T,S).
Now you can write poss_moves:
poss_moves(Lab, (X,Y), PreviousMoves, PossibleMoves)
i.e. PossibleMoves is the list of moves you can make from (X,Y) given that lookup(Lab,(X,Y),Walls) is true, and that map_calc on Walls gives you PossibleMoves.

Related

Sliding tile puzzle with varying tile size using logic programming

So I am trying to solve this Booth arrangement problem given here. It is basically a sliding tile puzzle where one (booth)tile has to reach a target spot and in the end all other (booths)tiles should be in their original location. Each tile/booth has a dimension and following are the input fact and relation descriptions:
One fact of the form room(W,H), which specifies the width W and
height H of the room (3 ≤ W, H ≤ 20).
One fact booths(B), which
specifies the number of booths (1 ≤ B ≤ 20).
A relation that consists
of facts of the form dimension(B, W, H), which specifies the width W
and height H of booth B.
A relation consisting of facts of the form
position(B, W, H), specifying the initial position (W, H) of booth B.
One fact target(B, W, H), specifying the destination (W, H) of the
target booth B.
An additional fact horizon(H) gives an upper bound on
the number of moves to be performed.
The program is supposed to read input facts from a file but I am just trying to do the solving so I have just copy pasted one possible input for now, and I have written some basic clauses:
room(3, 3).
booths(3).
dimension(1, 2, 1).
dimension(2, 2, 1).
dimension(3, 1, 1).
position(1, 0, 1).
position(2, 1, 2).
position(3, 0, 0).
target(3, 0, 2).
horizon(10).
xlim(X) :- room(X,_).
ylim(X) :- room(_,X).
sum(X,Y,Z) :- Z is X+Y .
do(position(B,X,Y),movedown,position(B,X,Z)) :- Y > 0 , sum(Y,-1,Z) .
do(position(B,X,Y),moveup,position(B,X,Z)) :- ylim(L), Y < L , sum(Y,1,Z) .
do(position(B,X,Y),moveleft,position(B,Z,Y)) :- X > 0 , sum(X,-1,Z) .
do(position(B,X,Y),moveright,position(B,Z,Y)) :- xlim(L), X < L, sum(X,1,Z) .
noverlap(B1,B2) :-
position(B1,X1,Y1),
position(B2,X2,Y2),
ends(Xe1,Ye1,B1),
ends(Xe2,Ye2,B2),
( Xe1 < X2 ;
Xe2 < X1 ;
Ye1 < Y2 ;
Ye2 < Y1 ).
ends(Xe,Ye,B) :-
dimension(B,W,H),
position(B,X,Y),
Xe is X+W-1,
Ye is Y+H-1.
between(X,Y,Z) :-
X > Y ,
X < Z .
validMove(M,B) :- do(position(B,X,Y),M,position(B,Xn,Yn)) .
I am new to Prolog and I am stuck on how to go from here, I have the no_overlap rule so I can test if a move is valid or not but I am not sure how with the current clauses that I have. My current clauses for moves do/3 probably needs some modification. Any pointers?.
You need to express the task in terms of relations between states of the puzzle. Your current clauses determine the validity of a single move, and can also generate possible moves.
However, that is not sufficient: You need to express more than just a single move and its effect on a single tile. You need to encode, in some way, the state of the whole puzzle, and also encode how a single move changes the state of the whole task.
For a start, I recommend you think about a relation like:
world0_move_world(W0, M, W) :- ...
and express the relation between a given "world" W0, a possible move M, and the resulting world W. This relation should be so general as to generate, on backtracking, each move M that is possible in W0. Ideally, it should even work if W0 is a free variable, and for this you may find clpfd useful: Constraints allow you to express arithmetic relations in a much more general way than you are currently using.
Once you have such a relation, the whole task is to find a sequence Ms of moves such that any initial world W0 is transformed to a desired state W.
Assuming you have implemented world0_move_world/3 as a building block, you can easily lift this to lists of moves as follows (using dcg):
moves(W0) --> { desired_world(W0) }.
moves(W0) --> [M], { world0_move_world(W0, M, W) }, moves(W).
You can then use iterative deepening to find a shortest sequence of moves that solves the puzzle:
?- length(Ms, _), initial_world(W0), phrase(moves(W0), Ms).

for-loop in prolog doesn't work

I'm trying to do a for loop in Prolog which doesn't work. The program should do following:
generate(N,[S,E],FinalSegments):-
segments([S,E],Initialsegments),
iterate_level(N,Initialsegments,FinalSegments).
When I'm calling generate(5,[(10,0),(-10,0)]) the first step is, that generate(5,[(10,0),(-10,0)],FinalSegments) is being called and this predicate generates 4 coordinates between the Startpoint (10,0) and the Endpoint (-10,0), and store those four ccordinates in the listFinalSegments. This is actually done correctly. In the next step the predicate iterate_level(N,Initialsegments,FinalSegments) is being called.
The predicate iterate_level(), takes the four coordinates from the last step as a list called Initialsegments:
Initialsegments:([[ (10, 0), (3.333333333333333, 0)],
[ (3.3333, 0), (-3.5527e-15, -5.7735)],
[ (-3.5527e-15, -5.77350), (-3.3333, 0)],
[(-3.3333, 0), (-10, 0)]])
And now iterate_level(5,Initialsegments,FinalSegments) should be a for-loop which should generate 16 coordinates after the first iteration, then 64 coordinates after the second iteration...
But here is my problem that this is not really working and I don't know what I'm still doing wrong.
It seem to me, when I'm looking at the trace, that
iterate_level(N,Ls,F):-
seq(0,N,Index),
next_level_segments(Index,Ls,F).
when next_level_segments(Index,Ls,F) is called within the for-loop, the list
Ls which should contain four times more coordinates after each iteration is not refreshed.(Maybe this is the problem).
When I call generate(3,[[(60,0),(-60,0)]],X). I get four times always the same 16 coordinates as a result but I should get 1024 different coordinates.
Maybe someone may have some time to have a look at this problem an give me some help.
Thanks
This is my implementation until now:
generate(N,[S,E],FinalSegments):-
segments([S,E],Initialsegments),
iterate_level(N,Initialsegments,FinalSegments).
generate(N,[],[]).
seq(From,_,From).
seq(From,To,X) :-
From<To,
Next is From+1,
seq(Next,To,X).
iterate_level(N,Ls,F):-
seq(0,N,Index),
next_level_segments(Index,Ls,F).
%fail.
iterate_level(Ls,F).
iterate_level([],[]).
segments([(Sx,Sy),(Ex,Ey)],Ls):-
X2 is Sx+(Ex-Sx)/3,
Y2 is Sy+(Ey-Sy)/3,
R1 is sqrt((X2-Sx)*(X2-Sx)+(Y2-Ey)*(Y2-Ey)),
Phi1 is atan((Y2-Sy)/(X2-Sx)),
X3 is X2 +R1*cos((Phi1-240)*pi/180),
Y3 is Y2 +R1*sin((Phi1+240)*pi/180),
X4 is X2+(X2-Sx),
Y4 is Y2+(Y2-Sy),
Ls=[
[(Sx,Sy),(X2,Y2)],
[(X2,Y2),(X3,Y3)],
[(X3,Y3),(X4,Y4)],
[(X4,Y4),(Ex,Ey)]
].
next_level_segments(N,[[(Sx,Sy),(Ex,Ey)]|E],[X|RLs]):-
segments([(Sx,Sy),(Ex,Ey)],X),
next_level_segments(N,E,RLs).
next_level_segments(N,[],[]).
So, first of all, Prolog doesn't do for-loops in the traditional sense - what you actually want to do is recurse over the list with an accumulator. This can be achieved as follows.
Firstly, generate(0, [[X|Y]|Z], [[X|Y]|Z]) :- !., which says "if I am trying to generate the 0th iteration of a list of lists, I have achieved my goal and I should succeed". This also cuts, as there is only ever going to be a single solution here.
generate(N, [[P1, P2]|Tail], Final) does the main body of the (outer) recursion. As long as this is a positive iteration (ie exclude negatives), we do_generate an iteration of coordinates, and recurse for another iteration (with iteration 0 succeeding as above).
do_generate([],[]). states that if we're trying to generate coordinates between an empty list, we're done for this level.
do_generate([Current|Rest], Interim) takes the first pair of coordinates and generates the set of four pairs of coordinates (using segments([(Sx,Sy),(Ex,Ey)],Ls) as before), then recurses onto the rest of the list of coordinates. Once we reach the above base-case, we append all the lists together from last to first to get the new set of coordinates. This is then unified with Interim, to send back to generate(N, [[P1, P2]|Tail], Final) for further recursion or unification with Final using the outer base-case.
As a caveat, in order to get output looking like input for the final base-case, the input is now required to be a list of lists of coordinate-pairs, not just a list of coordinate-pairs.
All put together, you get the following:
generate(0, [[X|Y]|Z], [[X|Y]|Z]) :- !.
generate(N, [[P1, P2]|Tail], Final) :-
N > 0,
do_generate([[P1, P2]|Tail], Interim),
N1 is N-1,
generate(N1, Interim, Final).
do_generate([], []).
do_generate([Current|Rest], Interim) :-
segments(Current, Segs),
do_generate(Rest, RestSegs),
append(Segs, RestSegs, Interim).
segments([(Sx,Sy), (Ex,Ey)], Ls) :-
X2 is Sx+(Ex-Sx)/3,
Y2 is Sy+(Ey-Sy)/3,
R1 is sqrt((X2-Sx)*(X2-Sx)+(Y2-Ey)*(Y2-Ey)),
Phi1 is atan((Y2-Sy)/(X2-Sx)),
X3 is X2+R1*cos((Phi1-240)*pi/180),
Y3 is Y2+R1*sin((Phi1+240)*pi/180),
X4 is X2+(X2-Sx),
Y4 is Y2+(Y2-Sy),
Ls=[[(Sx,Sy),(X2,Y2)],
[(X2,Y2),(X3,Y3)],
[(X3,Y3),(X4,Y4)],
[(X4,Y4),(Ex,Ey)]].

How I can add all the data in a knowledge base in Prolog?

I need help with this exercise of Prolog:
% items
items (cell).
items (labial).
items (control).
items (mirror).
% Weight of each item
weight (cell 2).
weight (labial, 3).
weight (control, 5).
weight (mirror, 10).
capacity (X, Y, Z, V) :-
weight (X C1), weight (Y, C2), weight (Z, C3), sum (C1, C2, C3, R), V> = R.
sum (X, Y, Z, K) :- K is X + Y + Z.
this program does is give me a combination of 3 items or less a given weight, eg capacity (X, Y, Z, 15).
result is, X: cell, Y: Lipstick, Z: mirror, X: control, Y: cell, Z: mirror. successively with all combinations where the sum of the 3 weight no higher input.
At the moment I am limited by the number of income variables manually, capacity (X, Y, Z, N. .......) I want that the combination with respect to number of items that are in the knowledge base, not manually enter the variables. How I can do that?
so would be ideal capacity (weight) and response.
the combination of items where the weight does not exceed
phone, lipstick, mirror.
control labial phone.
mirror, control, labilal .......
Sorry I do not speak English, I'm using google translator.
It looks like the structure that you are looking for is a list and you should look it up ([HD:TL] where TL is a list). The solution I provide below should show how to use lists to the desired effect although my solution allows duplicates and doesn't care about the order of the list. If this is for homework, you should be able to figure out how to fix that on your own once you know a little about lists.
Basically, the first predicate handles making long lists of items, HD is an item with weight X, and it looks for a predicate to define the tail (TL) list; the second handles the end of a list (TL is [] in the previous inductive step) and empty lists (because even empty lists need love too).
items(cell).
items(labial).
items(control).
items(mirror).
weight(cell,2).
weight(labial,3).
weight(control,5).
weight(mirror,10).
capacity(W, [HD|TL]) :- items(HD),weight(HD,X),W>0,capacity(W-X,TL).
capacity(W, []) :- W>=0.

Some explanation about 8 queen solution that use permutation

I am studyin Prolog for an universitary exam on SWI Prolog implementation.
I have some doubt about how work this version of 8 Queen problem that solve the problem using the permutations:
solution(Queens) :-
permutation([1,2,3,4,5,6,7,8], Queens),
safe(Queens).
permutation([],[]).
permutation([Head|Tail],PermList) :-
permutation(Tail,PermTail),
del(Head,PermList,PermTail).
del(Item,[Item|List],List).
del(Item,[First|List],[First|List1]) :-
del(Item,List,List1).
safe([]).
safe([Queen|Others]) :-
safe(Others),
noattack(Queen,Others,1).
noattack(_,[],_).
noattack(Y,[Y1|Ylist],Xdist) :-
Y1-Y=\=Xdist,
Y-Y1=\=Xdist,
Dist1 is Xdist + 1,
noattack(Y,Ylist,Dist1).
In a previous resolution I used this solution template: [1\Y1, 2\Y2, 3\Y3, 4\Y4, 5\Y5, 6\Y6, 7\Y7, 8\Y8] that simply say that every queen have to be on different rows the X calue could be constrained.
This version of the program it's pretty difference because we can observe that to prevent vertical attack all the queens have to be on different rows, so I have a queen for each row.
So without losing information so I can say that the solution will be a permutation of the list: [1,2,3,4,5,6,7,8] in wich every element rappresent the Y coordinate of a queen.
so in this case I am rappresenting a queen position only by its Y coordinate (its row index)
So I have the solution relation than say that a list Queens is a solution if Queens is a premutation of [1,2,3,4,5,6,7,8] original list and if this permutation is safe (every queen in this permutation list don't attack the others queen).
Ok, this is clear...now it is defined the safe relation.
There is a base case that say that if the list is empty then this is safe because there is no attack.
And there is a general case in wich the list is not empty. If the list is not empty I can divide it in [Queen|Others] where Queen is the first queen in the list and Others is the remaining sublist...so the original list [Queen|Others] is safe if the sublist Others it is itself a solution (if it is safe, if there is no attack in Others) and if the first item Queen do not attack the other queen in others sublist...
Ok...until now I think that it is clear for me...
Now I have some problems with the definition of noattack relation !!!
The problem is that I rappresent a queen position only by its Y coordinate and the X coordinate it is
not explicity present.
I have understand that for circumvent this limitation of the rappresentation there is the folowing generalizzation (I hope to have understood well...I am not so sure...): ** I know also that there must be a queen on each column of the board, so the X distance from the first queen in the list (Queen) and the sublist Others must be 1**
Where the distance from the first item Queen and the sublist Others is the X distance from the first item Queen and the queen nearest to it, is it righ my reasoning?
So the noattack relation is TRUE if the queen are on different columns (always true for construction) and I can express that have to be on different row sayng that the X distance from Queen and the nearest queen in the Others sublist is 1.
But, if my reasoning is truem I am finding many trouble trying to understand how rappresent this thing in this part of code:
noattack(Y,[Y1|Ylist],Xdist) :-
Y1-Y=\=Xdist,
Y-Y1=\=Xdist,
Dist1 is Xdist + 1,
noattack(Y,Ylist,Dist1).
i think your problem is only these 2 lines:
Y-Y1=/=Xdist,
Y1-Y=/=Xdist,
it checks whether the queen which has Y, attacks the queen in the row with a distance of Xdist in a diagonal way or not. (|Y - Y1| = Xdist --> diagonal attack ).
the other way to attack other queens is to qttack them in the same row, that doesn't happen simply because the solution is a permutation of [1,2,3,4,5,6,7,8]. so something like [1,1,3,4,5,6,7,8] never happens and thats enough to check diagonals.
I hope it solved the problem.
p.s. note that Y1 is the Y coordinate of the head of the Others from the rule Safe/1. so it is a queen with a Xdist of 1 at first, then it backtracks to other rows.
clarification
we are talking about noattack/3. you give it three arguments:
first: Y coordinate of current queen,
second: a right-most list [Y1| Ylist] the begins somewhere after where Y is in the list, and continues to the end of the primary list.(yes this is a sublist of the solution), and
third: Xdist is the index-distance between current queen (which has Y) and the queen which is gonna be checked with the current queen (which has Y1 and is the head of second argument).
third argument is necessary because without it we can not check diagonal interaction between the current queen and and the queen which has Y1. it is really basic mathematics, you have 2 points in the coordinate system with only natural numbers. lets say these 2 points attack each other in a diagonal way if and only-if abs(x1 - x2) = abs(y1 - y2).
Example #1 . and if you understood my explanations well, check it as Accepted.
P1 = (3, 4) and P2 = (1, 2) --> these points have a diagonal attack because abs(3-1) = abs(4-2) = 2
Example #2
P1 = (3, 4) and P2 = (7, 0) --> these points have a diagonal attack because abs(3-7) = abs(4-0) = 4
this is why we check both Y1-Y =\= Xdist and Y-Y1 =\= Xdist . because whenever there is a diagonal attack at least one of them is gonna be true. when non of them was true, it means that there is no diagonal attack between the queen with Y and the queen with Y1. so we can proceed to checking the next queen which is the head of Ylist.
I hope that's enough. this is a very easy problem, Just read it carefully and if you couldn't understand again try to trace the algorithm on a piece of paper yourself. that's a way always works.
I have the same confusion when solving the same problem in recursion using C. There are two possible directions for diagonal attacks. Number each square with x,y coordinates with top-left being 0,0. You will then see that these two attacking diagonal detection or calculation satisfies the conditions:
x1 - y1 == x2 - y2
x1 + y1 == x2 + y2
I annotated the comparison above in C equality notation and not Prolog notation. Between two points (x1,y1) and (x2,y2), there is queen diagonal attack if one of conditions above is meet.

Solve Cannibals/Missionaries using breadth-first search (BFS) in Prolog?

I am working on solving the classic Missionaries(M) and Cannibals(C) problem, the start state is 3 M and 3 C on the left bank and the goal state is 3M, 3C on the right bank. I have complete the basic function in my program and I need to implemet the search-strategy such as BFS and DFS.
Basically my code is learn from the Internet. So far I can successfuly run the program with DFS method, but I try to run with BFS it always return false. This is my very first SWI-Prolog program, I can not find where is the problem of my code.
Here is part of my code, hope you can help me find the problem of it
solve2 :-
bfs([[[3,3,left]]],[0,0,right],[[3,3,left]],Solution),
printSolution(Solution).
bfs([[[A,B,C]]],[A,B,C],_,[]).
bfs([[[A,B,C]|Visisted]|RestPaths],[D,E,F],Visisted,Moves) :-
findall([[I,J,K],[A,B,C]|Visited]),
(
move([A,B,C],[I,J,K],Description),
safe([I,J,K]),
not(member([I,J,K],Visited)
),
NewPaths
),
append(RestPaths,NewPaths,CurrentPaths),
bfs(CurrentPaths,[D,E,F],[[I,J,K]|Visisted],MoreMoves),
Moves = [ [[A,B,C],[I,J,K],Description] | MoreMoves ].
move([A,B,left],[A1,B,right],'One missionary cross river') :-
A > 0, A1 is A - 1.
% Go this state if left M > 0. New left M is M-1
.
.
.
.
.
safe([A,B,_]) :-
(B =< A ; A = 0),
A1 is 3-A, B1 is 3-B,
(B1 =< A1; A1 =0).
I use findall to find all possible path before go to next level. Only the one pass the safe() will be consider as possible next state. The state will not use if it already exist. Since my program can run with DFS so I think there is nothing wrong with move() and safe() predicate. My BFS predicate is changing base on my DFS code, but its not work.
There is a very simple way to turn a depth-first search program into a breadth-first one, provided the depth-first search is directly mapped to Prolog's search. This technique is called iterative deepening.
Simply add an additional argument to ensure that the search will only go N steps deep.
So a dfs-version:
dfs(State) :-
final(State).
dfs(State1) :-
state_transition(State1, State2),
dfs(State2).
Is transformed into a bfs by adding an argument for the depth. E.g. by using successor-arithmetics:
bfs(State, _) :-
final(State).
bfs(State1, s(X)) :-
state_transition(State1, State2),
bfs(State2, X).
A goal bfs(State,s(s(s(0)))) will now find all derivations requiring 3 or less steps. You still can perform dfs! Simply use bfs(State,X).
To find all derivations use natural_number(X), bfs(State,X).
Often it is useful to use a list instead of the s(X)-number. This list might contain all intermediary states or the particular transitions performed.
You might hesitate to use this technique, because it seems to recompute a lot of intermediary states ("repeatedly expanded states"). After all, first it searches all paths with one step, then, at most two steps, then, at most three steps... However, if your problem is a search problem, the branching factor here hidden within state_transition/2 will mitigate that overhead. To see this, consider a branching factor of 2: We only will have an overhead of a factor of two! Often, there are easy ways to regain that factor of two: E.g., by speeding up state_transition/2 or final/1.
But the biggest advantage is that it does not consume a lot of space - in contrast to naive dfs.
The Logtalk distribution includes an example, "searching", which implements a framework for state space searching:
https://github.com/LogtalkDotOrg/logtalk3/tree/master/examples/searching
The "classical" problems are included (farmer, missionaries and cannibals, puzzle 8, bridge, water jugs, etc). Some of the search algorithms are adapted (with permission) from Ivan Bratko's book "Prolog programming for artificial intelligence". The example also includes a performance monitor that can give you some basic stats on the performance of a search method (e.g. branching factors and number of state transitions). The framework is easy to extend, both for new problems and new search methods.
If anyone still interested in this for a python solution please find the following.
For the simplification, count of Missionaries and Cannibals on left is only taken to the consideration.
This is the solution tree.
#M #missionaries in left
#C #cannibals in left
# B=1left
# B=0right
def is_valid(state):
if(state[0]>3 or state[1]>3 or state[2]>1 or state[0]<0 or state[1]<0 or state[2]<0 or (0<state[0]<state[1]) or (0<(3-state[0])<(3-state[1]))):
return False
else:
return True
def generate_next_states(M,C,B):
moves = [[1, 0, 1], [0, 1, 1], [2, 0, 1], [0, 2, 1], [1, 1, 1]]
valid_states = []
for each in moves:
if(B==1):next_state = [x1 - x2 for (x1, x2) in zip([M, C, B], each)]
else:next_state = [x1 + x2 for (x1, x2) in zip([M, C, B], each)]
if (is_valid(next_state)):
# print(next_state)
valid_states.append(next_state)
return valid_states
solutions = []
def find_sol(M,C,B,visited):
if([M,C,B]==[0,0,0]):#everyne crossed successfully
# print("Solution reached, steps: ",visited+[[0,0,0]])
solutions.append(visited+[[0,0,0]])
return True
elif([M,C,B] in visited):#prevent looping
return False
else:
visited.append([M,C,B])
if(B==1):#boat is in left
for each_s in generate_next_states(M,C,B):
find_sol(each_s[0],each_s[1],each_s[2],visited[:])
else:#boat in in right
for each_s in generate_next_states(M,C,B):
find_sol(each_s[0],each_s[1],each_s[2],visited[:])
find_sol(3,3,1,[])
solutions.sort()
for each_sol in solutions:
print(each_sol)
Please refer to this gist to see a possible solution, maybe helpful to your problem.
Gist: solve Missionaries and cannibals in Prolog
I've solved with depth-first and then with breadth-first, attempting to clearly separate the reusable part from the state search algorithm:
miss_cann_dfs :-
initial(I),
solve_dfs(I, [I], Path),
maplist(writeln, Path), nl.
solve_dfs(S, RPath, Path) :-
final(S),
reverse(RPath, Path).
solve_dfs(S, SoFar, Path) :-
move(S, T),
\+ memberchk(T, SoFar),
solve_dfs(T, [T|SoFar], Path).
miss_cann_bfs :-
initial(I),
solve_bfs([[I]], Path),
maplist(writeln, Path), nl.
solve_bfs(Paths, Path) :-
extend(Paths, Extended),
( member(RPath, Extended),
RPath = [H|_],
final(H),
reverse(RPath, Path)
; solve_bfs(Extended, Path)
).
extend(Paths, Extended) :-
findall([Q,H|R],
( member([H|R], Paths),
move(H, Q),
\+ member(Q, R)
), Extended),
Extended \= [].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% problem representation
% independent from search method
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
initial((3,3, >, 0,0)).
final((0,0, <, 3,3)).
% apply a *valid* move
move((M1i,C1i, Bi, M2i,C2i), (M1f,C1f, Bf, M2f,C2f)) :-
direction(Bi, F1, F2, Bf),
who_move(MM, CM),
M1f is M1i + MM * F1, M1f >= 0,
C1f is C1i + CM * F1, C1f >= 0,
( M1f >= C1f ; M1f == 0 ),
M2f is M2i + MM * F2, M2f >= 0,
C2f is C2i + CM * F2, C2f >= 0,
( M2f >= C2f ; M2f == 0 ).
direction(>, -1, +1, <).
direction(<, +1, -1, >).
% valid placements on boat
who_move(M, C) :-
M = 2, C = 0 ;
M = 1, C = 0 ;
M = 1, C = 1 ;
M = 0, C = 2 ;
M = 0, C = 1 .
I suggest you to structure your code in a similar way, with a predicate similar to extend/2, that make clear when to stop the search.
If your Prolog system has a forward chainer you can also solve
the problem by modelling it via forward chaining rules. Here
is an example how to solve a water jug problem in Jekejeke Minlog.
The state is represented by a predicate state/2.
You first give a rule that filters duplicates as follows. The
rule says that an incoming state/2 fact should be removed,
if it is already in the forward store:
% avoid duplicate state
unit &:- &- state(X,Y) && state(X,Y), !.
Then you give rules that state that search need not be continued
when a final state is reached. In the present example we check
that one of the vessels contains 1 liter of water:
% halt for final states
unit &:- state(_,1), !.
unit &:- state(1,_), !.
As a next step one models the state transitions as forward chaining
rules. This is straight forward. We model emptying, filling and pouring
of vessels:
% emptying a vessel
state(0,X) &:- state(_,X).
state(X,0) &:- state(X,_).
% filling a vessel
state(5,X) &:- state(_,X).
state(X,7) &:- state(X,_).
% pouring water from one vessel to the other vessel
state(Z,T) &:- state(X,Y), Z is min(5,X+Y), T is max(0,X+Y-5).
state(T,Z) &:- state(X,Y), Z is min(7,X+Y), T is max(0,X+Y-7).
We can now use the forward chaining engine to do the job for us. It
will not do iterative deeping and it will also not do breadth first.
It will just do unit resolution by a strategy that is greedy for the
given fact and the process only completes, since the state space
is finite. Here is the result:
?- post(state(0,0)), posted.
state(0, 0).
state(5, 0).
state(5, 7).
state(0, 7).
Etc..
The approach will tell you whether there is a solution, but not explain
the solution. One approach to make it explainable is to use a fact
state/4 instead of a fact state/2. The last two arguments are used for
a list of actions and for the length of the list.
The rule that avoids duplicates is then changed for a rule that picks
the smallest solution. It reads as follows:
% choose shorter path
unit &:- &- state(X,Y,_,N) && state(X,Y,_,M), M<N, !.
unit &:- state(X,Y,_,N) && &- state(X,Y,_,M), N<M.
We then get:
?- post(state(0,0,[],0)), posted.
state(0, 0, [], 0).
state(5, 0, [fl], 1).
state(5, 7, [fr,fl], 2).
state(0, 5, [plr,fl], 2).
Etc..
With a little helper predicate we can force an explanation of
the actions that lead to a path:
?- post(state(0,0,[],0)), state(1,7,L,_), explain(L).
0-0
fill left vessel
5-0
pour left vessel into right vessel
0-5
fill left vessel
5-5
pour left vessel into right vessel
3-7
empty right vessel
3-0
pour left vessel into right vessel
0-3
fill left vessel
5-3
pour left vessel into right vessel
1-7
Bye
Source Code: Water Jug State
http://www.xlog.ch/jekejeke/forward/jugs3.p
Source Code: Water Jug State and Path
http://www.xlog.ch/jekejeke/forward/jugs3path.p

Resources