Robots building robots

Here’s a (final) example from by Warplan toychest.

The aim of the exercise is to program a robot that has access to simple tools to assemble a toy car (or a 4w robot).

As before, we get firmly in our minds a picture of the world the robot will work in, and then describe the set of "operators" that should be useful in manipulating that world.

Here’s the basic Warplan code:


%% Robot construction project.

%% SWI-Prolog interface
:- style_check(-singleton).

%% special syntax operators (not to be confused with Warplan operations) used in this problem
%% (using english words/phrases can make the code and any written
%% output a bit more readable):
:-o p(600,fx,insert).
:-o p(600,fx,push).
:-o p(600,fx,slide).
:-o p(600,fx,clamp).
:-o p(600,fx,unclamp).
:-o p(600,fx,block_car_body_to).
:-o p(600,fx,unblock_car_body_to).
:-o p(600,fx,car_body_is_blocked_to).
:-o p(600,fx,car_body_is_unblocked_to).
:-o p(600,xf,is_free).
:-o p(600,xf,is_clamped).
:-o p(500,xfy,is_thru).
:-o p(500,xfy,points).
:-o p(500,xfy,is_opposite_of).
:-o p(500,xfy,is_attached_to).
:-o p(400,yfx,into).
:-o p(400,yfx,from).
:-o p(400,yfx,to).
:-o p(400,yfx,onto).
:-o p(400,yfx,in).
:-o p(300,xfy,end_of).
:-o p(200,fx,wheel).
:-o p(200,fx,axle).
:-o p(200,fx,hole).


%% definition of Warplan world-manipulating operations

add(W is_attached_to E, insert E into W).
add(W is_attached_to E, push W from D1 to D2 onto E in H).
add(A is_thru H, slide D1 end_of A into H from D2).
add(wheel W is_clamped, clamp wheel W).
add(axle A is_clamped, insert D end_of axle A into W).
add(wheel W is_free, unclamp wheel W).
add(axle A is_free, unclamp axle A).
add(vice is_free, unclamp X).
add(car_body_is_blocked_to D, block_car_body_to D).
add(car_body_is_unblocked_to D, unblock_car_body_to D).
add(D1 end_of A points D, slide D1 end_of A into H from D2)
  :- always(D is_opposite_of D2).
add(D end_of A points D2, slide D1 end_of A into H from D2)
  :- always(D is_opposite_of D1).

can(insert D end_of axle A into wheel W,
  axle A is_free &
  D end_of axle A is_free &
  wheel W is_clamped).
can(push wheel W from D1 to D2 onto D end_of axle A in hole H,
  wheel W is_free &
  D end_of axle A is_free &
  axle A is_thru hole H &
  D end_of axle A points D1 &
  car_body_is_unblocked_to D1 &
  D2 is_opposite_of D1 &
  car_body_is_blocked_to D2).
can(slide D1 end_of axle A into hole H from D2,
  axle A is_free &
  D1 end_of axle A is_free &
  hole H is_free &
  car_body_is_unblocked_to D2).
can(clamp wheel W, wheel W is_free & vice is_free).
can(unclamp X, X is_clamped).
can(block_car_body_to D, true).
can(unblock_car_body_to D, true).

del(X is_free,U) :- add(X is_clamped,U).
del(X is_free,U) :- add(X is_attached_to Z,U).
del(X is_free,U) :- add(Z is_attached_to X,U).
del(A is_free, slide D1 end_of A into H from D2).
del(H is_free, slide E into H from D2).
del(vice is_free, clamp W).
del(car_body_is_unblocked_to D, block_car_body_to D).
del(car_body_is_blocked_to D, unblock_car_body_to D).
del(Z is_clamped, unclamp Z).
del(W is_clamped, insert E into W).

imposs(axle A is_free & axle A is_thru hole H).
imposs(D end_of A is_free & W is_attached_to D end_of A).
imposs(wheel W is_free & wheel W is_attached_to E).
imposs(wheel W is_free & wheel W is_clamped).
imposs(hole H is_free & A is_thru hole H).
imposs(axle A is_free & axle A is_clamped).
imposs(vice is_free & X is_clamped).
imposs(car_body_is_blocked_to D & car_body_is_unblocked_to D).

always(true).
always(left is_opposite_of right).
always(right is_opposite_of left).

%% Description of the middle state
given(middle, wheel W is_free) :- member(W,[2,3,4]).
given(middle, wheel 1 is_attached_to left end_of axle 1).
given(middle, axle N is_thru hole N) :- member(N,[1,2]).
given(middle, D end_of axle A points D) :- member(D,[left,right]),
  member(A,[1,2]).
given(middle, right end_of axle A is_free) :- member(A,[1,2]).
given(middle, left end_of axle 2 is_free).
given(middle, vice is_free).
given(middle, car_body_is_unblocked_to D) :- member(D,[left,right]).

given(start, wheel W is_free) :- member(W,[1,2,3,4]).
given(start, axle A is_free) :- member(A,[1,2]).
given(start, D end_of axle A is_free) :- member(D,[left,right]),
  member(A,[1,2]).
given(start, hole H is_free) :- member(H,[1,2]).
given(start, vice is_free).
given(start, car_body_is_unblocked_to D) :- member(D,[left,right]).

 

%% Definition of the goal state (saves some typing later)

goal(T) :- plans(axle A1 is_thru hole 1 &
  axle A2 is_thru hole 2 &
  wheel W1 is_attached_to left end_of axle A1 &
  wheel W2 is_attached_to left end_of axle A2 &
  wheel W3 is_attached_to right end_of axle A1 &
  wheel W4 is_attached_to right end_of axle A2, T).

 


In this example -- which you can see is more complex than the simple block-movers or even STRIPS micro-world -- I've defined 2 states; the usual start state, but also a middle state that we presume is seen somewhere in the middle of building the toy car.

The special rule goal defines a goal state, but starting from state T. So we can use it like goal(start) or goal(middle) to spit out a plan to reach the goal starting from the initial state-of-the-world or this middle place. If you run this example you'll see the difference in runtime. :)

The output from Warplan for reaching the goal from the starting point -- with all the pieces on the workbench -- is:

start;
slide left end_of axle 1 into hole 1 from left;
slide left end_of axle 2 into hole 2 from left;
block_car_body_to_left;
push wheel 1 from right to left onto left end_of axle 1 in hole 1;
push wheel 2 from right to left onto left end_of axle 2 in hole 2;
unblock_car_body_to_left;
block_car_body_to_right;
push wheel 3 from left to right onto right end_of axle 1 in hole 1;
push wheel 4 from left to right onto right end_of axle 2 in hole 2.


And now we might appreciate the use of the "op" declarations at the top of the example. Allowing words like "insert", "push", "slide", etc as prefix operations and "is_attached_to"  as infix operations allows the output from Warplan to be semi-readable. Otherwise it’d be bristling with parenthese, like LISP/SCHEME. :)