Coffee disambiguation (Dialog example)

From IFWiki

To do our disambiguating, we partly rely on the predicate (heads $), which effectively tells the program, "If disambiguating, ignore all names except these."

As helpful as this is, the real star of our show is (unlikely $), which allows us to not only prioritize our cupful of coffee but also make some versatile code that can disambiguate in a variety of contexts.

(story title) Coffee Disambiguation Example
(story author) Karona
(story noun) for IFWiki

(current player #player)

#kitchen
(name *) Test Kitchen
(room *)
(look *)
  This test kitchen is very clean. It's so clean, it hardly has anything in
  it. The living room is to the south.

(#urn is #in *)
(#mug is #in *)

(from * go #south to #room)

#urn
(name *) coffee urn
(heads *) urn
%% We use the item trait to make it possible to take an object:
(item *)
(container *)
(descr *)
  The urn is large, round, and heavy. It has to be large to hold the endless
  supply of coffee inside it. Perhaps you'd like to pour some into a mug?
(appearance *)
  You see (a *) here.

(#supply is #in *)

%% We override the standard library response.
(prevent [empty *])
  (#supply is #in *)
  You can't empty the urn.

(unlikely [pour $ into *])
  (#supply is #in *)

#supply
(name *) endless supply of coffee
(an *)
(potable *)
(descr *)
  Hello, tall, dark, and endless.

(before [pour *])
  (#player is in room $Room)
  (#mug is in room $Room)
  ~(#mug is #heldby #player)
  (first try [take #mug])

(unlikely [drink/taste *])
  (* is in room $Room)
  (#cupful is in room $Room)

(prevent [drink *])
  How rude! Use a mug!

(prevent [pour * into $Container])
  ~($Container = #mug)
  You can't pour coffee into that.

(prevent [pour * into $Container])
  (#cupful is #in $Container)
  ($Container = #mug)
  But the mug is full!

(perform [pour * into #mug])
  Using the urn, you fill the mug with hot coffee.
  (now)(#cupful is #in #mug)

(unlikely [smell *])
  (* is in room $Room)
  (#cupful is in room $Room)

(perform [smell *])
  Smells good. Endlessly good.

#mug
(name *) coffee mug
(heads *) mug
(item *)
(container *)
(descr *)
  The mug has the phrase "WORLD'S BEST EXAMPLE CODER" prominently on the
  outside. The inside of the mug is
  (if)(#cupful is #in *)(then)
      full of coffee.
  (else)
      empty.
  (endif)
(appearance *)
  You see (a *) here.

(instead of [fill *])
  (#player is in room $Room)
  (#supply is in room $Room)
  (try [pour #supply into *])

(instead of [drink *])
  *($Stuff is #in *)
  (potable $Stuff)
  (try [drink $Stuff])

(prevent [pour *])
  (#player is in room $Room)
  ~(* is in room $Room)
  But you don't have a mug!

#cupful
(name *) cupful of coffee
(potable *)
(descr *)
  The coffee in the mug looks quite drinkable.

(prevent [pour * into $Container])
  ~($Container = #urn)
  You can't pour the cupful of coffee into that.

(prevent [pour * into #urn])
  (#player is in room $Room)
  (#urn is in room $Room)
  The supply of coffee is already endless.

(prevent [empty (container $Container)])
  (* is #in $Container)
  That would be a waste of perfectly good coffee!

(prevent [empty (container $Container)])
  ~(* is #in $Container)
  ~(#supply is #in $Container)
  (The $Container) is already empty!

(perform [smell *])
  Smells good. A cupful of good.

(perform [drink *])
  You drink the mug of coffee. Aaah. The satisfaction.
  (now)(#cupful is nowhere)

#room
(name *) Living Room
(room *)
(look *)
  This pleasant living room is ever-so-lightly furnished with a coffee table.
  A kitchen can be found to the north.
(from * go #north to #kitchen)

(#player is #in *)
(#table is #in *)

#table
(name *) coffee table
(heads *) table
%% We make the table a supporter -- and not an actor supporter -- to allow
%% objects to be put on the table without allowing the PC to get on the table:
(supporter *)
(descr *)
  The coffee table is a modern piece of furniture with metal legs and a
  square glass top.

%% We put general code after specific code.

%% Liquids

%% We define all things that are potable (and thus our #supply and our #cupful)
%% to be liquids. Our code would work if we excluded this out and instead used
%% the predicate (potable $) pervasively, but the author might want to add other
%% liquids later, and using predicates in generic code is good practice.

(liquid $Stuff)  *(potable $Stuff)

(grammar [pour [single] [into in] [single]] for [pour $ into $])
(grammar [fill [single] with [single]] for [pour $ into $] reversed)

(instead of [pour $Obj into $Container])
  *($Stuff is #in $Obj)
  (liquid $Stuff)
  (try [pour $Stuff into $Container])

%% When actual coffee is in scope, we do not want the parser to understand POUR
%% COFFEE to be about the coffee table, the urn, or the mug. Declaring "table",
%% "urn", and "mug" to be heads (as we did above) would be sufficient, but here
%% we include versatile code that will handle liquids the author might include
%% later.
(unlikely [pour ~(liquid $Object)])

(prevent [pour $ into $Object])
  ~(container $Object)
  (The $Object) is not a container.

(prevent [pour $Object into $])
  ~(potable $Object)
  (The $Object) is not a liquid.

%% If there is an ambiguity, we assume that the player does not want to pour a
%% liquid into the container that already holds it.
(unlikely [pour $Liquid into $Object])
  ($Liquid is #in $Object)

(unlikely [pour $ into $Object])
  ~(container $Object)

(unlikely [pour $Object into $])
  ~(liquid $Object)

(grammar [pour [single]] for [pour $])

(unlikely [pour $Object])
  ~(liquid $Object)

(instead of [pour $Obj])
  *($Liquid is #in $Obj)
  (liquid $Liquid)
  (try [pour $Liquid])

(instead of [pour $Stuff])
  (#player is in room $Room)
  (#mug is in room $Room)
  (try [pour $Stuff into #mug])

(prevent [pour $Object])
  ~(liquid $Object)
  (The $Object) is not a liquid.

(grammar [fill [single]] for [fill $])

(grammar [empty [single]] for [empty $])

%% Potable

(unlikely [drink/taste ~(potable $Object)])

%% We divert TASTE to DRINK but only if the object is potable.
(instead of [taste (potable $Object)])
  (try [drink $Object])