73465ace35a83efec10235c119800304

This is an implementation of "Conway's Game of Life" [http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life]. Please comment on the design along with suggestions to improve it. Also please suggest ways that I can make it more efficient, both memory and speed wise.
Thanks.

#include <fstream>
using std::ofstream;
#include <time.h>

int const numOfStates = 2;
ofstream outFile("c:\\output.txt");
class UniverseOfLife
{

public: //methods

	UniverseOfLife(int population = 9) : population_(population)
	{
		srand((unsigned int)time(NULL));
		generateUniverse();
		outFile << "********* Initial State *********" << endl << endl;
		updateView();
		outFile << endl << "*********************************" << endl << endl;

	}

	~UniverseOfLife()
	{
		for (int i = 0; i < population_; ++i)
		{
			delete [] universe_[i];
			delete [] parallelUniverse_[i];
		}
		delete [] universe_;
		delete [] parallelUniverse_;
	}

	void operator()()
	{
		for (int i = 0; i < population_; ++i)
		{
			for (int j = 0; j < population_; ++j)
			{
				int state = universe_[i][j];
				int liveNeighbours = getLiveNeighbours(i, j);
				changeCellState(liveNeighbours, i, j);
			}
		}
		updateUniverse();
		updateView();
		outFile << endl << "*********************************" << endl << endl;
	}

private: //methods

	void generateUniverse()
	{
		universe_ = new int*[population_];
		parallelUniverse_ = new int*[population_];
		for (int i = 0; i < population_; ++i)
		{
			universe_[i] = new int[population_];
			parallelUniverse_[i] = new int[population_];
			for (int j = 0; j < population_; ++j)
			{
				universe_[i][j]  = rand() % numOfStates;
				parallelUniverse_[i][j] = universe_[i][j];
			}
		}
	}

	int getLiveNeighbours(int i, int j)
	{
		int liveNeighbours = 0;
		bool isFirstRow = !(i - 1 >= 0);
		bool isFirstCol = !(j - 1 >= 0);
		bool isLastRow = !(i + 1 < population_);
		bool isLastCol = !(j + 1 < population_);

		if(!isFirstRow)
		{
			if (!isFirstCol && universe_[i - 1][j - 1] == 1)
			{
				liveNeighbours++;
			}
			if (universe_[i - 1][j] == 1)
			{
				liveNeighbours++;
			}
			if (!isLastCol && universe_[i - 1][j + 1] == 1)
			{
				liveNeighbours++;
			}
		}

		if (!isFirstCol && universe_[i][j - 1] == 1)
		{
			liveNeighbours++;
		}
		if (!isLastCol && universe_[i][j + 1] == 1)
		{
			liveNeighbours++;
		}

		if(!isLastRow)
		{
			if (!isFirstCol && universe_[i + 1][j - 1] == 1)
			{
				liveNeighbours++;
			}
			if (universe_[i + 1][j] == 1)
			{
				liveNeighbours++;
			}
			if (!isLastCol && universe_[i + 1][j + 1] == 1)
			{
				liveNeighbours++;
			}
		}

		return liveNeighbours;
	}

	void changeCellState(int liveNeighbours, int i, int j)
	{
		if (universe_[i][j] == 1 && liveNeighbours < 2)
		{
			parallelUniverse_[i][j] = 0;
		}
		else if (universe_[i][j] == 1 && liveNeighbours > 3)
		{
			parallelUniverse_[i][j] = 0;
		}
		else if (universe_[i][j] == 0 && liveNeighbours == 3)
		{
			parallelUniverse_[i][j] = 1;
		}
	}

	void updateUniverse()
	{
		for (int i = 0; i < population_; ++i)
		{
			for (int j = 0; j < population_; ++j)
			{
				universe_[i][j] = parallelUniverse_[i][j];
			}
		}
	}
	void updateView()
	{
		for (int i = 0; i < population_; ++i)
		{
			for (int j = 0; j < population_; ++j)
			{
				outFile << universe_[i][j];
			}
			outFile << endl;
		}
	}

public: // member variables

private: // member variables

	int const population_;
	int **universe_;
	int **parallelUniverse_;

};

class Ticker
{

public:

	Ticker(UniverseOfLife &uol, int sleepIntervallMilliSecs = 1) : uol_(uol), sleepIntervallMilliSecs_(sleepIntervallMilliSecs)
	{
	}

	void start()
	{
		while(true)
		{
			uol_();
			//A poartable way to sleep needs to be added from Boost
			//Sleep(sleepIntervallMilliSecs_);
		}
	}

private:

	UniverseOfLife &uol_;
	int sleepIntervallMilliSecs_;
};

class GameOfLife
{

public: //methods

	GameOfLife(int population = 9, int sleepIntervallMilliSecs = 1) : uol_(population), ticker_(uol_, sleepIntervallMilliSecs)
	{
	}

