3.2. Families of Fluents, Actions and Objects

Many action domains involve parameterized families of fluents and actions, rather than individual fluents and actions as in the examples discussed in this tutorial so far. For instance, in the blocks world (Section 3.1), a configuration of blocks can be described by a family of fluents -- the location-valued fluents loc(B), where B is an object of sort block. Actions in this domain are move(B,L) ("move block B to location L"); this is a family of actions with two parameters. Here is how these families can be declared in the language of CCalc:
:- constants
 loc(block)           :: inertialFluent(location);
 move(block,location) :: exogenousAction.
After that, expressions such as loc(a) and loc(B) can be used as names of fluents (here a is an object of sort block, and B is a variable for blocks), and similarly for actions. For instance, the effect of moving blocks can be described by the schematic proposition
move(B,L) causes loc(B)=L.
As another example, consider the enhancement of the shooting domain from Section 2.1 in which there are two potential targets, Turkey 1 and Turkey 2, and the effect of shooting depends on how the gun is aimed (NMCT, Section 5.1). The CCalc representation of that domain shown in file turkeys declares the family of fluents alive(turkey) and the family of actions aim(turkey).

In this example, an interesting problem arises in connection with the fluent target, which describes how the gun is aimed. Its values are objects of sort turkey. But we want to allow the gun not to be aimed at any target at all; this is what happens immediately after loading. How can we declare such a "partially defined fluent"? Technically, this is accomplished by including the special symbol none in the set of its possible values. The supersort mechanism, described in Section 3.1, can be used to introduce an auxiliary sort that can be referred to in the declaration of target:

:- sorts
 aux >> turkey.

:- objects
 none    :: aux.

:- constants
 target  :: inertialFluent(aux).
But adding none to a sort is such a useful operation that the language of CCalc provides special, more compact syntax for it. In file turkeys we simply write:
:- sorts
  turkey.

:- constants
  target  :: inertialFluent(turkey+none).
The query at the end of file turkeys contains a variable:
:- query
maxstep :: 5..6;
0: alive(T),
   target=none,
   -loaded;
maxstep:
  -alive(T).
Variables in queries are understood to be universally quantified, so that this query has the same meaning as
:- query
 maxstep :: 5..6;
 0: alive(turkey1) & alive(turkey2),
    target=none,
    -loaded;
 maxstep: -alive(turkey1) & -alive(turkey2).
The implicit universal quantifier over T can be also shown explicitly:
:- query
 maxstep :: 5..6;
 0: [/\T | alive(T)],
    target=none,
    -loaded;
 maxstep: [/\T | -alive(T)].
If we replaced the universal quantifier /\ (that is to say, multiple conjunction) in the last line with the existential quantifier (multiple disjunction) \/ and wrote
 maxstep: [\/T | -alive(T)]
then the goal would be to kill at least one turkey. To express that the goal is to kill exactly one turkey, we can write
maxstep: [\/ T /\ T1 | alive(T1) <-> T=T1]
where T1 is another variable of sort turkey.

The answer to the query produced by CCalc begins with the lines

0:  alive(turkey1)  alive(turkey2)  

ACTIONS:  load  

1:  loaded  alive(turkey1)  alive(turkey2)

ACTIONS:  aim(turkey1)  

2:  loaded  alive(turkey1)  alive(turkey2)  target=turkey1  
Note that, in the output of CCalc, the value of target is shown in state 2, but not in states 0 and 1. This is because CCalc does not display the value of a fluent when that value is none, in accordance with the interpretation of that value as "undefined."

Exercise 3.2. Consider the modification of the domain from file turkeys in which two hunters with guns (mygun and hisgun) pursue three animals (buffalo, deer and fox). Initially the guns are loaded but not aimed, and the goal is to kill all three animals. Use CCalc to find a solution.

Exercise 3.3. John and Mary from Problem 3.1 (Section 3.1) have three coins: a penny, a nickel and a dime. Besides giving coins to each other, they can also throw them away. All coins are currently in Mary's possession. John wants to have exctly two coins, and he wants Mary to have nothing. Instruct CCalc to suggest an appropriate course of action.

If we want to talk about 100 turkeys, instead of two, the declaration

:- objects 
  turkey1, turkey2    :: turkey.
in file turkeys will become much longer. An alternative is to replace the individually declared objects turkey1, turkey2, etc., by a family of objects indexed by numbers:
:- sorts
  index; turkey.

:- objects 
  1..100               :: index;
  t(index)             :: turkey.

Exercise 3.4. Modify your solution to Exercise 3.2 to include 5 guns.


Forward to Section 4.1: Constraints
Back to Section 3.1: Objects and Sorts
Up to the Table of Contents