Search code examples
variablesprologblockpredicate

Stack of blocks in Prolog


I'm trying to do something very simple in Prolog, but for some reason it's not working and I can't figure out why. I'm using SWI.

I have a bunch of blocks, labeled a to f, and they're all stacked on top of each other, a being at the bottom and f all the way on top. Like this:

on(b, a).
on(c, b).
on(d, c).
on(e, d).
on(f, e).
above(X, Y) :- on(X, Y).
above(X, Y) :- on(X, Z), above(Z, Y).

So one block is above another block if there's some block beneath it that is on top of that block. Makes sense to me.

So now I want to define a predicate that tells me if a certain block has exactly 3 blocks beneath it. In my example that would be block d. So I started with:

exactlyThree(X) :- above(X, Y), above(Y, Z), above (Z, W), \+ above(W, _).

So X has exaclty three blocks beneath it if X is above a block Y, if Y is above a block Z, if Z is above a block W and block W is not above any block. But that's not working.

So I tried this, which is practically the same thing:

bottomBlock(X) :- \+ above(X, _).
exactlyThree(X) :- above(X, Y), above(Y, Z), above (Z, W), bottomBlock(W).

That didn't work either. As a test it tried:

?-bottomBlock(a).
true.

Makes sense, but then I tried:

?-bottomBlock(X).
false.

What's going on here? Why doesn't prolog say X=a? And why aren't my predicates doing what they're supposed to?


Solution

  • Starting from the bottom (hehe):

    ?-bottomBlock(X).
    false.
    

    Why doesn't prolog say X=a?

    Because it won't invent things to match a variable. You need to assert something, not just negate.

    bottomBlock(X) :- on(_, X), \+ above(X, _).
    
    exactlyThree(X) :- above(X, Y), above(Y, Z), above (Z, W), \+ above(W, _).
    

    This should match 3 blocks or more: if X is above Y, it could be 10 blocks above. You need to use on in order to guarantee it is exactly 3 blocks away.