512 Final Homework

Demonstration

If you are logged in on rabbit, the command ~demo/creatures (don't miss the wiggle at the beginning) will run a little demonstration. It isn't very exciting, but you can see it run and get the kind of idea.

Description

Write a program that simulates semi-intelligent creatures moving about and in some way interacting in a simulated world. The simulated world can be extremely simple, perhaps just an enclosed two-dimensional rectangle (although from the demonstration you can see that it is really easy to make something more complex and interesting). The creatures don't need to have any real intelligence, it can be all fake. They should move around in a non-trivial way (not just at random or in little circles etc) and they should be "aware" of each others' presence. How they decide to move is up to you; how they interact with each other is up to you; what their world is like is up to you; how you display the simulation is up to you.

If you use the "curses" or "ncurses" package (info here) it will be fairly easy to produce a real-time display of exactly what is going on. But that is not required.

The creatures should communicate with each other when they meet (not necessarily every time, but at least sometimes). It doesn't really matter what they communicate, so long as it is in some way real information. Perhaps you could scatter some "interesting" objects about their world for them to find and tell each other about.

The creatures must be object oriented (that is, there should be a class to represent creatures, with appropirate methods and members, and each creature should be represented by an object in that class).

The creatures should run independently in their own threads (info here), making their own moves in their own time. They should not simply be animated by a "main program" that processes each one in turn in an endless loop.

Because the creatures run in their own time in their own threads, you will have to be careful to avoid the problems that occur when two threads try to modify the same data at the same time. This should only be a problem in communications, so make sure you use an interlocked queue (info here) or something similarly protected to deliver messages.

Design Ideas

Just to get you started; by no means requirements.

It might be a good idea to have the World itself represented by a single object. There will probably only be one world in your simulation, so defining a whole class just for one object may seem a little wasteful, but it does help to keep things organised. Instead of dozens of different global variables to represent various aspects of the world, you'd just have a single globally accessible object, with public members or methods to give access to the important thing.

Assuming that the class to represent the world is called "World", and the class to represent creatures is called "Creature", you would probably have something like this (only larger):

class World
{ ...
    Creature *population[POPMAX];
    int threads[POPMAX];
    int current_population;
    char map[MAPMAX][MAPMAX];
  ...
    World(void); // initialise map, create creatures, start them running
    void redisplay(void);
  ... };
An array of pointers to creatures lets you keep track of the world's population easily; each creature can be known by a number, and can then be found by other creatures just by looking in the population array.

Remember that when you start a thread, you get an integer that can be used to refer to that thread again later (this allows you to check to see if a particular thread is still running or if it has terminated). The "threads" array is simply used to keep these integer thread references safe.

Making the map a two dimensional array of characters is a very simple way of doing things. You may choose to have something more sophisticated.

The redisplay() function may or may not be needed, depending on the design you choose. The idea here is that each creature as it is running will occasionally decide to move or do something else that should make the display move. Curses and ncurses don't automatically update the display after every change, they need to be told to do it. So, whenever a creature has made all the modifications to the world that it needs to make, it can call the world's redisplay() method. This would be where curses is told to do its stuff (by refresh() or wrefresh()). This way, you can keep all the curses operations insode the World class, and none of the rest of the program will need to even know they exist.

You will almost certainly design your creature class to have at least the following parts:

class Creature
{ ...
    char myname;
    int mypos_x, mypos_y;
    World *w;
  public:
    Creature(World *wo);
    void main(void);
    void tell(Message m);
  protected:
    Queue incoming_messages;
  ... };
The tell() message is used by other creatures (and anything else that exists) to communicate with this creature. Remember, all communications have to go through a special queue. It would be very messy to make everybody deal with that queue individually, so each creature has its own tell() method. All the tell method does is add the message it is given to the creature's queue. So to send message M to creature C, anyone in the world can just say C.tell(M) or C->tell(M) and everything is handled automatically. The creature can look at the queue whenever it likes, and receive the messages it was sent.

The idea here is that a creature knows its own name and position in the world. (The name is just a single character so that it will fit nicely in the map for displaying). The constructor for a creature initialises all the members approriately. it would be a very good idea if a creature also has a pointer to the world it is part of, then you might not need ugly global variables at all.

Some other function (probably a method (perhaps the constructor) of the World object) will create all the creatures, then start them running in their own threads. The bit of program that creates the creatures and starts them running might look like this (assuming it is a method of World):

  for (int i=0; i<desired_population; i+=1)
  { population[i]=new Creature(this);       // this creates the creatures
    current_population=i+1; }
  ...
  for (int i=0; i<current_population; i+=1)
  {                                         // this starts them running
    threads[i]=start_thread(creature_starter,i); }
The start_thread function is from the threads library, it is what brings something to life by starting a new thread for it to run in. It expects to be given a function to call once the thread is started (unfortunately it can't just be given an object and told "run this"). It is also given an integer which can be used to tell it exactly what the new thread should be doing. In this case, we use the integer to tell it which creature it should be breathing life into. All it has to do is find that creature in the population array, and call its main() method. Because creature_starter() is automatically called when a new thread is created, the new thread will be running a creature's main() method.
void creature_starter(int n)
{ Creature *c=the_world.population[n];
  c->main(); } 
That is why the creatures have a main() method. It is just like the main() function of a program; it is the whole life of the creature. Once main() exits, the creature is dead.

Therefore, each creature's main() method should have a non-terminating loop. Each time round the loop, it can look at its message queue, to see if anyone has sent it a message, it can decide whether or not it wants to move somewhere, it can do anything else it likes, but IT MUST take a little rest (using the sleep() or usleep()) function. If it doesn't take a little rest it will just swallow up all of the CPU time, and no other creatures will be able to do anything, and nobody else will be able to use the computer. usleep() takes a single integer parameter, which tells it the number of micro-seconds (millionths of a second) to rest.

So your creatures' main() methods might look something like this:

void main(void)
{ ...  // set up before starting
  while (alive)
  { usleep(200000);
    if (!(incoming_messages.isempty()))
    { Message *m=incoming_messages.remove();
      // deal with message *m }
    if (you want to move)
    { work out where, update the world's map
         w->map[oldy][oldx]=' ';
         w->map[y][x]=myname;
      and do all the other related stuff, then call
         w->redisplay(); }
    if (you want to do anything else)
    { do it } } }
(You will probably find it much easier if the World object provides a special method that creatures can use to update the map, so they can't get it wrong. The movement code above might then become: w->move_me(myname,x,y);.

That should give you something to think about.