Beginning with the version 2.0.0.205 of MCell users can program new rules that do not have to obey limits of any predefined CA families. This page is a short tutorial on programming own Cellular Automata.
User rules are programmed as stand alone DLLs that are dynamically loaded to MCell at runtime. One can compile them using any Windows 32-bit compiler supporting __stdcall (or _pascal) method of passing parameters. Provided examples show programming user DLLs in Microsoft Visual C 6.0, Borland Delphi 4.0/5.0, and in Borland C++ Builder 3.0/4.0; one should however have no problems with other compilers.
One example tells more than thousands of words, so let's first look at a simple example in C:
///////////////////////////// // Sample 2D rule - Conway's Life with decay // Calculate the new state of the 'Me' cell. This function is executed for every cell in a grid. int __stdcall __declspec(dllexport) CARule(int Generation,int col,int row, int NW, int N, int NE, int W, int Me, int E, int SW, int S, int SE) { int iSum; int newState; iSum = (NW != 0) + (N != 0) + (NE != 0) + (W != 0) + (E != 0) + (SW != 0) + (S != 0) + (SE != 0); newState = 0; if (Me == 0) // was dead { if (iSum == 3) newState = 1; } else // was alive { if ((iSum == 2) || (iSum == 3)) newState = Me + 1; // make it older } return newState; } ///////////////////////////// // Setup the rule. // The function is called immediatelly after this rule is selected in MCell. void __stdcall __declspec(dllexport) CASetup(int* RuleType, int* CountOfColors, char* ColorPalette, char* Misc) { *RuleType = 2; // 1 - 1D, 2 - 2D, 3 - Margolus *CountOfColors = 16; // count of states, here 16: 0..15 strcpy(ColorPalette, "MCell standard"); // optional color palette specification strcpy(Misc, ""); // optional extra parameters, none in this case }
Note that the folder \MCell\UserDLLs\DLLs\Sources\ contains many examples of rules programmed in several languages. I didnt succeed yet in programming user DLLs in Visual Basic I see no way of exporting programmed functions. If anybody knows how to do it Ill be very thankful for the information.
User DLLs are expected to provide two exported functions: CASetup and CARule (or CASetup and CARule1D in case of 1-D rules). Starting with version 2.50 of MCell user DLLs optionally can contain two more functions: CAPass and CAClose.Since the version 4.20 the next function, CAConfig, is available.
Note: programming user rules in Microsoft Visual C++ got much simpler!
The procedure is used to initialize the rule. Its parameters return to the program information about the rule type (1- or 2-D), count of states, optional color palette request and optional other parameters.
Syntax:
C / C++:
void __declspec(dllexport) __stdcall CASetup(int* RuleType, int* CountOfColors, char* ColorPalette, char* Misc)
Pascal:
procedure CASetup(var RuleType, CountOfColors: Integer; ColorPalette, Misc: PChar); stdcall;
Parameters:
RuleType | integer specifying the rule/neighborhood type. Assign 1 to define the
1-D rule, 2 to define the 2-D rule, and 3 to define a Margolus neighborhood
rule. Example: C/C++: *RuleType = 2; Pascal: RuleType := 2; |
CountOfColors | integer specifying the number of states of the automaton. The number
includes also the zero state, so specifying for example 7 results in an automaton with
cells in states 0..6. Example: C/C++: *CountOfColors = 5; Pascal: CountOfColors := 5; |
ColorPalette | pointer to the allocated string buffer of 256 characters. It can be used
to specify the color palette that should be automatically loaded when the automaton is
selected. One can also ignore this parameter. Example: C/C++: strcpy(ColorPalette, 8 colors); Pascal: StrCopy(ColorPalette, '8 colors'); |
Misc | pointer to the allocated string buffer of 256 characters. It can be used
for additional initialization of the DLL. On entry the buffer contains several keywords that can be optionally used for the DLL
initialization: On exit the user set into the buffer optional keywords. Current version supports one
keyword - "ALLCELLS". If the keyword appears in the returned 'Misc' string, the
rule evaluator calls the CARule function for every cell in the board, even if the cell
appears to have no chances for changing the state. |
The function is used to calculate and return the new state of the cell given as the Me parameter. The resulting new state can be based on the cells previous state, and states of neighbours, as in many standard rules, but can also use randomizing, the cycle (generation) number and the cell location on the lattice. One cannot rely on the order in which board cells are evaluated. Currently the board is divided into 10x10 blocks and CARule is called for all cells within the first block, next for all cells within the second block, ..., up to the last block.
Syntax:
C / C++:
int __declspec(dllexport) __stdcall CARule( int Generation, int col, int row, int NW, int N, int NE, int W, int Me, int E, int SW, int S, int SE)
Pascal:
function CARule(Generation,col,row, NW,N, NE, W, Me,E, SW,S, SE: Integer): Integer; stdcall;
Parameters:
Generation | integer specifying the generation (cycle) count. |
col | integer specifying the cell column. |
row | integer specifying the cell row. |
Me | integer specifying the state of the focused cell. State is in range 0..CountOfColors-1. |
NW,N,NE,W,E,SW,S,SE | integers specifying the states of the cell neighbours. States are in range 0..CountOfColors-1. |
Example:
C/C++:
return (NW + N + NE + W + Me + E + SW + S + SE) % 9;
Pascal:
CARule := (NW + N + NE + W + Me + E + SW + S + SE) mod 9;
The function is used to calculate and return the new state of the cell given as the Me parameter. The resulting new state can be based on the cells previous state, and states of left and right neighbours, as in many standard rules, but can also use randomizing, the generation number and the cell location in the row.
C / C++:
int __declspec(dllexport) __stdcall CARule1D( int Generation, int col, int l10, int l9, int l8, int l7, int l6, int l5, int l4, int l3, int l2, int l1, int Me, int r1, int r2, int r3, int r4, int r5, int r6, int r7, int r8, int r9, int r10)
Pascal:
function CARule1D(Generation,col, l10,l9,l8,l7,l6,l5,l4,l3,l2,l1, Me, r1,r2,r3,r4,r5,r6,r7,r8,r9,r10: Integer): Integer; stdcall;
Parameters:
Generation | integer specifying the generation (cycle) count. |
col | integer specifying the cell column (position in the active row). |
Me | integer specifying the state of the focused cell. State is in range 0..CountOfColors-1. |
l10,l9,l8,l7,l6,l5,l4,l3,l2,l1 | integers specifying the states of the cell left neighbours. l10 is left-most parameter, l1 is the immediate left neighbour. States are in range 0..CountOfColors-1. |
r1,r2,r3,r4,r5,r6,r7,r8,r9,r10 | integers specifying the states of the cell right neighbours. r10 is right-most parameter, r1 is the immediate right neighbour. States are in range 0..CountOfColors-1. |
Example:
C/C++:
return (l2 + l1 + Me + r1 + r2) % 5;
Pascal:
CARule1D := (l2 + l1 + Me + r1 + r2) mod 5;
The procedure is used to determine the new state of four passed cells,
building one 2x2 block of the Margolus neighborhood. The resulting new states can be based on the cells' previous
states as in many standard rules, but can also
use randomizing, the generation number and the cells location in the grid.
The procedure is available since MCell v.4.0.
C / C++:
void __declspec(dllexport) __stdcall CARuleMG(int Generation, int col, int row, int* ul, int* ur, int* ll, int* lr)
Pascal:
procedure CARuleMG(Generation,col,row: Integer; var ul, ur, ll, lr: Integer); stdcall;
Parameters:
Generation | integer specifying the generation (cycle) count. Generation % 2 tells if the cycle is even or odd. |
col | integer specifying the ul (upper left) cell column. |
row | integer specifying the ul (upper left) cell row. |
ul | upper left cell (passed by reference) |
ur | upper right cell (passed by reference) |
ll | lower left cell (passed by reference) |
lr | lower right cell (passed by reference) |
Example:
C/C++:
if (Generation % 2) // odd pass { iTmp = *ul; *ul = *lr; *lr = iTmp; } else // even pass { iTmp = *ur; *ur = *ll; *ll = iTmp; }
Pascal:
if (Generation mod 2) = 1 then // odd pass begin iTmp := ul; ul := lr; lr := iTmp; end else // even pass begin iTmp := ur; ur := ll; ll := iTmp; end;
If the CAPass procedure is defined in a user DLL, it gets called both before and after
each pass (cycle). The procedure is optional, the use of it depends to the user.
The procedure is available since MCell v.2.50.
Syntax:
C / C++:
void __declspec(dllexport) __stdcall CAPass(int Generation, int Population, int PreFlag, char* Misc)
Pascal:
procedure CAPass(Generation, Population, PreFlag: Integer; Misc: PChar); stdcall;
Parameters:
Generation | integer specifying the generation (cycle) count. |
Population | integer specifying the total count of alive (non-zero) cells. |
PreFlag | integer specifying if the call is done before the pass. PreFlag is 1 before the pass, and 0 after the pass. |
Misc | pointer to the allocated string buffer of 256 characters. It can be used in next versions for extra communication with DLL. One should ignore this parameter in this version. |
If the CAClose procedure is defined in a user DLL, it gets called before the DLL is
disconnected. The procedure is optional; it's usually used to perform the final
clean-up as freeing allocated memory.
The procedure is available since MCell v.2.50.
Syntax:
C / C++:
void __declspec(dllexport) __stdcall CAClose(int Generation, int Population)
Pascal:
procedure CAClose(Generation, Population: Integer); stdcall;
Parameters:
Generation | integer specifying the generation (cycle) count. |
Population | integer specifying the total count of alive (non-zero) cells. |
Optional CAConfig function allows programming interactively configurable DLLs. When the function is detected in a DLL, MCell’s “Rules setup” offers a new “Configure” button on the UDLL page. It’s up to the DLL author what will happen when the function gets invoked. Usually DLLs will read the rule string from the “Rule” parameter, open a custom dialog for modifying the, and send the modified rule back to the program.
See “\MCell\UserDLLs\DLLs\Sources\D5_WeightedGen” project for an example of a configurable DLL.
Syntax:
C / C++:
void __declspec(dllexport) __stdcall CASetup( int* RuleType, int* CountOfColors, char* Rule, char* Misc)
Pascal:
procedure CASetup(var RuleType, CountOfColors: Integer; Rule, Misc: PChar); stdcall;
Parameters:
RuleType | parameter currently ignored; |
CountOfColors | parameter currently ignored; |
Rule | pointer to the allocated string buffer of 256 characters. On entry the buffer contains the active rule string. On exit the buffer contains the modified rule string that should be activated in the program. Example: C/C++: strcpy(Rule, ”c=3,r=1”); Pascal: StrCopy(Rule, 'c=3,r=1'); |
Misc | pointer to the allocated string buffer of 256 characters. Parameter currently ignored; |
Note!
This simplified method of creating user DLLs in MSVC works in MCell starting with
2.60.0.399. Older versions require DLLs created with a use of a DEF file.
Big thanks to Brian Behlendorf for writing Borland C and Borland C++ Builder DLL templates and making this recipe!
Note that such created DLLs are not stand alone - they require Borland runtime libraries. To create DLLs that will work on any computer, perform two additional steps:
Webmaster: Mirek Wojtowicz http://www.mirekw.com |
MCell mirrors: |
Last update: 10 Mar 2002