	void start()
	{
		ticker_.start();
	}

private: //methods

public: // member variables

private: // member variables

	UniverseOfLife uol_;
	Ticker ticker_;
};
int main(int argc, char *argv[])
{
	GameOfLife gol(5);
	gol.start();
	return 0;
}

Refactorings

No refactoring yet !

D41d8cd98f00b204e9800998ecf8427e

bob

January 22, 2010, January 22, 2010 09:02, permalink

No rating. Login to rate!

a couple of comments:
* you should include <ctime> instead of <time.h>, and then use std::time
* you need to include <cstdlib> for rand and srand, and use std::rand, std::srand
* you need to include <iostream> for endl, and use std::endl
* srand should be called once in main() instead of every time a UniverseOfLife object is created
* all your methods that take more than one or two lines should be implemented outside of the class definition (usually in a separate file, because the class is usually defined in a header file, while its implementation is in a source file), because by default all methods included in the class definition are inlined, which generates lots of duplicate code for large functions that are reused. Also, if you include it, compiling any file that includes the class definition, will also need to read in all this extra code, making it slower.
* getLiveNeighbours() should be declared const, since it does not affect the state

F9a9ba6663645458aa8630157ed5e71e

Ants

January 23, 2010, January 23, 2010 20:02, permalink

No rating. Login to rate!

Hopefully this is a bit more readable.

#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <ctime>
#include <cstdlib>
#include <vector>
using namespace std;

enum CellState
{
    cellstateDead = 0,
    cellstateAlive = 1,
    countOfCellStates,
};

class Universe
{
public:
    Universe(size_t width, size_t height)
        : _cells(width + 2, vector<CellState>(height + 2))
    {
        clear();
    }

    void clear()
    {
        for(vector<vector<CellState>>::iterator row = _cells.begin(); row != _cells.end(); ++row)
        {
            for(vector<CellState>::iterator col = row->begin(); col != row->end(); ++col)
                *col = cellstateDead;
        }
    }

    CellState get(size_t x, size_t y) const
    {
        return _cells[x + 1][y + 1];
    }

    void set(size_t x, size_t y, CellState state)
    {
        _cells[x + 1][y + 1] = state;
    }

    unsigned int countNeighbors(size_t x, size_t y,
                                CellState state = cellstateAlive) const
    {
        unsigned int count = 0;

        for(int row = int(x) - 1; row <= int(x) + 1; ++row)
        {
            for(int col = int(y) - 1; col <= int(y) + 1; ++col)
            {
                if (row == x && col == y)
                    continue;
                if (_cells[row + 1][col + 1] == state)
                    ++count;
            }
        }
        return count;
    }

    size_t getWidth() const
    {
        return _cells.size() - 2;
    }

    size_t getHeight() const
    {
        return _cells[0].size() - 2;
    }

private:
    vector<vector<CellState>>   _cells;
};

class Ticker
{
public:
    Ticker(int sleepIntervallMilliSecs = 1)
        : _sleepIntervallMilliSecs(sleepIntervallMilliSecs)
    {
    }

    template<class Functor>
    void start(Functor & func)
    {
        while(true)
        {
            func();
            //A portable way to sleep needs to be added from Boost
            //Sleep(_sleepIntervallMilliSecs);
        }
    }

private:
    int _sleepIntervallMilliSecs;
};

class GameOfLife
{
public: //methods
    GameOfLife(ostream & outFile, int population = 9, int sleepIntervallMilliSecs = 1)
        : _outFile(outFile)
        , _ticker(sleepIntervallMilliSecs)
        , _active(new Universe(population, population))
        , _next(new Universe(population, population))
    {
    }

    void init()
    {
        srand(static_cast<unsigned int>(time(NULL)));
        generateUniverse();
        _outFile << "********* Initial State *********" << endl << endl;
        outputView();
    }

    void start()
    {
        _ticker.start(*this);
    }

    void operator()()
    {
        updateUniverse();
        outputView();
    }

private:
    ostream &           _outFile;
    Ticker              _ticker;
    auto_ptr<Universe>  _active;
    auto_ptr<Universe>  _next;

    void generateUniverse()
    {
        for (size_t i = 0; i < _active->getWidth(); ++i)
        {
            for (size_t j = 0; j < _active->getHeight(); ++j)
                _active->set(i, j, static_cast<CellState> (rand() % countOfCellStates));
        }
    }

    void outputView() const
    {
        for (size_t i = 0; i < _active->getWidth(); ++i)
        {
            for (size_t j = 0; j < _active->getHeight(); ++j)
                _outFile << ((_active->get(i, j) == cellstateAlive) ? 'A' : ' ');
            _outFile << endl;
        }
        _outFile << endl << "*********************************" << endl << endl;
    }

    CellState getNextCellState(CellState state, int count)
    {
        if (state == cellstateAlive)
        {
            if (count < 2 || count > 3)
                state = cellstateDead;
        }
        else if (count == 3)
        {
            state = cellstateAlive;
        }
        return state;
    }

