+ Ents - Simplified Chatter Scenario

Further Information Sources

TODO for OndrejBojar

  • copy the commented interface of the needed modules so a place available for the students

Configuring and Running the Simulation

World Configuration

A world configuration is stored in a file with extension .zad and describes:

  • how many rooms are there in the world, what are their dimensions in tiles (rooms are rectangular)
  • where are the doors located and what rooms do the doors connect
  • where are the objects (including furniture) located and what are their properties
  • how many participants are to join the simulation, what are their properties and their types (ent/human). For computer controlled ents, a string argument can be supplied, this argument is then passed to the participant when it joins the simulation, so it can read appropried file of scripts/lexikons etc.

Preparing a Simulation

To prepare a new simulation, the best way is to make a copy of the sample configuration directory ("the world package") and modify the world configuration file, "svet.zad".

Before launching the simulation and always after modifying the world configuration file, use the command "make" in the world package. This will prepare the configuration stored in file svet.zad for running, or as we say, "instantiate the world".

Within one world package, you can prepare several configuration files, such as kitchen.zad, full_house.zad etc. Before starting the simulation, use "make kitchen.inst" or "make full_house.inst" to instantiate one of the worlds. (As expected, "make" and "make svet.inst" are equal.)

Launching a Simulation

Once the configuration is ready and a world has been instantiated, one can launch the simulation.

Always launch the world server and the browser from the same configuration directory (the world package). Only the "chatter" can be launched anywhere, as long as it doesn't need any extra configuration files (as long as the students do not add any such files).

  1. First launch the world server: "entiserver start"
  2. The server loads the instantiated world and will expect the participants to join. Ents/Browsers are expected to connect in fixed order, the order given in the configuration file.
  3. As requested, launch and connect browsers ("entiprohlizec") or ents ("chatter").

The world server expects the participants to connect to the default port number 24444. If you need to launch server on a differrent port, such as 12345, use the command:

entiserver start -p 12345

Remember, that all the participants must learn on what computer and what port is the server running. For the browser, specify the host and port in the Connect dialog box. The chatter connects as the default to the 'localhost' and the default port number 24444. If you wish to change this, use:

chatter -h another_host -p another_port

Chatter - The Lazy Ent

For purposes of experiments with natural language processing only, a simplified version of ent was prepared, the chatter.

The chatter does not contain any interpreter of scripts (a "planner") or the full Czech linguistic module. The chatter contains only the network part to communicate with the world server and the memory (the "database") to store the positions and properties of objects and ents.

The most important part of the chatter is the module chatting.m.shan, the chatting module. This is the only module of the sample chatter you need to modify in order to teach the chatter new (linguistic) skills.

