Prolog - Arguments are not sufficiently instantiated - Knight/chessboard

317 views Asked by At

I'm very new to Prolog and I'm trying to write a simple method to determine if a knight on a chessboard can jump to another square, or output all of the squares that a knight could jump to given a square. For this method, assume the first argument must always be instantiated. It works correctly, given both parameters, but I am not sure why it will not output only given one.

% validSquare/2
% validSquare(X1/Y1, X2/Y2) iff the coordinate is a valid position on an 8x8 chessboard.
validSquare(X1/Y1, X2/Y2) :-
  X1 >= 1, X1 =< 8,
  Y1 >= 1, Y1 =< 8,
  X2 >= 1, X2 =< 8,
  Y2 >= 1, Y2 =< 8.


% jump/2
% jump(Square1, Square2) iff a knight could jump to the coordinate
% Square1/Square2 on a chessboard.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 + 1,
  Y2 is Y1 + 2.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 + 2,
  Y2 is Y1 + 1.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 + 1,
  Y2 is Y1 - 2.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 + 2,
  Y2 is Y1 - 1.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 - 1,
  Y2 is Y1 - 2.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 - 2,
  Y2 is Y1 - 1.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 - 1,
  Y2 is Y1 + 2.
jump(X1/Y1, X2/Y2) :-
  validSquare(X1/Y1, X2/Y2),
  X2 is X1 - 2,
  Y2 is Y1 + 1.

Like I said, I'm very new to Prolog, so I'm not really sure how I should format the query. This query is false.

?- jump(1/1, X2/Y2).
ERROR: >=/2: Arguments are not sufficiently instantiated

Thanks for any help.

3

There are 3 answers

1
false On
:- use_module(library(clpfd)).

jump(X0/Y0, X/Y) :-
   abs(X0-X)+abs(Y0-Y)#=3,
   X0 #\= X,
   Y0 #\= Y,
   [X0,Y0,X,Y]ins 1..8.
3
Junuxx On

It's breaking on the >= test in validSquare. You can't determine the truth value of X <= Y when both are unbound, basically, because numerical operators don't assign values. You could solve this by using a member to bind the coordinates to valid squares, i.e.

validSquare(X1/Y1, X2/Y2):-
    member(X1, [1,2,3,4,5,6,7,8]),
    member(X2, [1,2,3,4,5,6,7,8]),
    member(Y1, [1,2,3,4,5,6,7,8]),
    member(Y2, [1,2,3,4,5,6,7,8]).

Depending on the Prolog dialect you're using, member might be a built-in (e.g. SWI-Prolog) or not.

0
repeat On

Here is an alternative to the code presented in this fine answer. It is also based on .

jump(X0/Y0, X/Y) :-
   D_x #= X0-X,
   D_y #= Y0-Y,
   abs(D_x) #\= abs(D_y),
   [D_x , D_y] ins (-2 .. -1)\/(1..2),
   [X0,X,Y0,Y] ins 1..8.

It's efficient1,2!


Footnote 1: Tested with version 4.3.2 (64-bit) and version 7.3.11 (64-bit).
Footnote 2: SICStus Prolog requires a suitable definition of (ins)/2 to run above code.