One can debug this using trace. (added indentation):
[trace] ?- level(X, 0).
Call: (6) level(_G2697, 0) ? creep
Call: (7) supported(_G2697, _G2771, _G2772) ? creep
Exit: (7) supported(c, a, b) ? creep
Call: (7) level(a, _G2771) ? creep
Call: (8) supported(a, _G2771, _G2772) ? creep
Fail: (8) supported(a, _G2771, _G2772) ? creep
Redo: (7) level(a, _G2771) ? creep
Call: (8) _G2770 is 0 ? creep
Exit: (8) 0 is 0 ? creep
Exit: (7) level(a, 0) ? creep
Call: (7) 0 is 0+1 ? creep
Fail: (7) 0 is 0+1 ? creep
Fail: (6) level(_G2697, 0) ? creep
false.
So what happened?
First you call level(X,0). This predicate calls supported with supported(BLOCK,X,_). There is one answer that unifies: BLOCK=a, X=b and _=c. So that means the if-then-else statement uses the then-part.
In the then-part, it queries level/2 again with level(b,LEV1), now this call results in a call to support(b,LEV1,_). For this call, it can't resolve the call, because there is no support predicate with the first value b. So now we take the else-part. We unify LEV1 is 0, thus LEV1=0 and we return.
In the return part (in the then-part), level(b,0) is the result. Now we unify LEVEL is LEV1+1, LEVEL however was already grounded as 0 and 0+1 is 1. Thus it fails on the top level.
The resolution is probably to make supported finer, and define it as: support/2.
The program reads:
block(a).
block(b).
block(c).
%supported(BLOCK, BY1, BY2)
supported(c,b).
supported(c,a).
supported(b,a).
Now the level predicate reads:
level(B,L) :-
supported(B,C),
level(C,LC),
L is LC+1.
level(_,0).