The sample chatting module of the chatter is equipped with a very simple reacting behaviour:

  • As default, he does nothing (i.e. he sends "nop"s). Being idle for too long, he requests all the ent to speak to him.
  • If somebody speaks to him, he analyses the input and immediately answers. (As a demonstration, he can only add 1 to a number he hears. If he hears something that is not a number, he responds with an error message.)
  • If he hears somebody speaking to a third ent, he requests the speaker to speak to him. (He ignores the case when he hears himself, as it wouldn't be a good idea to request oneself to speak to oneself -- by the way, this would start an infinite loop.)

Because the chatter is too lazy to move etc., after a longer simulation, he'll become hungry, thirsty etc. Finally, he might even die (and so will the user, if he doesn't take care of himself). The world server treats death in a simplified way - for a certain period of time, the dead ent is excluded from participating the simulation (he doesn't receive any update of the room, nor is asked for atomic instructions), later, he rejoins the simulation again. However the death is not tested properly, so the users are kindly requested to avoid dying.

Calls to the Chatting Module

The main loop of the chatter calls the chatting module after every update received. The memory (database) has been already updated so the facts about objects are fresh.

The module chatting.m must export a predicate:

   % After every AI received, the main module calls this predicate.
   % The pointer to the main database of the ent is always given, so that
   % the chatter can observe its neighbourhood.
   % Given the list of input utterances and the result of our last atomic
   % instruction we are expected to output an atomic instruction.
   % For this can we can utilize and update out chatting__memory
   % and also we can touch the io.
:- pred chat(db_mint__db, utterances, ai_result, ai, memory, memory, io, io).
:- mode chat(in, in, in, out, in, out, di, uo) is det.

After every AI received, the main module calls this predicate. The pointer to the main database of the ent is always given as the first parameter, so that the chatter can observe its neighbourhood (see the chapter Database below). Given the list of input utterances (empty, if nothing was uttered) and the result of our last atomic instruction the predicate is expected to output an atomic instruction. For this can it can utilize and update its chatting__memory and also it can touch the io.

On output, this predicate is expected to respond with an atomic instruction (ai), either an explicit silence (that is to perform a Nop), or an utterance at another ent or possibly an action. Also, the chatter produces a new chatting memory.

The data types utterances, ai_result and ai are defined in the module communicator.m of the core ent library. (To be described.)

The Chatting Memory

The chatting memory can be used to store arbitrary information for the chatting module. You can redefine the type of the chatting memory, so that it will contain whatever you wish.

The chatting memory is initialized once, after receiving the first update from the world server. The module chatting.m must export a constructor for the chatter memory, that is the predicate:

:- pred init_memory(db_mint__db, maybe(string), chatting__memory, io, io).
:- mode init_memory(in, in, out, di, uo) is det.

This predicate can already access the main database, and also receives a parameter specified in the world configuration file. (This way, several different chatters can be distinguished if they join the same simulation. Ignore the parameter, if you do not need it.)

After initialization, the main loop stores the pointer to the chatting memory and passes the pointer to every call of the chatting module. The chatter can always update the memory and return a new version.

Console Mode of the Chatter

For purposes of programming and debugging, the chatter is ready for a server-less mode of operation. To run the chatter in this mode use:

chatter --static=fixed_database_file

The fixed_database_file is a snapshot of the chatters main database, i.e. the snapshot of the chatter's neighbourhood. Such a snapshot can be created in the regular mode of running by issuing a special command to the sample chatter: when in the simulation is running, tell "save filename" to the chatter. It should respond with something like "the snapshot was saved".

In the console mode, the world server is not contacted (no need to run the world server at all) and the chatter responds to messages typed directly on the command line. Here is a sample discussion:

klokan:chatter$./chatter --static=foo
Loading the database dump file: foo
Starting to chat on the console, the user is the ent id XXX
Say something:
> Hello.
Received: Hello.
Got Utterances: [utterance(268369920, 0, "Hello.")]
Got Last RetVal: 0
I say: Sorry, I understand only plain numbers.
> 1234
Received: 1234
Got Utterances: [utterance(268369920, 0, "1234")]
Got Last RetVal: 0
I say: 1234 plus 1 is 1235
> Chatter finished.

Use Ctrl-D to stop the conversation.

Basics of the Project The Ents

This chapter describes the core ideas of the project The Ents you need to understand

The Ato-protocol

The world server and the participants of the simulation (be they ents or browsers) communicate with so called "ato-protocol". The world server describes with this protocol all the changes that have just happened in the current room (incl. what was uttered), and the participants express an atomic instruction, what they want to do now.

The network module of the chatter knows the ato-protocol and updates all the information stored in chatter's database. The network module also simplifies the utterance protocol, i.e. the encoding of uttered sentences.

In the simplified chatter scenario, you don't need to know anything about the ato-protocol or updating the memory. You just need to know, when is the chatting module called.

Handles, Symbolic Handles and Preprocessing

Handles

Everything in the world of ents has a unique identification number, so-called handle. Handles are assigned not just to objects and ents, but also to all the possible values of all the properties, to all the tiles in the rooms, all the actions that can be performed and all the moments in time. All the components of simulation (World Server, Browser, Ent or Chatter) use the same set of handles. Handles are the "common dialect".

Type, Token, Subtype, Main Type

Every handle is a four-byte unsigned integer. For simplicity, handles have an inner structure. The first two bytes specify the type of the thing (the type of the object, the room, where the tile is). The second two bytes specify the token (id of the object of the given type, the coordinates of the tile within the given room). The type of handles is further divided into main_type (first half byte, specifies the kind of the handle -- handle of an object, handle of a tile, handle of time) and subtype (the type of the object etc.).

Usually, the token part equal to zero means "any object of the given type". The handle of both type and token equal to zero is used to represent any handle at all, it is a "free variable".

All the routines for manipulating handles are stored in the module handle.m. This module also defines the most useful handles as constants/functions, such as handle__self, handle__ent (for any ent) etc. See the module for the details.

Symbolic Handles

For simplicity and consistence, no handle should be entered in the code as the number. All the important handles have a string alias name, so called symbolic handle.

For example the name HanyH is used for the value 0x0. The name HOpenH is used for an *o*bject of the type pen and the name HXopenH is used for the atomic instruction of opening a thing. Generally, all the symbolic handles are enclosed in H...H and the first character after the H is used to distinguish main types.

Instead of preparing a source file foo.m and using numeric handles in it, create foo.m.shan ("shan" for symbolic handles) and use symbolic handles. Then run the script dohandles.pl foo.m.shan to obtain the file foo.m. (Usually this type of automatic preprocessing is set up in your M(m)akefile, so you just need to remember to modify the files *.m.shan and not the *.m.)

The full list of symbolic handles is available in the file $ENTIROOT/conf/handles. The script dohandles.pl uses this handles register to assign the numbers.

Compilation Caveat

You may have already noticed the main problem with using (symbolic) handles: once anybody changes the file $ENTIROOT/conf/handles, all the sources of all the components (Entiserver, Entiprohlizec, etc., including your code) have to be passed to dohandles.pl and recompiled again. Or the other way round: if the components are compiled and installed for you in a global way, you are not allowed to add any new handles. The good news is that you should never need to add new symbolic handles.

The Memory of the Ent (the "Database")

The ent (and the chatter) has built in a simple memory, also called the "database". This memory stores information about observed objects, their location and other properties. The information of all the visible objects is updated in every turn of the simulation. For invisible objects, the database stores the most recent information. In other words, no history of object properties is stored. To log this type of information, your code must explicitly make a copy of the properties of interest.

The Structure of the Database

The structure of the database is fairy simple: it is a list of n-tuples of handles. The lists are indexed by special set of handles, so called "senses" (or meanings), such as HShavecolor_obj_colorH, HSposition_obj_tile_sinceH. Within a list, all the n-tuples are of the same length (the same n, number of arguments). The number and meaning of the arguments is arbitrary but fixed for a given sense. The name of the sense indicates the number of arguments and their meaning quite clearly: under the sense HSposition_obj_loc_sinceH, the n-tuples are triples {handle of the object, handle of its position, handle of the time since when is the object there}.

Using the Database

Upon startup (and contacting the world server) the database is inited and updated for you. You obtain a pointer to the database. This pointer is needed when accessing the database.

The predicates for querying and manipulating the database are stored in the module db_mint.m.shan. (The name of the module comes from database-mercury-interface, as for historical reasons the database is implemented in C.)

To query the database, use the predicate:

:- pred db_mint__dbq(db, handle, list(handle), list(handle)).
:- mode db_mint__dbq(in, in, in, out) is nondet.

Given the pointer to the database, the sense handle and the arguments "sample", this predicate consecutively returns all the matching tuples stored in the database (or immediately fails if no matching tuple is found). For example the call:

dbq(Database, HShavecolor_obj_colorH, [HOpenH, HPcolor-redH], FoundArgs)

will return all the known red pens (in fact tuples [pen handle, HPcolor-redH]).

A predicate for modifying the database is available too. In general, you should never want to modify the database, so I will not describe it. Use the custom chatting__memory to store any kind of information.

-- OndrejBojar - 04 Apr 2003
Warning: Can't find topic Enti.WebLeftBarExample

 
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback