Knight's tour in Prolog: Stack limit exceeded - prolog

After I tried to optimize the program using Warnsdorff's rule, the compiler started issuing Stack limit exceeded. All parts separately seem to work, but I have no idea how this could be optimized. I am writing a program on an old laptop with 32-bit windows, so I can’t increase the size of the stack manually, as it is written on the official website https://www.swi-prolog.org/FAQ/StackSizes.html.
knightpath(Board, [1 / 1 | Path]) :-
Jumps is Board * Board,
the_way(Jumps, [1 / 1 | Path]).
the_way(1, [X / Y]) : -
between(1, 5, X),
between(1, 5, Y).
the_way(Jumps, [X1 / Y1, X2 / Y2 | Path]) : -
Jumps1 is Jumps - 1,
the_way(Jumps1, [X2 / Y2 | Path]),
warnsdorff(X2 / Y2, Path, X1 / Y1).
jump(X1 / Y1, X2 / Y2) : -
((X1 is X2 + 2;
X1 is X2 - 2),
(Y1 is Y2 + 1;
Y1 is Y2 - 1);
(X1 is X2 + 1;
X1 is X2 - 1),
(Y1 is Y2 + 2;
Y1 is Y2 - 2)),
between(1, 5, X1),
between(1, 5, Y1).
warnsdorff(X1 / Y1, Path, X2 / Y2) :-
find_posible(X1 / Y1, Path, Pos),
find_best(_, [X1 / Y1 | Path], Pos, X2 / Y2).
find_best(N, Path, [X / Y], X / Y) : -
find_posible(X / Y, Path, Pos),
length(Pos, N).
find_best(N1, Path, [X / Y | List], X / Y) : -
find_best(N2, Path, List, _),
find_posible(X / Y, Path, Pos),
length(Pos, N1),
N1 < N2.
find_best(N2, Path, [X1 / Y1 | List], X2 / Y2) : -
find_best(N2, Path, List, X2 / Y2),
find_posible(X1 / Y1, Path, Pos),
length(Pos, N1),
N1 >= N2.
find_posible(X1 / Y1, Path, Pos) : -
findall(X2 / Y2, jump(X2 / Y2, X1 / Y1), All_tog),
filter_path(All_tog, Path, Pos).
filter_path([], _, []).
filter_path([X / Y | All_tog], Path, [X / Y | Pos]) : -
not(member(X / Y, Path)),
filter_path(All_tog, Path, Pos).
filter_path([X / Y | All_tog], Path, Pos) : -
member(X / Y, Path),
filter_path(All_tog, Path, Pos).
This is what the compiler produces
ERROR: Stack limit (0.5Gb) exceeded
ERROR: Stack sizes: local: 0.1Gb, global: 42.7Mb, trail: 0Kb
ERROR: Stack depth: 1,863,822, last-call: 0%, Choice points: 3
ERROR: In:
ERROR: [1,863,822] user:the_way(-1863788, [length:1|_22365890])
ERROR: [1,863,821] user:the_way(-1863787, '<garbage_collected>')
ERROR: [1,863,820] user:the_way(-1863786, '<garbage_collected>')
ERROR: [1,863,819] user:the_way(-1863785, '<garbage_collected>')
ERROR: [1,863,818] user:the_way(-1863784, '<garbage_collected>')

The traceback already shows what is wrong: the the_way is called with:
[1,863,822] user:the_way(-1863788, [length:1|_22365890])
This thus means that the Jumps variable is -1863788. You did not perform proper checking in your recursion to avoid making paths longer than the threshold. You should add a constraint like:
the_way(1, [X / Y]) : -
between(1, 5, X),
between(1, 5, Y).
the_way(Jumps, [X1 / Y1, X2 / Y2 | Path]) : -
Jumps > 1,
Jumps1 is Jumps - 1,
the_way(Jumps1, [X2 / Y2 | Path]),
warnsdorff(X2 / Y2, Path, X1 / Y1).

The problem was that Warnsdorff's rule does not provide a solution for the case where the first (last in terms of the stack) square is 1/1. I have to write
knightpath(Answ) :-
Jumps is Board * Board,
the_way(Jumps, Path),
reverse(Path, Answ).
...
the_way(1, [1/1]).
By the way, if I write, as advised by Willem Van Onsem https://stackoverflow.com/a/57348007/11779964, then error can be avoided, and the compiler will simply output 'false'.

Related

Define predicate from following the rules Prolog

I'm new to Prolog and I would like to convert given rules to Prolog language.
I have to define a predicate p(X1, Y1, X2, Y2) ,where X1, Y1 are integers, and X2 and Y2 are obtained from X1, Y1 following these rules:
if Y1 ≤ 0 and |X1|≤−Y1 then X2 is X1 + 1 and Y2 is Y1
else if X1 > 0 and |Y1| < X1 then X2 is X1, and Y2 is Y1+1
else if Y1 > 0 and −Y1 < X1 ≤ Y1 then X2 is X1−1, and Y2 is Y1
else X2 is X1 and Y2 is Y1−1
I'll provide a steer, here is rule 1:
p(X1, Y1, X2, Y2) :-
Y1 =< 0,
abs(X1) =< -Y1,
% Cut, to prevent processing alternatives
!,
X2 is X1 + 1,
Y2 is Y1.
Sample output in swi-prolog:
?- p(-3, -5, X2, Y2).
X2 = -2,
Y2 = -5.

Moving within a list, by giving the possible moves. Prolog

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.

Compute addition and subtraction of an arithmetic expression without 'is'

How can I find the result of an arithmetic expression composed of pluses and minuses without using 'is'?
For example, to find the result of 1+2+3+4-6, I do: X is 1+2+3+4-6 and I get: X = 4.
What I've tried so far:
eval(EXPR, EXPR) :-
integer(EXPR),
!.
eval(X+Y, RES) :-
eval(X, X1),
eval(Y, Y1),
plus(X1, Y1, RES).
eval(X-Y, RES) :-
eval(X, X1),
eval(Y, Y1),
Y2 = -Y1,
plus(X1, Y2, RES).
but when I try to compute an expression containing negative numbers, I get the error: `integer' expected, found `- number` (a compound).
How can I solve it?
Add plus(Y1,Y2,0) instead of Y2 = -Y1.
In the latter case, Y2 is a structure with functor - and Argument Y1, whereas in the former case, Y1 is a number.
Take Y1 = 3 for example. [Just for fun: try it with Y1 = -3]
?- Y1 = 3, Y2 = -Y1, Y2 =.. L.
Y1 = 3,
Y2 = - 3,
L = [-, 3].
?- Y1 = 3, plus(Y1,Y2,0), Y2 =.. L.
Y1 = 3,
Y2 = -3,
L = [-3].
However, Y2 is supposed to be an argument in the predicate plus/3, which does not allow structures as arguments.
Maybe you prefer this solution:
eval(X-Y, RES) :-
eval(X, X1),
eval(Y, Y1),
plus(Y1, RES, X1).

Prolog Program not exiting

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.

Why can I not "find all solutions" for a deterministic predicate?

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)

Resources