
   % Sequential, Temporal Golog Program for a Coffee Delivery Robot

% GOLOG Procedures 

proc(deliverCoffee(T),
  ?(some(t, now(t) & t $<= T)) :
  (?(all(p,all(t1,all(t2,wantsCoffee(p,t1,t2) => hasCoffee(p))))) 
   #
   pi(rloc, ?(robotLocation(rloc)) :
            if(rloc = cm, /* THEN */ deliverOneCoffee(T),
                          /* ELSE */ goto(cm,T) : pi(t, ?(now(t)) : 
                                                     deliverOneCoffee(t))) :
            pi(t, ?(now(t)) : deliverCoffee(t))))).

proc(deliverOneCoffee(T),
  pi(p, pi(t1, pi(t2, pi(wait, pi(travTime,
     ?(wantsCoffee(p,t1,t2) & -hasCoffee(p) & (wait $>= 0) &
       travelTime(cm,office(p),travTime) & 
       t1 $<= T + wait + travTime & T + wait + travTime $<= t2) :
     pi(t, ?(t $= T + wait) : pickupCoffee(t)) :
     pi(t, ?(now(t)) : goto(office(p),t)) :
     pi(t, ?(now(t)) : giveCoffee(p,t)))))))). 


proc(goto(L,T),
  pi(rloc,?(robotLocation(rloc)) : pi(deltat,?(travelTime(rloc,L,deltat)) :
     goBetween(rloc,L,deltat,T)))).

proc(goBetween(Loc1,Loc2,Delta,T),
   startGo(Loc1,Loc2,T) :
   pi(t, ?(t $= T + Delta) : endGo(Loc1,Loc2,t))).

% Preconditions for Primitive Actions.

poss(pickupCoffee(T),S) :- not holdingCoffee(S),
                           robotLocation(cm,S).

poss(giveCoffee(Person,T),S) :- holdingCoffee(S),
                                robotLocation(office(Person),S).

poss(startGo(Loc1,Loc2,T),S) :- not going(L,LL,S), not Loc1 = Loc2,
                                robotLocation(Loc1,S).

poss(endGo(Loc1,Loc2,T),S) :- going(Loc1,Loc2,S).

% Successor State Axioms.

hasCoffee(Person,do(A,S)) :- A = giveCoffee(Person,T) ;
                             hasCoffee(Person,S).

robotLocation(Loc,do(A,S)) :- A = endGo(Loc1,Loc,T) ;
                                         (robotLocation(Loc,S),
                                          not A = endGo(Loc2,Loc3,T)).

going(Loc1,Loc2,do(A,S)) :- A = startGo(Loc1,Loc2,T) ;
                                   (going(Loc1,Loc2,S),
                                    not A = endGo(Loc1,Loc2,T)).

holdingCoffee(do(A,S)) :- A = pickupCoffee(T) ;
                                (holdingCoffee(S),
                                 not A = giveCoffee(Person,T)).


% Initial Situation.

robotLocation(cm,s0).

start(s0,0).

wantsCoffee(sue,140,160).
wantsCoffee(bill,100,110).
wantsCoffee(joe,90,100).
wantsCoffee(mary,130,170).

travelTime0(cm,office(sue),15).
travelTime0(cm,office(mary),10).
travelTime0(cm,office(bill),8).
travelTime0(cm,office(joe),10).

travelTime(L,L,0).
travelTime(L1,L2,T) :- travelTime0(L1,L2,T) ;
                       travelTime0(L2,L1,T).

% The time of an action occurrence is its last argument.

time(pickupCoffee(T),T).       time(giveCoffee(Person,T),T).
time(startGo(Loc1,Loc2,T),T).  time(endGo(Loc1,Loc2,T),T).

% Restore situation arguments to fluents. 

restoreSitArg(robotLocation(Rloc),S,robotLocation(Rloc,S)).
restoreSitArg(hasCoffee(Person),S,hasCoffee(Person,S)).
restoreSitArg(going(Loc1,Loc2),S,going(Loc1,Loc2,S)).
restoreSitArg(holdingCoffee,S,holdingCoffee(S)).

% Primitive Action Declarations.

primitive_action(pickupCoffee(T)).
primitive_action(giveCoffee(Person,T)).
primitive_action(startGo(Loc1,Loc2,T)).
primitive_action(endGo(Loc1,Loc2,T)).

% Fix on a solution to the temporal constraints. 

chooseTimes(s0).
chooseTimes(do(A,S)) :- chooseTimes(S), time(A,T), rmin(T).

% "now" is a synonym for "start".

now(S,T) :- start(S,T).
restoreSitArg(now(T),S,now(S,T)).

%  Utilities. 

prettyPrintSituation(S) :- makeActionList(S,Alist), nl, write(Alist), nl. 
 
makeActionList(s0,[]).
makeActionList(do(A,S), L) :- makeActionList(S,L1), append(L1, [A], L).
 
coffeeDelivery(T) :- do(deliverCoffee(T),s0,S), chooseTimes(S),
                     prettyPrintSituation(S), askForMore.                       
 
askForMore :- write('More? '), read(n).
