System Overview - PathogenDavid/Periodic GitHub Wiki

The main app architecture of the Periodic project can be distilled into four parts:

  1. Sifteo Cube Management
  2. Element Cube
  3. Element Core
  4. Reaction Processing

Architecture

System overview

Sifteo Cube Management (main.cpp - PeriodicCore above)

This portion of the app is responsible for listening to Sifteo SDK events and dispatching them to the reaction processing system. It also wrangles the ElementCubes and triggers their rendering.

Its most important role is that it figures out what elements are involved in a reaction, and creates a Reaction instance for processing.

Element Cube (ElementCube)

An ElementCube wraps an instance of Element primarily to provide rendering facilities for that Element and to associate it with a specific Cube. It also provides some utility methods for managing the state of the element on the cube.

Element Core (Element)

An Element is primarily responsible for handling the database of available elements on the periodic table. It is also responsible for maintaining Bond information with other Elements and mutating its own state to a specific Reaction, Compound pair. Additionally, Element contains many utility functions for getting information about Elements it has bonds with.

Reaction Processing (Reaction, Element, Compound, Bond, BondSolution)

By far the most complex portion of the Periodic system is the reaction processing.

Following is a short list of important associations in the reaction processing system.

  • Every Element is involved in exactly one Reaction at most, all Elements in a Reaction are connected (perhaps indirectly) by a Bond.
  • Each Element has up to four Bonds with other Elements. Each Bond may have many BondSolutions associated between it and a Compound.
  • A Compound is a potential result of a Reaction. Each Reaction may contain many Compounds. The Reaction.Process method determines which Compound will be displayed to the user, this Compound will become the currentCompound for each Element it is associated with and that Compound is what will be used to mutate the Elements in Element.ApplyCompound.

Flow of events

Follows is a rough overview of the flow of control, starting in PeriodicCore.ProcessNeighborhood:

  1. A Reaction is created with a single Element in it, representing the root Element.
  2. All neighbors of that Element are added to the Reaction via Element.AddBond.
  3. Perform 2 recursively for each of those newly added neighbor Elements until no more new cubes can be added.
  4. Call Reaction.Process to process the reaction.
  5. Process the Elements in this Reaction to see if they might form a Compound. If we find a (potential) Compound, we create a new one and set all the relevant bond information for that Compound in each Element involved in the Compound.
  6. Determine the ideal Compound to be displayed to the user. (Right now, this algorithm is "Prefer the Compound with the most elements that doesn't contain potential bonds.")
  7. Apply that Compound to relevant Elements via Compound.Apply, which invokes Element.ApplyCompound.

Details of potential reaction processing

Please refer to the reaction possibility engine page for details on how it works.

General Notes

Static Allocation

Since by default, the Sifteo SDK doesn't have any facilities for dynamic memory allocation, everything needed by the app is allocated statically at compile time. Therefore, reactions manipulate the existing instance of Element rather than creating a new Element with the relevant attributes.

Because of this, we differentiate elements between raw base elements (which are in the element database) and mutated elements (which are the ones used in reactions.) All mutated elements have an associated raw base element. A mutated element can be reset to default using Element::ResetToBasicState or by changing it to a new raw element with one of the Element::GetRawElement methods.

As of v0.2s2, some basic dyanmic memory allocation via the ObjectPool class is available. The object pool is detailed in the following section.

If you do attempt to allocate a non-ObjectPool class dynamically (with new), you will get the following cryptic error:EXEC : -!- Slinky error : Taking address of undefined symbol '_Znwm'

Dynamic Allocation via ObjectPool

As of v0.2s2, some basic dynamic memory allocation facilities are available via the ObjectPool class. This class allows using new and delete to allocate objects from a statically-sized pool. The objects are still allocated statically by the compiler, but it is easier to instantiate and destroy the objects at runtime, which can make code easier to read.

To enable a type to use the object pool, simply include ObjectPool.h and publicly inherit it from the ObjectPool type. You will need to specify your type and the desired size of the object pool.

For example, the following will allow allocating up to 10 instances of SomeObject at runtime.

class SomeObject : public ObjectPool<SomeObject, 10>
{ /* ... */ }

Note: The ObjectPool has not been tested with child types, and may behave incorrectly if you attempt to do so.

Standard C Library

Because the Sifteo SDK does not include the standard C library, we don't use any of it. Any standard functions you see, such as strlen or strcmp are actually implemented within periodic.cpp.

Therefore: You should be wary of developing / testing outside of the Sifteo environment as you may develop code that won't compile.