So I recently started to learn Prolog and I had a question regarding making a predicate which gives u all the possible solutions (next moves) as the current spot is given. The best example is a maze. So this is my data which tells me 'w = white' and 'b = black' within a 5x5 maze:
grid([ [w, w, w, b, w],
[b ,b, w, w, w],
[w, w, w, b, w],
[w, b, b, b, b],
[w, w, w, w, w] ]).
I also implemented a predicated called white/1, which tells me if the given spot in the maze is white:
white(X/Y) :-
grid(M),
nth1(X, M, Line),
nth1(Y, Line, w).
What I now want to do is make a predicate that gives me all the possible moves within the maze. For example if I query:
?- move(5/2, NextState).
NextState = 4/2 ;
NextState = 5/1 ;
NextState = 5/3 ;
No
This is my code, but it gives false and I know it's completely wrong, but I don't know how to implement such a predicate:
move(5/5, _).
move(X/Y, NextState) :-
white(X/Y),
X1 is X + 1,
X1 =< 5,
move(X1/Y, NextState),
Y1 is Y + 1,
Y1 =< 5,
move(X/Y1, NextState),
X2 is X - 1,
X2 >= 5,
move(X2/Y, NextState),
Y2 is Y - 1,
Y2 >= 0,
move(X/Y2, NextState).
If someone could help me, I would really appreciate it! :)
EDIT:
move(X/Y, _) :-
white(X/Y).
move(X/Y, X/Y) :-
X1 is X + 1,
move(X/Y, X1/Y);
X2 is X - 1,
move(X/Y, X2/Y);
Y1 is Y + 1,
move(X/Y, X/Y1);
Y2 is Y - 1,
move(X/Y, X/Y2).
If I query it gives me:
?- move(3/3, NextState).
true ;
NextState = 3/3 ;
NextState = 3/3 ;
NextState = 3/3 ;
NextState = 3/3 ;
move(X/Y, X1/Y1, Xm/Ym) :-
X1 is X + 1, Y1 = Y, X1 =< Xm;
X1 is X - 1, Y1 = Y, X1 > 0;
X1 = X, Y1 is Y + 1, Y1 =< Ym;
X1 = X, Y1 is Y - 1, Y1 > 0.
For each line in the above predicate we have a new direction. The Xm/Ym are bounds of the maze.
| ?- move(5/2, X, 5/5).
X = 4/2 ? ;
X = 5/3 ? ;
X = 5/1
yes
You can remove Xm/Ym as an argument and just put 5 in the body. Or you can define a new predicate
move1(Current, Next) :- move(Current, Next, 5/5)
Instead of disjunctions, we can also write multiple clauses.
move(X/Y, X1/Y) :- X1 is X + 1, X1 =< 5.
move(X/Y, X1/Y) :- X1 is X - 1, X1 > 0.
move(X/Y, X/Y1) :- Y1 is Y + 1, Y1 =< 5.
move(X/Y, X/Y1) :- Y1 is Y - 1, Y1 > 0.
Both are equivalent and this is even clearer.
I've written prolog program which takes initial, pickup and final (x,y,z) coordinates. The agent should reach the final coordinates through the pickup coordinates. When I run the query, the program runs indefinitely. I'm assuming this is because there are a huge number of combinations to search through. So i decreased my constraints in in_range(X,Y,Z). I'm new to prolog, any help would be appreciated. Thanks
in_range(X,Y,Z):-
X > -5,
X < 5,
Y >= 0,
Y < 10,
Z > -5,
Z < 5.
deliver(Ix,Iy,Iz,Px,Py,Pz,Fx,Fy,Fz):-
in_range(Ix,Iy,Iz),
in_range(Px,Py,Pz),
in_range(Fx,Fy,Fz),
move(Ix,Iy,Iz,Px,Py,Pz),
move(Px,Py,Pz,Fx,Fy,Fz).
move(X1,Y1,Z1,X2,Y2,Z2):-
X is X1-1,
Y is Y1,
Z is Z1,
in_range(X,Y,Z),
X =:= X2,
Y =:= Y2,
Z =:= Z2;
X is X1-1,
Y is Y1,
Z is Z1,
in_range(X,Y,Z),
move(X,Y,Z,X2,Y2,Z2).
move(X1,Y1,Z1,X2,Y2,Z2):-
X is X1,
Y is Y1-1,
Z is Z1,
in_range(X,Y,Z),
X =:= X2,
Y =:= Y2,
Z =:= Z2;
X is X1,
Y is Y1-1,
Z is Z1,
in_range(X,Y,Z),
move(X,Y,Z,X2,Y2,Z2).
move(X1,Y1,Z1,X2,Y2,Z2):-
X is X1,
Y is Y1,
Z is Z1-1,
in_range(X,Y,Z),
X =:= X2,
Y =:= Y2,
Z =:= Z2;
X is X1,
Y is Y1,
Z is Z1-1,
in_range(X,Y,Z),
move(X,Y,Z,X2,Y2,Z2).
move(X1,Y1,Z1,X2,Y2,Z2):-
X is X1+1,
Y is Y1,
Z is Z1,
in_range(X,Y,Z),
X =:= X2,
Y =:= Y2,
Z =:= Z2;
X is X1+1,
Y is Y1,
Z is Z1,
in_range(X,Y,Z),
move(X,Y,Z,X2,Y2,Z2).
move(X1,Y1,Z1,X2,Y2,Z2):-
X is X1,
Y is Y1+1,
Z is Z1,
in_range(X,Y,Z),
X =:= X2,
Y =:= Y2,
Z =:= Z2;
X is X1,
Y is Y1+1,
Z is Z1,
in_range(X,Y,Z),
move(X,Y,Z,X2,Y2,Z2).
move(X1,Y1,Z1,X2,Y2,Z2):-
X is X1,
Y is Y1,
Z is Z1+1,
in_range(X,Y,Z),
X =:= X2,
Y =:= Y2,
Z =:= Z2;
X is X1,
Y is Y1,
Z is Z1+1,
in_range(X,Y,Z),
move(X,Y,Z,X2,Y2,Z2).
move(6,_,_,_,_,_).
move(-6,_,_,_,_,_).
move(_,11,_,_,_,_).
move(_,-1,_,_,_,_).
move(_,_,6,_,_,_).
move(_,_,-6,_,_,_).
The query I'm running is
?-deliver(0,0,0,1,1,1,4,4,4).
You're hitting an infinite recursion because you're doing a depth-first search without tracking visited nodes, by tracing it you'll see for the example that your search gets lost in a (0, 0, 4) <> (0, 0, 5) loop, based on the first move predicate.
Tabling will work for SWI-Prolog, but let's take this opportunity to explore portable solutions as they'll aid your Prolog (and broader CS) learning.
First, let's look at that in_range/3 predicate and turn our locations into terms (if you bump into the word "reification", this is doing that). We want to consider our locations as terms so we can pass them around as whole entities. It also helps us think! We can define what a location is in your world like so:
% loc(X, Y, Z) is a location in 3D integer co-ordinate space
loc(X, Y, Z) :-
between(-5, 5, X),
between(0, 10, Y),
between(-5, 5, Z).
Thus in_range/3 becomes in_range/1:
in_range(Loc) :- call(Loc).
As a bonus, you can generate locations: ?- loc(X, Y, Z).
Now those move predicates can be tidied up to make them much easier to read, trace and think about. To that end, they only define a single move, best to keep journeys
to their own predicate so we can use these individually when we only want a single step. (?- move(loc(1, 1, 2), Step).)
%! move(Loc1, Loc2)
move(loc(X1, Y, Z), loc(X2, Y, Z)) :- X2 is X1 + 1, in_range(loc(X2, Y, Z)).
move(loc(X1, Y, Z), loc(X2, Y, Z)) :- X2 is X1 - 1, in_range(loc(X2, Y, Z)).
move(loc(X, Y1, Z), loc(X, Y2, Z)) :- Y2 is Y1 + 1, in_range(loc(X, Y2, Z)).
move(loc(X, Y1, Z), loc(X, Y2, Z)) :- Y2 is Y1 - 1, in_range(loc(X, Y2, Z)).
move(loc(X, Y, Z1), loc(X, Y, Z2)) :- Z2 is Z1 + 1, in_range(loc(X, Y, Z2)).
move(loc(X, Y, Z1), loc(X, Y, Z2)) :- Z2 is Z1 - 1, in_range(loc(X, Y, Z2)).
Now let's define our delivery predicate. in_range/1 can be used to check the From is valid, whereas Pickup and Dest are expected to take care of that themselves:
deliver(From, Pickup, Dest) :-
in_range(From),
go_to(From, Pickup),
go_to(Pickup, Dest).
So far I've only refactored your code to break predicates down into smaller definitions for more versatility and easier readability. The big change to prevent the infinite recursion is in go_to/2, which is not yet defined. Given that you're doing a search in integer 3D coordinate space, the most suitable search algorithm is A*, which will not only exclude search locations already visited, but will first search locations closest to the intended goal.
go_to(Origin, Destination) :-
a_star(Origin, Destination).
% A* for SWI-Prolog
:- use_module(library(heaps)).
% Use to order search, 3D euclidean distance squared
heuristic_distance(loc(X1, Y1, Z1), loc(X2, Y2, Z2), Distance) :-
Distance is (X1 - X2)^2 + (Y1 - Y2)^2 + (Z1 - Z2)^2.
% Add a list of nodes to the heap
open_add_nodes(Heap, [], _, Heap).
open_add_nodes(Heap, [ToAdd|Tail], Dest, Out) :-
heuristic_distance(ToAdd, Dest, Dist),
add_to_heap(Heap, Dist, ToAdd, Heap1),
open_add_nodes(Heap1, Tail, Dest, Out).
% Get an ordered list of reachable locations from the origin
get_reachable(Loc, Locations) :-
setof(L, move(Loc, L), Locations).
% A* search setup
a_star(Origin, Dest) :-
% Create heap of open search nodes
heuristic_distance(Dest, Origin, Dist),
singleton_heap(Open, Dist, Origin),
% Do the search
a_star(Open, Dest, [Origin]).
% Do the A* search
a_star(Open, Dest, Closed) :-
% Get the most promising Node
get_from_heap(Open, _, Loc, RemainingSearch),
% If we've reached the goal, return the Answer
( Dest = Loc
% Otherwise keep searching
; get_reachable(Loc, ReachableLocations),
% Exclude visited nodes
ord_union(Closed, ReachableLocations, Closed1, ToSearch),
% Add new open nodes to search heap
open_add_nodes(RemainingSearch, ToSearch, Dest, Open1),
% Carry on searching
a_star(Open1, Dest, Closed1)
).
Now A* might take a bit to read and understand, but it'd be much more difficult if we were having to deal with the X, Y, Z coordinates than with locations: every Loc, Dest, Origin and ToAdd is a location. It's also only possible to code because our move/2 predicate only takes a single step, so we can choose not to use Prolog's implicit Depth-First Search.
All this code is in a SWISH Notebook so you can explore it. To learn more about search algorithms I'd recommend the MIT AI lectures on YouTube, 4 and 5 cover search. For Prolog implementations, "The Craft of Prolog", also out of MIT, is excellent.
I am writing a program to find all of the squares that a Knight can move to in a game of chess.
For example: validKnightMove(X1/Y1, X2/Y2). where each argument is a co-ordinate pair.
What I've done:
Written the predicate.
Made it deterministic using cuts.
Unit-tested it with PL-Unit.
The predicate works, but I cannot query it in a desirable way from the Prolog shell.
What I'd like to do:
I would like to make a query that will find all of the valid squares I can move to from a given location. For example, ?- validKnightMove(4/4, X/Y), then the shell would search for X and Y values which satisfy the predicate.
However, when I make the query, it simply returns false., despite having valid solutions.
Here is some output from the shell to demonstrate the issue:
1 ?- validKnightMove(4/4, 6/3).
true.
2 ?- validKnightMove(4/4, X/Y).
false.
Here is my code:
This code is admittedly verbose, but should be easy to read.
validKnightMove(X1/Y1, X2/Y2) :- % Right 1, Down 2
onBoard(X2/Y2),
X2 =:= X1 + 1,
Y2 =:= Y1 + 2,
!.
validKnightMove(X1/Y1, X2/Y2) :- % Right 2, Down 1
onBoard(X2/Y2),
X2 =:= X1 + 2,
Y2 =:= Y1 + 1,
!.
validKnightMove(X1/Y1, X2/Y2) :- % Left 2, Down 1
onBoard(X2/Y2),
X2 =:= X1 - 2,
Y2 =:= Y1 + 1,
!.
validKnightMove(X1/Y1, X2/Y2) :- % Left 1, Down 2
onBoard(X2/Y2),
X2 =:= X1 - 1,
Y2 =:= Y1 + 2,
!.
validKnightMove(X1/Y1, X2/Y2) :- % Right 1, Up 2
onBoard(X2/Y2),
X2 =:= X1 + 1,
Y2 =:= Y1 - 2,
!.
validKnightMove(X1/Y1, X2/Y2) :- % Right 2, Up 1
onBoard(X2/Y2),
X2 =:= X1 + 2,
Y2 =:= Y1 - 1,
!.
validKnightMove(X1/Y1, X2/Y2) :- % Left 2, Up 1
onBoard(X2/Y2),
X2 =:= X1 - 2,
Y2 =:= Y1 - 1,
!.
validKnightMove(X1/Y1, X2/Y2) :- % Left 1, Up 2
onBoard(X2/Y2),
X2 =:= X1 - 1,
Y2 =:= Y1 - 2,
!.
onBoard(X/Y) :-
between(1, 8, X),
between(1, 8, Y),
!.
My question is: Why can't prolog find all the solutions for a deterministic predicate?
Note: My prolog version is SWI-Prolog (Multi-threaded, 32 bits, Version 6.2.2)