-4

im trying to code a Cellular Automaton using C++, for some reason however when the program is run, my rules either seems to be applied incorrectly, or not at all. Any advice would be appreciated.

Main simulation function:

/* Simulate Array */
int beginSimulation(char parentArray[],char childArray[],int width, int ruleSet[]){

  //get the amount of generations the program produces
    int generationNum;
    cout << "Please enter how many generations you would like to simulate" << endl;
    cin >> generationNum;

  for(int times=0; times< generationNum; times++){  

    //loop for applying ruleset to each cell in array

    for(int i=0; i< width; i ++){

        char left = parentArray[i-1];
        char middle = parentArray[i];
        char right = parentArray[i+1];


    // if statement that compares the current cells and its neighbours 
    // with the rules in the ruleset to define the current generation.

        if(left == 'X' && middle == 'X' && right == 'X'){

          if(ruleSet[7] == 1){
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[7];
        }

         else if(left == 'X' && middle == 'X' && middle == '~'){

           if(ruleSet[6] == 1){                 //do this for each rule should work
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[6];
        }

         else if (left == 'X' && middle == '~' && middle == 'X'){

          if(ruleSet[5] == 1){
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[5];
         }

         else if (left == 'X' && middle == '~' && middle == '~'){

          if(ruleSet[4] == 1){
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[4];
         }

         else if (left == '~' && middle == 'X' && middle == 'X'){

          if(ruleSet[3] == 1){
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[3];
         }


         else if (left == '~' && middle == 'X' && middle == '~'){

          if(ruleSet[2] == 1){
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[2];
         }

         else if (left == '~' && middle == '~' && middle == 'x'){

          if(ruleSet[1] == 1){
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[1];
         }

         else if (left == '~' && middle == '~' && middle == '~'){
          childArray[i] = ruleSet[0];
          if(ruleSet[0] == 1){
            childArray[i] = 'X';
          } else {
            childArray[i] = '~';
          }
          childArray[i] = ruleSet[0];
         }

    }



      //for loop that iterates through the array and display all its elements
      for(int i = 0; i < width; i++)
      {

      cout << childArray[i];
      }

      cout<< endl;

      // loop to make the current generation the past generation for the next 
      //iteration of the code

      for(int c=0; c< width; c ++){
        parentArray[c] = childArray[c];
      }

  }
}

Function that uses beginSimulation:

/* Initialize Array */
int initializeArrays(char parentArray[],char childArray[],int width, int ruleSet[]){
  //cout << "Please enter the size of the array" << endl;
  //cin >> width;

  cout << "Please enter the rule you would like to simulate" << endl;
  int userInput = 0; //initialises userInput variable to be passed
  cin >> userInput;     //into the insertItem function    

    for(int x=0; x<width; x++){

      if(x==(width/2)){
        parentArray[(width/2)] = 'X';
        continue;
      }
        cout << "";
        parentArray[x] = '~'; /* or whatever number you want */

    }
    /* parentArray[0..width-1] = "~~...~~X~...~~"
     *                                   ^
     *                                   \- at width/2
     */

    cout << parentArray << endl;

    for(int i=0; i<width; i++){
      childArray[i] = '~'; /* or whatever number you want */
      cout << "";
    }
    /* childArray[0...width - 1] = "~~...~~" */

    cout << childArray << endl;

    /* User input is bit mask to activate rules 0..7
     * e.g. input = 10 = 0x0A = 0b1010 => rule 1 and 3 activated */
    for (int z=7; z>(-1); z --){

      ruleSet[z] = userInput % 2;
      userInput = userInput/2;
     } 



     cout << ruleSet[0] << endl;
     cout << ruleSet[1] << endl;
     cout << ruleSet[2] << endl;
     cout << ruleSet[3] << endl;
     cout << ruleSet[4] << endl;
     cout << ruleSet[5] << endl;
     cout << ruleSet[6] << endl;
     cout << ruleSet[7] << endl;

    beginSimulation(parentArray, childArray, width, ruleSet);

    return 0;

}
FRob
  • 3,883
  • 2
  • 27
  • 40
Adam
  • 1
  • 2
  • 1
    This way too vague: rules applied 'incorrectly'. Try to be very specific in what it should do, what it does do and how that's wrong. Also, you might try stepping through this with a debugger. – MicroVirus Nov 02 '15 at 20:10

2 Answers2

0

First, when you are evaluating cellular automata, you need to handle the edge conditions properly. You are walking off both ends of the arrays into undefined behavior when you write:

char left = parentArray[i-1];
char middle = parentArray[i];
char right = parentArray[i+1];

Because i goes from 0 to width-1. Therefore when i is 0, i-1 will be index -1 into your array and access backward before the beginning of the array. Index i+1 similarly will access forward past the end of the array when i is equal to width-1.

Second, your attempt to use your rules to drive the next value of the cells based on the current values is unconditionally overwritten by the very next statement. You have statements like:

if(ruleSet[7] == 1){
    childArray[i] = 'X';
} else {
    childArray[i] = '~';
}
childArray[i] = ruleSet[7];

It doesn't matter whether ruleSet[7] contains a 1 or not because you immediately overwrite childAray[i] with whatever is in ruleSet[7]. You do this for all your other rule processing. So yes, your rules aren't affecting the result at all.

legalize
  • 2,214
  • 18
  • 25
0

This seems to be a Game-of-Life kind of algorithm/simulation, where the user can decide the width of the game field and which rules are active in order to study the results for a number of generations.

Game field is array[width], initialized to "~" (empty?) with a single "X" (active) at width / 2. Rules in ruleSet will act on this array to produce the next generation after one simulation step.

First off, you obviously did not run this code and edited it for this site, see line #13:

    for(int i=0; i< width; i ++){

        char left = parentArray[i-1]; // <-- What will happen for i == 0?
        char middle = parentArray[i];
        char right = parentArray[i+1];
        ...
    }

You probably have some special condition to handle i == 0, probably

if (i == 0)
  left = '~';

So I'll assume that.

Secondly, you mistyped rule #1 around line #84:

else if (left == '~' && middle == '~' && middle == 'x'){ // <-- small Latin x

I'm not sure what's supposed to happen, since all rules except rule #0 are the same code and all rules do the same thing. So you should not have included so much code in your original post.

Anyway, looking at any rule:

childArray[i] = ruleSet[0]; // this line in ruleSet[0] only
if(ruleSet[0] == 1){
  childArray[i] = 'X';
} else {
  childArray[i] = '~';
}
childArray[i] = ruleSet[0]; // overwrite childArray[i]

Your arrays are sequences of 'X' and '~' -- yet you store integers 0 and 1, which will be characters '\x00' and '\x01' respectively after you're done. Maybe that was just for debugging but you left it in? Or maybe that's the mistake you're after?

Since all if clauses only match combinations of '~' and 'X' (and 'x'), your simulation will stop after the first step and always produce the same result when any rule is turned on.


I suggest you improve your data structures in order to be easier to work with. Rules have two patterns: search, which will be replaced with replace when found.

typedef struct rule {
    char search[3];
    char replace[3];

    rule(char* searchPattern, char* replacePattern) {

        memcpy(&search, searchPattern, sizeof(search));
        memcpy(&replace, replacePattern, sizeof(replace));

    }
} rule_t;

The playing field is a structure that holds its width and the actual data. The custom operator= creates a deep copy of the whole field for us.

typedef struct field {
    size_t width;
    char* data;

    field(size_t field_width) {

        width = field_width;
        data = new char[width + 1];
        memset(data, '~', width);
        data[width] = '\0';
        /* catch out of memory exception here */
    }

    ~field() {

        width = 0;
        delete[] data;

    }

    field& operator=(const field& rhs) {
        /* prevent self-assignment */
        if (this == &rhs)
            return *this;
        this->width = rhs.width;
        delete[] data;
        this->data = new char[width + 1];
        /* catch out of memory exception here */
        memcpy(this->data, rhs.data, width + 1);
        return *this;

    }

} field_t;

The main algorithm

for (int generation = 0; generation < numGenerations; generation++) {

    myFieldAfter = myFieldBefore; /* deep copy of field */

    /* sliding window pointers */
    char *windowBefore = myFieldBefore.data;
    char *windowAfter = myFieldAfter.data;
    /* we have to stop sizeof(rule_t.search) - 1 = 3 - 1 = 2 bytes before end */
    for (size_t x = 0; x < myFieldBefore.width - 2; x++) {

        /* apply rules hierarchically */
        for (auto it = activeRules.begin(); it != activeRules.end(); it++) {

            if (!(memcmp(it->search, windowBefore, sizeof(it->search)))) {
                memcpy(windowAfter, it->replace, sizeof(it->replace));
                break; /* only apply first matching rule */
            }

        }

        /* move windows */
        windowBefore++;
        windowAfter++;

    }

    std::cout << "Generation " << generation << ": " << myFieldBefore.data << "\n";
    myFieldBefore = myFieldAfter; /* deep copy back */

}

I suggest you parse the arguments from command line, for instance a call could look like myCellAutomaton 32 5 "1,3" to get a field of width 32, simulate 5 generations and use rules #1 and #3. Take a look at Boost.Program_options. A rough sketch of the final program looks like this:

#include <iostream>
#include <string>
#include <cstring>
#include <vector>

/* definitions from above */

int main(int argc, char** argv) {

    int numGenerations;
    int width;
    std::vector<rule_t> activeRules;

    /* parse command line arguments */
    numGenerations = 5;
    width = 32;

    /* example rule */
    rule_t myRule("X~~", "XX~");
    /* vector of active rules */
    activeRules.push_back(myRule);

    field_t myFieldBefore(width);
    myFieldBefore.data[width / 2] = 'X';
    field_t myFieldAfter(width);

    /* algorithm here */

}

Output is as follows:

Generation 0: ~~~~~~~~~~~~~~~~X~~~~~~~~~~~~~~~
Generation 1: ~~~~~~~~~~~~~~~~XX~~~~~~~~~~~~~~
Generation 2: ~~~~~~~~~~~~~~~~XXX~~~~~~~~~~~~~
Generation 3: ~~~~~~~~~~~~~~~~XXXX~~~~~~~~~~~~
Generation 4: ~~~~~~~~~~~~~~~~XXXXX~~~~~~~~~~~

I hope this answer gave you a rough idea of what to do. I used simple structs and standard calls to keep it simple, though that makes the whole program be C-ish rather than pure C++.

FRob
  • 3,883
  • 2
  • 27
  • 40