% File 'library'

% Part 1: Modules ACTOR, THEME, ORDER, ASSIGN

% We need to include the library-ontology file
% but due to pathname issues, we will not include
% it here.  Instead, a domain description will
% have to include both files
%
% include "library-ontology"

module ACTOR;

% An action may be executed by an agent.

  fluents
    Actor(Thing,action): rigid;

  variables
    x: Thing;
    a: action;

  axioms
    default -Actor(x,a);


module THEME;

% An action may have a theme.

  fluents
    Theme(Thing,action): rigid;

  variables
    x: Thing;
    a: action;

  axioms
    default -Theme(x,a);



module ORDER;

% A transitive, irreflexive order relation among objects of sort Domain

  fluents
    Less(Domain, Domain) : staticallyDetermined;

  variables
    s, s1, s2: Domain;

  axioms

    Less(s, s2) if Less(s, s1) & Less(s1, s2);

    % There is no order relations unless we explicitly say so
    default -Less(s,s1);

    constraint -Less(s,s);



module ASSIGN;

  actions
    Assign(Domain, Range);

  fluents
    Value(Domain)  : simple(Range);

  variables
    x: Domain;
    y: Range;

  axioms

    inertial Value(x);

    exogenous Assign(x,y);

    Assign(x,y) causes Value(x)=y;

% The following might be useful but will be useless after Assign
% is renamed, so we don't include it.
%    Theme(x, Assign(x,y));



% Part 2: Modules MOVE, MOVE_IN_REGION

module MOVE;

% Location(Thing) is the place where Thing is located.
% Action Move(Thing,Place) causes Thing to be at Place.
%
% A Thing cannot be moved to its current Place.

  actions
    Move(Thing,Place);

  fluents
    Location(Thing): simple(Place);


  variables
    x: Thing;
    p: Place;

  import ASSIGN;
    Domain is Thing;
    Range is Place;
    Value(x) is Location(x);
    Assign(x,p) is Move(x,p);

  axioms

% The following might be useful but will be useless after Assign
% is renamed, so we don't include it.
%    Theme(x,Move(x,p));

    % Prevent trivial moves
    nonexecutable Move(x,p) if Location(x)=p;



module MOVE_IN_REGION;

  import MOVE;

  fluents
    At(Region, Region) : staticallyDetermined;
    Movable(Region): rigid;

  variables
    x: Thing;
    p,p1: Place;
    r, r1: Region;

  import ORDER;
    Domain is Region;
    Less(r,r1) is At(r,r1);

  axioms
    nonexecutable Move(x,p) if Location(x)=p1 
                               & -exists r (At(p1,r) & At(p,r) 
                                            & Movable(r));

    default -Movable(r);


% Part 3: Modules MOUNT, TOWER, TOP

module MOUNT;

% Support(Thing) is what Thing rests on.  A thing cannot be supported by
% itself, even indirectly.

% Action Mount(Thing,Supporter) causes Thing to rest on Supporter.  Its theme
% is Thing.

  actions
    Mount(Thing,Supporter);

  fluents
    Support(Thing):                 simple(Supporter);
    Supported(Supporter,Supporter): staticallyDetermined;

  variables
    x, y: Thing;
    s, s1: Supporter;

  import ASSIGN;
    Domain is Thing;
    Range is Supporter;
    Value(x) is Support(x);
    Assign(x,s) is Mount(x,s);

  import ORDER;
    Domain is Supporter;
    Less(s,s1) is Supported(s,s1);

  import THEME;

  axioms

    Theme(x,Mount(x,s));
    Theme(y,Mount(x,y));
 
    % Prevent trivial mounts
    nonexecutable Mount(x,s) if Support(x)=s;

    Supported(x,s) if Support(x)=s;


module TOWER;

  import MOUNT;

  fluents
    Wide(Supporter) : rigid;

  variables
    x, y : Thing;
    s    : Supporter;

  axioms
    % Two things cannot be directly on top of the same supporter
    % unless it's Wide
    constraint Support(x)=s & Support(y)=s & -Wide(s) -> x=y;

    % We can only mount things which are clear (not under others)
    nonexecutable Mount(x,s) if Support(y)=x;

    default -Wide(s);


module TOP;

% TopLocation(x) is the location of the things whose support is x.
% By default, it's the same as the location of x.

  fluents
    Location(Thing):    simple(Place);
    Support(Thing):     simple(Supporter);
    TopLocation(Thing): staticallyDetermined(Place);


  variables
    x, y: Thing;
    p:    Place;

  axioms
    default TopLocation(x)=p if Location(x)=p;
    Location(x)=p if Support(x)=y & TopLocation(y)=p;



