TGB/Tutorials/Cellular Automata Laboratory

From TDN

Cellular Automata Laboratory Tutorial

Written for TGB/iTGB.

Written by:Pedro Vicente

In this tutorial we will extend the Game of Life Tutorial with the definition of new cellular automata classes.

Comments, questions?: Please use this forum

Source files : Download source files and executable from here

Getting started

In this tutorial we will extend the Game of Life Tutorial with the definition of new cellular automata classes. To see the Game of Life Tutorial please visitGame of Life.

We will be defining a Torque class named CaLab for the Torque script. Copy the level file used in the Game of Life or create a new project from scratch and edit the level file and add a parameter with the name of our new class to the t2dSceneGraph instantiation as in

%levelContent = new t2dSceneGraph(CaLab)

Then change the camera size parameters to

cameraSize = "64 96";

This class works both for TGB or iTGB, so these camera settings allow to simulate an iPhone portrait aspect ratio while running in Windows.

Adding a panel 

Our Cellular Automata Laboratory consists of a panel, where a state of the cellular automata can be chosen by mouse click (TGB) or touch (iTGB). Here's the definition of our panel

function CaLabPart1::CreatePanelTileLayer(%this)
{
    %this.PanelTileLayer = new t2dTileLayer()
    {
       class = "TileLayerPanel";
    }; 
    %this.PanelTileLayer.setUseMouseEvents( true );
    %tileCountX = 8;
    %tileCountY = 1;
    %tileSizeX = 4;
    %tileSizeY = 4;
    %this.PanelTileLayer.createLayer( %tileCountX, %tileCountY, %tileSizeX, %tileSizeY ); 
    %this.PanelTileLayer.setSize( %tileCountX *%tileSizeX, %tileCountY  * %tileSizeY );
    %this.PanelTileLayer.setGridActive( 1 ); 
    %this.PanelTileLayer.setPosition( 0, -45 );
    %this.getGlobalTileMap().addTileLayer( %this.PanelTileLayer  );
    for  ( %x = 0;  %x <  %this.PanelTileLayer.getTileCountX(); %x++)
    {
       %this.PanelTileLayer.setStaticTile( %x, 0, CaImageMap, %x );
    }
}


If you remember from the Game of Life Tutorial, our basic image consists of 8 square frames.

The code above just draws them at the center top of the screen. Here it is what we get

Note that we defined the panel with a class name "TileLayerPanel".

This is the Torque script way of subclassing. In this case we subclassed the class because we want it to respond to mouse events. Here's the custom mouse down event for the class

function TileLayerPanel::onMouseDown( %this, %mod, %worldPos, %mouseClicks )
{
   CaLab.pickPanelTile( %worldPos );
}

It calls a function of the main class. Here is the function:

function CaLabPart1::pickPanelTile( %this, %worldPos )
{
   %this.selected_state = getWord(  %this.PanelTileLayer.pickTile( %worldPos ), 0 );
}


We use the pickTile function oft2dTileLayer to initialize a variable that tells us what is the current state of the Cellular Automata.

The CA Grid


We need a grid to draw our Cellular Automata. Here it is

function CaLabPart1::CreateModelTileLayer(%this)
{
    %tileCountX = %this.model.get_size_x();
    %tileCountY = %this.model.get_size_y();
    %tileSizeX = 3;
    %tileSizeY = 3;
    %this.WallTileLayer = new t2dTileLayer()
    {
       class = "TileLayerModel";
    }; 
    %this.WallTileLayer.setUseMouseEvents( true );
    %this.WallTileLayer.createLayer( %tileCountX, %tileCountY, %tileSizeX, %tileSizeY ); 
    %this.WallTileLayer.setSize( %tileCountX * %tileSizeX, %tileCountY * %tileSizeY  );  
    %this.WallTileLayer.setGridActive( 1 ); 
    %this.WallTileLayer.setPosition( 0, 0 );  
    %this.WallSizeX = %this.WallTileLayer.getTileCountX();
    %this.WallSizeY = %this.WallTileLayer.getTileCountY(); 
    %this.getGlobalTileMap().addTileLayer( %this.WallTileLayer ); 
}


Very similar to the panel tile layer, since we also want the grid to respond to mouse events. This time we will use dragging the mouse to draw tiles on the grid, and assign the frame index directly to the C++ model.

function CaLab::pickModelTile( %this, %worldPos )
{
   %tile = %this.WallTileLayer.pickTile( %worldPos );
   %x = getWord( %tile, 0 );
   %y = getWord( %tile, 1 );
   %this.model.set_value( %x, %y, %this.selected_state );
   %this.DrawGrid();
}

For the custom tile layer to respond to mouse drag events, we defined.

      
function TileLayerModel::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
   CaLab.pickModelTile( %worldPos );
}

Here is an example where we selected the last frame on the panel and defined some grid positions by dragging the mouse

The C++ model

This time we defined a new class that attempts to simulate (probably in a not very scientific way) heat diffusion. We extended our basic Cellular Automata class for this. Here's the class declaration

class CaHeatSpread : public CaModel
{
   typedef ScriptObject Parent;

public:
   CaHeatSpread();
   bool onAdd();
   void onRemove();
   void initialize( S32 sx, S32 sy );

   DECLARE_CONOBJECT(CaHeatSpread);

protected:
    virtual int rule( int i, int j );
};

We have the virtual method rule, this time implemented as

int CaHeatSpread::rule( int i, int j )
{  
  int cell, next; VonNeumann_neighbourhood( i, j ); 
  if (state  < max_state ) 
  //if not last state 
  { next = (  N  + S + E + W ) / 4 + 1; 
  if ( next <   max_state ) cell = next;
  else if ( next == max_state ) cell = 0; //last turns first
  }
  else if ( state == max_state ) 
  {
   cell = max_state; //last stays last
  }
  return cell;
}


Putting it all together

Finnaly we need a constructor in script that instantiates our C++ model

function CaLab::OnAdd(%this)
{
   if( $platform $= "windows" ) 
   setScreenMode( 640, 960, 32, false );
   %this.model = new CaHeatSpread();
   %this.model.initialize( 20, 28 );
   %this.CreateModelTileLayer();
   %this.CreatePanelTileLayer();
   %this.DrawGrid();
   %this.selected_state = 0;  
   new ActionMap( CaActionMap );
   CaActionMap.bindCmd( keyboard, "right", "CaLabPart1.PlayerMovement( right );", "" );
   CaActionMap.bindCmd( keyboard, "left", "CaLabPart1.PlayerMovement( left );", "" );
   CaActionMap.push();
}

Here is the result of our Cellular Automata running.



Credits: This work was inspired from John Walker and Rudy Rucker's Cellular Automata