/*			APPENDIX  B
		Section B.4   A Coffee Delivery Robot.
*/
:- set_flag(all_dynamic, on). 
:- dynamic(proc/2).

/*        A Coffee Delivery Robot          */
/* GOLOG Procedures */

/* The coffee serving procedures serve(T) and serveOneCoffee(T) 
   (written in regular Golog) below deliver coffee to all people 
   who can be served in time.  serve(T) terminates when there
   does NOT exist a person who can be served in his/her
   preferable time. The termination is conditioned only upon
   temporal inequalities. The procedure timeGoal expresses this
   termination condition in regular Golog.                        */

proc(timeGoal,
     pi(t, ?(now(t)) : 
       pi(rloc, ?(robotLocation(rloc)) :
         ?(-some(p, some(t1, some(t2, 
           some(travTime1, some(travTime2, some(wait,
             wantsCoffee(p,t1,t2) & -hasCoffee(p) & 
               wait $>= 0 & travelTime(rloc,cm,travTime1) &
                 travelTime(cm,office(p),travTime2) &
                   t1 $<= t + wait + travTime1 + travTime2 &
                     t + wait + travTime1 + travTime2 $<= t2 
      )))))))))
    ).
 
proc(serve(T),
     timeGoal
   #
     if( holdingCoffee,
          serveOneCoffeeNew(T), 
          goto(cm,T) : pi(t, ?(now(t)) : pickupCoffee(t) : serveOneCoffee(t) )   
       )
   ).

proc(serveOneCoffee(T),
    pi(p, pi(wait, pi(t1, pi(t2, pi(travTime, pi(rloc,  pi(currTime, 
     ?(wantsCoffee(p,t1,t2) & -hasCoffee(p) & (wait $>= 0) & now(currTime) &
         robotLocation(rloc) & travelTime(rloc,office(p),travTime) &
       t1 $<= T + wait + travTime & T + wait + travTime $<= t2) ))))) :
     pi(t, ?(t $= T + wait) : goto(office(p),t)) ) :
     pi(t, ?(now(t)) : giveCoffee(p,t) ) 
      ) :
     pi(t, ?(now(t)) : serve(t))
    ).

goal(s0).
goal(do(A,S)) :- A \= giveCoffee(P,T), goal(S).
goal(do(A,S)) :- A = giveCoffee(P,T), pref(do(A,S),S,s0), goal(S).

proc(visit1,
     goto(office(mary),1) : 
       pi(t1, ?(now(t1)) : 
             (  (goto(cm,t1) : pi(t2, ?(now(t2)) : goto(office(sue),t2)))  
               # 
                (goto(cm,t1) : pi(t2, ?(now(t2)) : goto(office(bill),t2))) 
             )
          ) :   
         pi(t, ?(now(t)) : ?(t $< 40)) ).


proc(visit2,
     pi(p, pi(t1, pi(t2, ?(-hasCoffee(p) & wantsCoffee(p,t1,t2)) :
        goto(cm,91) :  pi(t, ?(now(t)) : pickupCoffee(t)) :
                pi(t, ?(now(t)) : goto(office(p),t)) : 
                pi(t, ?(now(t)) : ?(t $<= t2) )
       )))
    ).

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),
   ?(Loc1=Loc2 & Delta=0) # 
          ?(-(Loc1=Loc2) & Delta > 0) : 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), 
                   start(S,TS),  TS $<= T.

poss(giveCoffee(Person,T),S) :- holdingCoffee(S),
                                robotLocation(office(Person),S),
                                start(S,TS), TS $<= T.

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

poss(endGo(Loc1,Loc2,T),S) :- going(Loc1,Loc2,S),
                                start(S,TS), TS $<= T.

/* 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)).

util(0, s0).
util(V2, do(giveCoffee(Person,T),S)) :- util(V, S), 
               wantsCoffee(Person,T1,T2), not hasCoffee(Person,S),
               V1 $<= (T2 - T)/2, 
               V1 $<= T - (3*T1 - T2)/2, 
               V2 $= V + V1.
                
util(V, do(A,S)) :- A \=giveCoffee(P,T), util(V, S).


/* 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)).
restoreSitArg(goal,S,goal(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)).


/* Initial Situation.  */