    void updateUniverse()
    {
        for (size_t i = 0; i < _active->getWidth(); ++i)
        {
            for (size_t j = 0; j < _active->getHeight(); ++j)
            {
                _next->set(i, j, getNextCellState(_active->get(i, j),
                                                  _active->countNeighbors(i, j)));
            }
        }
        swap(_active, _next);
    }
};

int main(int argc, char *argv[])
{
    ofstream outFile("c:\\output.txt");
    GameOfLife gol(outFile, 5);

    gol.init();
    gol.start();
    return 0;
}
F9a9ba6663645458aa8630157ed5e71e

Ants

January 23, 2010, January 23, 2010 21:53, permalink

No rating. Login to rate!

Or if you don't want the overhead of the guard cells, and make Universe class purer by making it only take care of the cells and not know anything about counting neighbors...

#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <ctime>
#include <cstdlib>
#include <vector>
using namespace std;

enum CellState
{
    cellstateDead = 0,
    cellstateAlive = 1,
    countOfCellStates,
};

class Universe
{
public:
    Universe(size_t width, size_t height)
        : _cells(width, vector<CellState>(height))
    {
    }

    CellState & at(size_t x, size_t y)
    {
        return _cells[x][y];
    }

    size_t getWidth() const
    {
        return _cells.size();
    }

    size_t getHeight() const
    {
        return _cells[0].size();
    }

private:
    vector<vector<CellState>>   _cells;
};

class Ticker
{
public:
    Ticker(int sleepIntervallMilliSecs = 1)
        : _sleepIntervallMilliSecs(sleepIntervallMilliSecs)
    {
    }

    template<class Functor>
    void start(Functor & func)
    {
        while(true)
        {
            func();
            //A portable way to sleep needs to be added from Boost
            //Sleep(_sleepIntervallMilliSecs);
        }
    }

private:
    int _sleepIntervallMilliSecs;
};

class GameOfLife
{
public: //methods
    GameOfLife(ostream & outFile, int population = 9, int sleepIntervallMilliSecs = 1)
        : _outFile(outFile)
        , _ticker(sleepIntervallMilliSecs)
        , _active(new Universe(population, population))
        , _next(new Universe(population, population))
    {
    }

    void init()
    {
        srand(static_cast<unsigned int>(time(NULL)));
        generateUniverse();
        _outFile << "********* Initial State *********" << endl << endl;
        outputView();
    }

    void start()
    {
        _ticker.start(*this);
    }

    void operator()()
    {
        updateUniverse();
        outputView();
    }

private:
    ostream &           _outFile;
    Ticker              _ticker;
    auto_ptr<Universe>  _active;
    auto_ptr<Universe>  _next;

    void generateUniverse()
    {
        for (size_t i = 0; i < _active->getWidth(); ++i)
        {
            for (size_t j = 0; j < _active->getHeight(); ++j)
                _active->at(i, j) = static_cast<CellState> (rand() % countOfCellStates);
        }
    }

    void outputView() const
    {
        for (size_t i = 0; i < _active->getWidth(); ++i)
        {
            for (size_t j = 0; j < _active->getHeight(); ++j)
                _outFile << ((_active->at(i, j) == cellstateAlive) ? 'A' : ' ');
            _outFile << endl;
        }
        _outFile << endl << "*********************************" << endl << endl;
    }

    unsigned int countNeighbors(size_t x, size_t y,
                                CellState state = cellstateAlive) const
    {
        const int width = _active->getWidth();
        const int height = _active->getHeight();
        unsigned int count = 0;

        for(int i = max(int(x) - 1, 0);
            i <= min(int(x) + 1, width - 1);
            ++i)
        {
            for(int j = max(int(y) - 1, 0);
                j <= min(int(y) + 1, height - 1);
                ++j)
            {
                if (i == x && j == y)
                    continue;
                if (_active->at(i, j) == state)
                    ++count;
            }
        }
        return count;
    }

    CellState getNextCellState(CellState state, int count)
    {
        if (state == cellstateAlive)
        {
            if (count < 2 || count > 3)
                state = cellstateDead;
        }
        else if (count == 3)
        {
            state = cellstateAlive;
        }
        return state;
    }

    void updateUniverse()
    {
        for (size_t i = 0; i < _active->getWidth(); ++i)
        {
            for (size_t j = 0; j < _active->getHeight(); ++j)
            {
                _next->at(i, j) = getNextCellState(_active->at(i, j),
                                                   countNeighbors(i, j));
            }
        }
        swap(_active, _next);
    }
};

int main(int argc, char *argv[])
{
    ofstream outFile("c:\\output.txt");
    GameOfLife gol(cout, 5);

    gol.init();
    gol.start();
    return 0;
}

Your refactoring





Format Copy from initial code

or Cancel