% Part 4: Modules NOCONCURRENCY, LOCAL

module NOCONCURRENCY;

% Actions cannot be executed concurrently.

  variables
    a, a1: explicitAction;

  axioms
    nonexecutable a & a1 if a!=a1;



module LOCAL;

% An action can only be executed locally--by an agent located where the
% theme of the action is.

  import ACTOR;

  import THEME;

  fluents
    Location(Thing): simple(Place);

  variables
    x, y: Thing;
    a: action;

  axioms
    nonexecutable a
      if (Actor(x,a) | Theme(x,a))
         & (Actor(y,a) | Theme(y,a))
         & Location(x)!=Location(y);


% Part 5: Module CARRIER

module CARRIER;

  objects
    Ground : Supporter;

  actions
    Load(Thing, Carrier);
    Unload(Thing);

  fluents
    Big(Carrier) : rigid;
    TooSmallToSupport(Carrier, Thing) : staticallyDetermined;
    DriverRequired(Vehicle) : rigid;
    Holds(Carrier, Thing) : staticallyDetermined;

  variables
    x,y : Thing;
    c : Carrier;
    m,m1 : Person;
    v,v1 : Vehicle;
    p : Place; 

  import MOUNT;
    Mount(x,c) is Load(x,c);

  import MOUNT;
    Mount(x,Ground) is Unload(x);

  import ORDER;
    Domain is Thing;
    Less(c,x) is TooSmallToSupport(c,x);

  % Something supported by a carrier moves with it
  import TOP;

  % Carrier actions enforce locality of themes for mounting
  import LOCAL;

  import MOVE;

  axioms

    % Persons and Vehicles are Big by default, so they cannot support objects
    % of the same sort
    default Big(c);
    constraint Support(m)=m1 -> -Big(m);
    constraint Support(v)=v1 -> -Big(v);

    constraint Support(x)=y -> Carrier(y);

    constraint Support(x)=c -> -TooSmallToSupport(c,x);

    TooSmallToSupport(m,v);

    default DriverRequired(v);

    nonexecutable Move(v,p) if  DriverRequired(v) & -exists m Support(m)=v;

    Holds(c,x) if Supported(x,c);
    default -Holds(c,x);


% Part 5: Module TIME

module TIME;

  actions
    Wait(1..MaxTime);

  fluents
    Time : simple(0..MaxTime);
    Duration(action) : rigid(1..MaxTime);

  variables
    t_s   : 1..MaxTime;
    t     : 0..MaxTime;
    a     : action;
    a_exp : explicitAction;


  axioms
    a_exp causes Time=t if t=Time+Duration(a_exp);

    default Duration(a)=1;

    exogenous Wait(t_s);

    Duration(Wait(t_s))=t_s;


module TRANSFER;

  actions
    Transfer(1..MaxAmount, Resource, Accumulator, Accumulator);

  fluents
    Amount(Resource, Accumulator) : simple(0..MaxAmount);

  variables
    m,m1 : 0..MaxAmount;
    n    : 1..MaxAmount;
    r    : Resource;
    x,y  : Accumulator;
    p    : Place;

  import LOCAL;

  axioms

    exogenous Transfer(n,r,x,y);

    inertial Amount(r,x);

    nonexecutable Transfer(n,r,x,y) if Amount(r,x) < n;
    nonexecutable Transfer(n,r,x,y) if Amount(r,y)=m1 & MaxAmount < m1+n;

    Transfer(n,r,x,y) causes Amount(r,x)=m if Amount(r,x)=m1 & m1=m+n;
    Transfer(n,r,x,y) causes Amount(r,y)=m1 if Amount(r,y)=m & m1=m+n;

    % These will help us say that transfers obey locality
    Theme(x, Transfer(n,r,x,y));
    Theme(y, Transfer(n,r,x,y));

    % Thought about this, but it won't work if ther are multiple
    % groups
    % constraint Amount(r,x) =< Capacity(x);

module BUY;

  objects
    Money   : Resource;

  actions
    Buy(Buyer, 1..MaxAmount, Commodity, Seller, 1..MaxAmount); 

  fluents
    Price(Commodity) : rigid(1..MaxPrice);

  variables
    n    : 1..MaxAmount;
    n2   : 1..MaxPrice;
    c    : Commodity;
    b    : Buyer;
    s    : Seller;
    x    : Thing;
    cost : 1..MaxAmount;

  import TRANSFER;
    Transfer(n,c,s,b) is Buy(b,n,c,s,cost);

  import TRANSFER;
    Transfer(cost,Money,b,s) is Buy(b,n,c,s,cost);

  axioms
    nonexecutable Buy(b,n,c,s,cost) if Price(c)=n2 & cost!=n*n2;