robotLocation(park,s0).
start(s0, 0).

wantsCoffee( lounge,    600, 700 ).   % (3*660 - 740)/2 = 620
wantsCoffee( ray,       360, 440 ).  %  (3*460 - 540/2 = 420
wantsCoffee( yves,   160, 240 ).    % (3*220 - 320)/2 = 170

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

/*
--- TABLE OF AVERAGE TRAVEL TIME FROM cm ---

        -----------RAY    75  (51, 72 cm->ray; 56, 55 ray->cm)
        |
        +--------------
        |
        |
        +--ITRC            24
    CM--+
        |
        |
        |
        +--VISITOR         40
        |
 LOUNGE-+                 58 (cm->lounge), 63 (lounge->cm)
        |
  ------+--------------------
*/

travelTime0( park,   cm,      100 ).     /* 73, 82 on simulator */
travelTime0( cm,    office(ray),    120 ).   /* 51,72,56,55 on simulator*/
travelTime0( cm,    office(itrc),   30 ).   /* 24 on simulator */
travelTime0( cm,    office(yves),   45 ).   /* 29 on simulator  */
travelTime0( cm,    office(lounge), 75 ).   /* 58, 63 on simulator */

/* Other travel times (measured on simulator):  */

travelTime0( office(ray),    office(itrc),    70 ).
travelTime0( office(ray),    office(yves),  120 ).  
travelTime0( office(ray),    office(lounge),  120 ). 
travelTime0( office(lounge),  office(itrc),    70 ).
travelTime0( office(lounge),  office(yves),  60 ).
travelTime0( office(yves),  office(itrc),    50 ).
travelTime0( park,        office(yves),  60 ).

/* Geometric coordinates on the real office map. */
/* office(ray)= (3753.4, 1800)    cm= (2675, 2555)    park= (118, 2487) */

drivePath( cm,    office(lounge),  [ ( 840, 2560 ), ( 770, 2530 ) ] ).
drivePath( cm,    office(yves), [ ( 1620, 2500 ) ] ).
drivePath( cm,    office(ray),     
                      [ (3535, 2490 ), (3760, 2480), ( 3770, 1770 ) ] ).

drivePath( office(ray),  cm, 
      [ ( 3760, 2420 ), (3560, 2500 ), ( 2820, 2560 ), ( 2690, 2550 ) ] ).
drivePath( park,    cm,      [ ( 2685, 2500 ), ( 2700, 2550 ) ] ).
drivePath( office(yves), cm, 
      [ ( 3120, 2450 ), ( 2700, 2550 ) ] ).
drivePath( _, cm,      [ ( 2700, 2550 ) ] ).
drivePath( office(yves), office(ray),
      [ ( 3120, 2450 ), (3535, 2490 ), (3760, 2480), ( 3770, 1770 ) ] ).
drivePath( office(ray),  office(lounge),
      [ ( 3760, 2420 ), (3560, 2480 ), ( 840, 2560 ), ( 770, 2530 ) ] ).
 
drivePath( X, Z, BigList) :- drivePath( X, Y, List1), 
                      drivePath( Y, Z, List2),
                      append(List1, List2, BigList).

driveSim( StartPos, EndPos ) :- nl, write("drive, drive, drive from "), 
                       write(StartPos), write(" to "), write(EndPos), nl.

/* Drive in the corridor and turn to the goal location */
driveReal( StartPos, EndPos ) :-
	drivePath( StartPos, EndPos, Path ),   % get path
           hli_go_path( Path ),               % drive, drive...
	look( EndPos, X, Y ),              % get aim point for turning
	hli_turn_to_point( X, Y ).         % and turn the robot

look( office(lounge),  640, 2670 ).
look( office(yves), 1600, 2180 ).
look( cm,      2720, 2720 ).
look( office(itrc),    2872, 2200 ).
look( office(ray),     3728, 1650 ).