  % An Elevator Controller with Randomly Generated Exogenous Actions.

% Primitive action declarations

primitive_action(goUp).            primitive_action(goDown).
primitive_action(resetButton(N)).  primitive_action(toggleFan).
primitive_action(ringAlarm).       primitive_action(wait).
primitive_action(startFire).       primitive_action(endFire).
primitive_action(callElevator(N)). primitive_action(resetAlarm).
primitive_action(changeTemp).

% GOLOG Procedures 

proc(control, wait :
              while(some(n,buttonOn(n)),
                    pi(n, ?(buttonOn(n)) : serveFloor(n)))).

proc(serveFloor(N), while(-atFloor(N),
                          if(aboveFloor(N), goDown, goUp)) :
                    resetButton(N)).

proc(rules, ?(fire & -alarmOn) : ringAlarm :
                                             while(alarmOn,wait) #
                 ?(fire & alarmOn) #
                 ?(tooHot & -fan) : toggleFan #
                 ?(tooCold & fan) : toggleFan #
                 ?(-(fire v tooHot & -fan v 
                                  tooCold & fan))).


requestExogenousAction(E,S) :- randomNumber(R), exoAction(E,I1,I2),
                               I1 =< R, R =< I2, poss(E,S),!, nl,
                               write("Generating exogenous action: "),
                               write(E).
requestExogenousAction(nil,S) :- nl, write("No exogenous action.").
 
/* Generate a random number in the interval [0,R], where R has been
   declared to be the range for random numbers. */
 
randomNumber(Y) :- frandom(X), % System predicate for random real in [0,1].
                   randomRange(R), Y is X * R.
 
randomRange(200).
 
exoAction(changeTemp,0,10).           exoAction(startFire,16,20).
exoAction(endFire,23,27).             exoAction(resetAlarm,42,48).
exoAction(callElevator(1),116,120).   exoAction(callElevator(2),123,127).
exoAction(callElevator(3),130,134).   exoAction(callElevator(4),137,141).
exoAction(callElevator(5),144,148).   exoAction(callElevator(6),151,154).

% Preconditions for Primitive Actions 

poss(goUp,S) :- atFloor(N,S), topFloor(T), N < T.
poss(goDown,S) :- atFloor(N,S), firstFloor(F), F < N.
poss(resetButton(N),S) :- atFloor(N,S), buttonOn(N,S).
poss(toggleFan,S).
poss(ringAlarm,S) :- fire(S).
poss(startFire,S) :- not fire(S).
poss(endFire,S) :- fire(S).
poss(callElevator(N),S) :- not buttonOn(N,S).
poss(changeTemp,S).
poss(resetAlarm,S) :- alarmOn(S).
poss(wait,S).

% Successor State Axioms for Primitive Fluents. 

buttonOn(N,do(A,S)) :- A = callElevator(N) ;
                       (buttonOn(N,S), not A = resetButton(N)).

fire(do(A,S)) :- A = startFire ; (not A = endFire, fire(S)).

fan(do(A,S)) :- (A = toggleFan, not fan(S)) ;
                (not A = toggleFan, fan(S)).

atFloor(N,do(A,S)) :- (A = goDown, atFloor(M,S), N is M - 1) ;
                      (A = goUp, atFloor(M,S), N is M + 1) ;
                      (not A = goDown, not A = goUp, atFloor(N,S)).

alarmOn(do(A,S)) :- A = ringAlarm ; (not A = resetAlarm, alarmOn(S)).

temp(T,do(A,S)) :- (A = changeTemp, temp(T1,S), 
                                    ((not fan(S), T is T1 + 1) ;
                                     (fan(S), T is T1 - 1))) ;
                   (not A = changeTemp, temp(T,S)).

% Abbreviations.

tooHot(S) :- temp(T,S), T > 3.

tooCold(S) :- temp(T,S), T < -3.

aboveFloor(N,S) :- atFloor(M,S), N < M.

% Initial Situation. 

atFloor(1,s0).  temp(0,s0).  topFloor(6).  firstFloor(1).

% Restore suppressed situation arguments. 

restoreSitArg(buttonOn(N),S,buttonOn(N,S)).
restoreSitArg(fire,S,fire(S)). restoreSitArg(fan,S,fan(S)).
restoreSitArg(tooHot,S,tooHot(S)).
restoreSitArg(tooCold,S,tooCold(S)).
restoreSitArg(aboveFloor(N),S,aboveFloor(N,S)).
restoreSitArg(alarmOn,S,alarmOn(S)).
restoreSitArg(atFloor(N),S,atFloor(N,S)).
restoreSitArg(temp(T),S,temp(T,S)).
restoreSitArg(requestExogenousAction(E),S,requestExogenousAction(E,S)).

elevator :- doR(control,rules,s0,S), prettyPrintSituation(S).

prettyPrintSituation(S) :- makeActionList(S,Alist), nl,
                           write(Alist), nl.

makeActionList(s0,[]).
makeActionList(do(A,S),L) :- makeActionList(S,L1), append(L1,[A],L).

exoTransition(S1,S2) :- requestExogenousAction(E,S1),
                        (E = nil, S2 = S1 ;
                         not E = nil, S2 = do(E,S1)).
