Game of Life

This program shows the well known Game of Life. I’ve chosen this simulation as an exercise to explore:

  • Member function pointers as argument
  • Problem domain slicing
  • concurrency, using all available cpu cores, without creating and destroying thread objects after each iteration.
Game of Life
Game of Life

(Member) function pointers as argument

When using function pointers as argument, where the function is a member function of a class, you also need to parse a pointer of the instance of the object. To illustrate the difference between C style with an ordinary function pointer and C++ style with a member function pointer as an argument, have a look on the example below:

int Foo::add(int i, int j) {
    return i + j;
}

// C style
void Foo::printOutputCFunction(double (*fn)(double), double x) {
    double y = fn(x);
    std::cout << std::to_string(y) << std::endl; } 

// C++ style
void Foo::printOutputCppMemberFunction(int (Foo::*fn)(int, int), Foo* foo, int i, int j) { 
    int k = (foo->*fn)(i, j);
    std::cout << k << std::endl;
}

void Foo::printOutputCppMemberFunctionSelf(int i, int j) {
    printOutputCppMemberFunction(&Foo::add, this, i, j);
}

This Game of Life simulation uses one (void)function for domain slicing and distributing the work load over the available CPU cores by starting an equal amount of threads, for the computation of initial conditions and time stepping.

void startThreads(void (GameOfLifeKernel::*fn)(int, int), GameOfLifeKernel *kernel);

It takes a member function pointer as an argument. The function itself is then called with:

// compute inner domain
startThreads(&GameOfLifeKernel::timeStepInnerDomainSlice, this);

for computing the inner domain and similar for setting the initial conditions. The reference to the object itself is needed, because the function is non-static.

Domain slicing

The Game of Life simulation can be distributed in many different ways. Since it is a cellular automata, every cell is updated from the current state to the new state fully independently. It only needs the states from its direct neighbours. This code used the domain slicing approach, which can easily be applied to other tasks, like image processing. The domain is divided horizontally in row batches. Each slice is then sent to another thread where its new state is computed. After the computation, the threads are joined and the new state is set as the current state (swap buffers). To prevent excessive memory allocation and destruction for each time step, only the memory range of the slice is given to the threads. All threads share the same memory block. This is fine, since they read the current state from buffer 1 and update only their own part in buffer 2.

Compile

You can find the source code here in my repository at Bitbucket. I used Xcode 7 on Mac OS and it needs the OpenFrameworks library, used for the graphical output. To compile the source code yourself, copy the source code in the {OF_PATH}/apps directory and open the project in XCode.

Download

Click on the icon to download the application and give it a try… You can create new life by clicking and dragging the mouse.

Download Game of Life