HOG2

Note: This is older documentation for simulating agents. It is still accurate, but a majority of projects involve other environments besides agent-based environments.

Basic World Objects

There are two basic types of units that can exist in the world, display units and world units. Display units are drawn into the world, and can be used as placeholders in the world, but they can never move or do any calculations. Also, display units do not block other units in the world. A world unit is the basic type of unit that can move around the world, which will be described in more detail in the next section.

The unit type is specified from the result returned by the function getObjectType (No longer exits! TODO: Update).

Moving in the world

The Unit is the most basic object in the world. At each time step in the world a unit is asked which direction it wants to move via the function MakeMove. For a moment we'll ignore the parameters to MakeMove, and focus on the calling conventions. When asked to make a move, a unit must return the direction it would like to move, one of: kN, kNE, kE, kSE, kS, kSW, kW, kNW. Additionally, if the unit does not wish to move, it can return kStay. And, if the unit is allowed to teleport, it can return kTeleport.

After returing a value from MakeMove, the unit simulation verifies that the move is legal, and then calls UpdateLocation on the unit, informing the unit of its current location and whether the previous move was successful or not. If a unit returns kTeleport the simulation will call GetLocation on the unit and move the unit directly to the returned location.

The unit simulation measures the amount of thinking time used by each unit in both the MakeMove and SetLocation functions. Details of how these measurements are used can be found in Simulating the world.

Map and Reservation providers

When a unit is asked to move it is provided with a mapProvider (No longer exists! TODO: Update) and a reservationProvider. A map provider can return both a map of the world and an abstract map of the world. The unit can locate itself on the map and use that information to do any planning in the map world. The maps are not guaranteed to be exact representations of the world. For instance, if a unit is exploring the world the map may change as new parts of the world are explored.

Similarly, a reservationProvider can provide information about the other units in the world and make reservations for paths the unit would like to travel. But, like the maps, the information provided by the reservation provider may not be completely accurate. A unit, for instance, may only know about the other units in his vicinity, or reservations may only be shared by a select group of units. There is no default implementation for reservations, so units will always successfully make their reservations.

Provided unit implementations

The file unit.h declares the basic unit class, as well as a few simple implementations: a teleporting unit, a randomly moving unit, and a unit that uses the right-hand-rule to walk around the world. (Note that if the right-hand-rule unit is not next to a wall it will just walk in cicles.)

In the shared directory more one useful unit class is defined: a search unit. The search unit takes a search algorithm as a parameter. This unit asks the search algorithm to plan a path for the unit, and then caches the path returned by the unit, executing it one step at a time in the world.

Shared memory for units

By itself a single unit has only limited usefulness for experiments. In practice we may want many units to share a view of the world, or to cooperate in building a map or pathing through the world. Unit groups are the best mechanism for such needs.

If a unit is part of a unit group, the simulation will ask the unit group to make a move on behalf of each unit in that group. Usually the simulation will do some pre-processing on that request, and then ask the unit which move it would like to make, returning the result to the simulation.

An example would be a group of units that build a map together SharedAMapGroup. In this example the unit group is responsible for building a localized unit view of the world based on the global view provided by the simulation. So, each time a unit moves, the SharedAMapGroup first intervenes to see if the unit has explored any new territory, and if it has, it updates the local (partially complete) map in the unitGroup. Then, the unitGroup tells the unit to move, passing itself as a map provider.

For each step in the unit simulation, a unit group has the chance to do its own processing via the think function.

Simulating the world

The unitSimulation is in charge of allowing units to move around the world, enforcing limits on where units can move. The unitSimulation also maintains the world clock and draws the world when a visual display is needed.

As units and unit groups are created they must be added to the simulation. Units can optionally be specified as non-blocking when they are added to the simulation.

The most important function for a unitSimulation is stepTime, where all units are advanced forward in time by that increment of time. There are many different simulation options that can be set to provide slightly different simulation parameters. See the unitSimulation documentation for more details on these.

A unit simulation can be paused. If paused, all calls to stepTime will be ignored until the simulation is unpaused.

Spacial abstractions of maps

The point of this document is not to completely explain spacial abstractions, but to give an overview of how they work. Technical reserach papers are available which give a more in-depth overview of the concepts involved. See:

http://www.cs.ualberta.ca/~nathanst/papers.html

That being said, map abstractions are used widely within the HOG2 environment. The mapAbstraction class contains the relevant functions for converting between map coordinates (getNodeFromMap) and extracting information about the abstractions available.

The function getAbstractGraph will return an abstract graph at any particular level of abstraction. At the lowest level of abstraction this is just a connectivity map of the underlying map, with each node in the graph corresponding to a tile on the Map, and each edge representing that a unit can travel directly from one tile (node) to another.

Each node has a set of labels describing the place of that node in the hierarcy of abstractions. Each label can either be accessed as a LONG value or a FLOATING POINT value. The labels are as follows:

kAbstractionLevel (LONG) The level of abstraction in the graph. Level 0 is the original map.

kNumAbstractedNodes (LONG) The number of nodes abstracted by a particular node. The node ID's of these nodes are stored in kFirstData (1...kNumAbstracted Nodes). At level 0 this value will always be 0.

kParent (LONG) The parent that abstracts this node in the next abstraction level of the graph.

kTemporaryLabel This label can be used temporarily by any

kXCoordinate (private cache) kYCoordinate (private cache) kZCoordinate (private cache) kNodeBlocked (currently unused - LONG) kNodeWidth (currently unused - LONG)

kFirstData (LONG) Other relevant data is stored here. At level 0 you can find the x/y coordinates of the underlying map location here. At other levels you find a list of children. Arbitrary data can be stored in successive ID's, although there is no guarantee it will be preserved if nodes are deleted out of the graph.

Memory Management

It is important to use memory effienctly when building simulations that run over extended periods of time.

Any time an object is added to the unit simulation, the unit simulation is responsible for freeing that object. This includes, units, unitGroups, and abstract maps. Maps, which are added to mapAbstractions, will be freed by the mapAbstraction. Similarly, algorithms added to searchUnits will be freed by the searchUnit when appropriate.

New code should follow the general guideline that when one object is added to another, the parent object is in charge of freeing memory for the other object upon termination. Units and unitGroups are both added to each other, but they are also both added to the unit simulation, so the simulation is responsible for deallocating both of these objects.

Prev: Usage