Simple implementation of Conway's game of life with SFML

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
6
down vote

favorite
2












This is a very simple version of Conway's game of life. It treats the edge of the grid as dead cells and uses a very basic algorithm. Despite this it works fine.

As people might not have the SFML library installed here's a picture of what it looks like when running: Game of Life



It can be paused with p and updated one step at a time with u (only while paused). Reset is possible by pressing r.



Testing has shown that the performance bottleneck is the drawing itself. I'm using slightly modified code from an SFML example for this. I would appreciate input on this issue from someone with experience in rendering as I'm new to this.

I'm also concerned with the coupling between the Gol class and the render engine. Any ideas on how to decouple this are very welcome.



gol.h:



#include <SFML/Graphics.hpp>

#include <random>
#include <vector>

class Gol
public:
Gol(int width_, int height_);

void display_grid(sf::RenderWindow& window) const;
void update();
void reset();

private:
void init_visuals();
void seed_grid();
int count_neighbors(int m_origin, int n_origin) const;
void apply_rules(int neighbors, int m, int n);

int width;
int height;
std::vector<std::vector<bool>> grid_current;
std::vector<std::vector<bool>> grid_next;
std::vector<sf::Vertex> grid_visual;
std::mt19937 rng;
sf::Color visual_alive = sf::Color::White;
sf::Color visual_dead = sf::Color::Black;
;


gol.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <cstddef>
#include <ctime>
#include <random>
#include <vector>

Gol::Gol(int width_, int height_)
: widthwidth_
, heightheight_
, grid_current
, grid_next
, grid_visual
, rng

std::random_device rd;
rng.seed(rd());

reset();


void Gol::display_grid(sf::RenderWindow& window) const
window.draw(&grid_visual[0], grid_visual.size(), sf::Points);


void Gol::update()
grid_current.swap(grid_next);
for (int m = 0; m < height; ++m)
for (int n = 0; n < width; ++n)
apply_rules(count_neighbors(m, n), m, n);




void Gol::reset()
grid_current.clear();
grid_current.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
grid_next.clear();
grid_next.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
init_visuals();
seed_grid();


void Gol::init_visuals()
grid_visual.clear();
grid_visual.resize(width * height);

int pos = 0;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
grid_visual[pos].position =
sf::Vector2f(static_cast<float>(x), static_cast<float>(y));
// postfix intended
grid_visual[pos++].color = visual_dead;




void Gol::seed_grid()
std::uniform_int_distribution<int> m_axis(0, height - 1);
std::uniform_int_distribution<int> n_axis(0, width - 1);
std::uniform_int_distribution<int> cells(width * height / 2,
width * height);

int initial_cell_nr = cells(rng);
while (initial_cell_nr > 0)
std::size_t m = m_axis(rng);
std::size_t n = n_axis(rng);
if (!grid_next[m][n])
grid_next[m][n] = true;
grid_visual[m * width + n].color = visual_alive;
--initial_cell_nr;




int Gol::count_neighbors(int m_origin, int n_origin) const
int neighbors = 0;
for (int m_current = m_origin - 1; m_current <= m_origin + 1; ++m_current)
for (int n_current = n_origin - 1; n_current <= n_origin + 1; ++n_current)
if (m_current < 0

return neighbors;


void Gol::apply_rules(int neighbors, int m, int n)
grid_next[m][n] = grid_current[m][n];

// Any live cell with fewer than two live neighbors dies,
// as if caused by under population.
if (grid_current[m][n] && neighbors < 2)
grid_next[m][n] = false;
grid_visual[m * width + n].color = visual_dead;


// Any live cell with two or three live neighbors lives on
if (grid_current[m][n] && (neighbors == 2


main.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <string>

int main()
constexpr int width = 1440;
constexpr int height = 900;
Gol golwidth, height;

sf::RenderWindow window(sf::VideoMode(width, height), "0",
sf::Style::Titlebar






share|improve this question



















  • Make the grid an sf::Drawable and use an sf::VertexArray not a std::vector<sf::Vertex> as I'm pretty sure the vector won't be stored in the video memory. Not certain however.
    – pointerless
    May 3 at 10:35










  • @pointerless The linked tutorial explicitly mentions that a sf::VertexArray is just a std::vector<sf::Vertex> so it should make no difference unless it changed in recent versions.
    – yuri
    May 3 at 10:38










  • A very good point. Still make it a drawable and provide the draw method, it may not improve performance but it is more logical. Also what is your compile command? Maybe add some optimiser flags?
    – pointerless
    May 3 at 11:10











  • @pointerless I always compile with -O2 in this case I also tried -O3 and -Ofast neither of which changed the rather high CPU usage.
    – yuri
    May 3 at 11:37










  • I would rather simulate the game in a separate thread, store the results (or changes) in a container and use sfml for playback. This would also allow you to go to previous steps.
    – Timo
    May 3 at 11:48
















up vote
6
down vote

favorite
2












This is a very simple version of Conway's game of life. It treats the edge of the grid as dead cells and uses a very basic algorithm. Despite this it works fine.

As people might not have the SFML library installed here's a picture of what it looks like when running: Game of Life



It can be paused with p and updated one step at a time with u (only while paused). Reset is possible by pressing r.



Testing has shown that the performance bottleneck is the drawing itself. I'm using slightly modified code from an SFML example for this. I would appreciate input on this issue from someone with experience in rendering as I'm new to this.

I'm also concerned with the coupling between the Gol class and the render engine. Any ideas on how to decouple this are very welcome.



gol.h:



#include <SFML/Graphics.hpp>

#include <random>
#include <vector>

class Gol
public:
Gol(int width_, int height_);

void display_grid(sf::RenderWindow& window) const;
void update();
void reset();

private:
void init_visuals();
void seed_grid();
int count_neighbors(int m_origin, int n_origin) const;
void apply_rules(int neighbors, int m, int n);

int width;
int height;
std::vector<std::vector<bool>> grid_current;
std::vector<std::vector<bool>> grid_next;
std::vector<sf::Vertex> grid_visual;
std::mt19937 rng;
sf::Color visual_alive = sf::Color::White;
sf::Color visual_dead = sf::Color::Black;
;


gol.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <cstddef>
#include <ctime>
#include <random>
#include <vector>

Gol::Gol(int width_, int height_)
: widthwidth_
, heightheight_
, grid_current
, grid_next
, grid_visual
, rng

std::random_device rd;
rng.seed(rd());

reset();


void Gol::display_grid(sf::RenderWindow& window) const
window.draw(&grid_visual[0], grid_visual.size(), sf::Points);


void Gol::update()
grid_current.swap(grid_next);
for (int m = 0; m < height; ++m)
for (int n = 0; n < width; ++n)
apply_rules(count_neighbors(m, n), m, n);




void Gol::reset()
grid_current.clear();
grid_current.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
grid_next.clear();
grid_next.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
init_visuals();
seed_grid();


void Gol::init_visuals()
grid_visual.clear();
grid_visual.resize(width * height);

int pos = 0;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
grid_visual[pos].position =
sf::Vector2f(static_cast<float>(x), static_cast<float>(y));
// postfix intended
grid_visual[pos++].color = visual_dead;




void Gol::seed_grid()
std::uniform_int_distribution<int> m_axis(0, height - 1);
std::uniform_int_distribution<int> n_axis(0, width - 1);
std::uniform_int_distribution<int> cells(width * height / 2,
width * height);

int initial_cell_nr = cells(rng);
while (initial_cell_nr > 0)
std::size_t m = m_axis(rng);
std::size_t n = n_axis(rng);
if (!grid_next[m][n])
grid_next[m][n] = true;
grid_visual[m * width + n].color = visual_alive;
--initial_cell_nr;




int Gol::count_neighbors(int m_origin, int n_origin) const
int neighbors = 0;
for (int m_current = m_origin - 1; m_current <= m_origin + 1; ++m_current)
for (int n_current = n_origin - 1; n_current <= n_origin + 1; ++n_current)
if (m_current < 0

return neighbors;


void Gol::apply_rules(int neighbors, int m, int n)
grid_next[m][n] = grid_current[m][n];

// Any live cell with fewer than two live neighbors dies,
// as if caused by under population.
if (grid_current[m][n] && neighbors < 2)
grid_next[m][n] = false;
grid_visual[m * width + n].color = visual_dead;


// Any live cell with two or three live neighbors lives on
if (grid_current[m][n] && (neighbors == 2


main.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <string>

int main()
constexpr int width = 1440;
constexpr int height = 900;
Gol golwidth, height;

sf::RenderWindow window(sf::VideoMode(width, height), "0",
sf::Style::Titlebar






share|improve this question



















  • Make the grid an sf::Drawable and use an sf::VertexArray not a std::vector<sf::Vertex> as I'm pretty sure the vector won't be stored in the video memory. Not certain however.
    – pointerless
    May 3 at 10:35










  • @pointerless The linked tutorial explicitly mentions that a sf::VertexArray is just a std::vector<sf::Vertex> so it should make no difference unless it changed in recent versions.
    – yuri
    May 3 at 10:38










  • A very good point. Still make it a drawable and provide the draw method, it may not improve performance but it is more logical. Also what is your compile command? Maybe add some optimiser flags?
    – pointerless
    May 3 at 11:10











  • @pointerless I always compile with -O2 in this case I also tried -O3 and -Ofast neither of which changed the rather high CPU usage.
    – yuri
    May 3 at 11:37










  • I would rather simulate the game in a separate thread, store the results (or changes) in a container and use sfml for playback. This would also allow you to go to previous steps.
    – Timo
    May 3 at 11:48












up vote
6
down vote

favorite
2









up vote
6
down vote

favorite
2






2





This is a very simple version of Conway's game of life. It treats the edge of the grid as dead cells and uses a very basic algorithm. Despite this it works fine.

As people might not have the SFML library installed here's a picture of what it looks like when running: Game of Life



It can be paused with p and updated one step at a time with u (only while paused). Reset is possible by pressing r.



Testing has shown that the performance bottleneck is the drawing itself. I'm using slightly modified code from an SFML example for this. I would appreciate input on this issue from someone with experience in rendering as I'm new to this.

I'm also concerned with the coupling between the Gol class and the render engine. Any ideas on how to decouple this are very welcome.



gol.h:



#include <SFML/Graphics.hpp>

#include <random>
#include <vector>

class Gol
public:
Gol(int width_, int height_);

void display_grid(sf::RenderWindow& window) const;
void update();
void reset();

private:
void init_visuals();
void seed_grid();
int count_neighbors(int m_origin, int n_origin) const;
void apply_rules(int neighbors, int m, int n);

int width;
int height;
std::vector<std::vector<bool>> grid_current;
std::vector<std::vector<bool>> grid_next;
std::vector<sf::Vertex> grid_visual;
std::mt19937 rng;
sf::Color visual_alive = sf::Color::White;
sf::Color visual_dead = sf::Color::Black;
;


gol.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <cstddef>
#include <ctime>
#include <random>
#include <vector>

Gol::Gol(int width_, int height_)
: widthwidth_
, heightheight_
, grid_current
, grid_next
, grid_visual
, rng

std::random_device rd;
rng.seed(rd());

reset();


void Gol::display_grid(sf::RenderWindow& window) const
window.draw(&grid_visual[0], grid_visual.size(), sf::Points);


void Gol::update()
grid_current.swap(grid_next);
for (int m = 0; m < height; ++m)
for (int n = 0; n < width; ++n)
apply_rules(count_neighbors(m, n), m, n);




void Gol::reset()
grid_current.clear();
grid_current.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
grid_next.clear();
grid_next.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
init_visuals();
seed_grid();


void Gol::init_visuals()
grid_visual.clear();
grid_visual.resize(width * height);

int pos = 0;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
grid_visual[pos].position =
sf::Vector2f(static_cast<float>(x), static_cast<float>(y));
// postfix intended
grid_visual[pos++].color = visual_dead;




void Gol::seed_grid()
std::uniform_int_distribution<int> m_axis(0, height - 1);
std::uniform_int_distribution<int> n_axis(0, width - 1);
std::uniform_int_distribution<int> cells(width * height / 2,
width * height);

int initial_cell_nr = cells(rng);
while (initial_cell_nr > 0)
std::size_t m = m_axis(rng);
std::size_t n = n_axis(rng);
if (!grid_next[m][n])
grid_next[m][n] = true;
grid_visual[m * width + n].color = visual_alive;
--initial_cell_nr;




int Gol::count_neighbors(int m_origin, int n_origin) const
int neighbors = 0;
for (int m_current = m_origin - 1; m_current <= m_origin + 1; ++m_current)
for (int n_current = n_origin - 1; n_current <= n_origin + 1; ++n_current)
if (m_current < 0

return neighbors;


void Gol::apply_rules(int neighbors, int m, int n)
grid_next[m][n] = grid_current[m][n];

// Any live cell with fewer than two live neighbors dies,
// as if caused by under population.
if (grid_current[m][n] && neighbors < 2)
grid_next[m][n] = false;
grid_visual[m * width + n].color = visual_dead;


// Any live cell with two or three live neighbors lives on
if (grid_current[m][n] && (neighbors == 2


main.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <string>

int main()
constexpr int width = 1440;
constexpr int height = 900;
Gol golwidth, height;

sf::RenderWindow window(sf::VideoMode(width, height), "0",
sf::Style::Titlebar






share|improve this question











This is a very simple version of Conway's game of life. It treats the edge of the grid as dead cells and uses a very basic algorithm. Despite this it works fine.

As people might not have the SFML library installed here's a picture of what it looks like when running: Game of Life



It can be paused with p and updated one step at a time with u (only while paused). Reset is possible by pressing r.



Testing has shown that the performance bottleneck is the drawing itself. I'm using slightly modified code from an SFML example for this. I would appreciate input on this issue from someone with experience in rendering as I'm new to this.

I'm also concerned with the coupling between the Gol class and the render engine. Any ideas on how to decouple this are very welcome.



gol.h:



#include <SFML/Graphics.hpp>

#include <random>
#include <vector>

class Gol
public:
Gol(int width_, int height_);

void display_grid(sf::RenderWindow& window) const;
void update();
void reset();

private:
void init_visuals();
void seed_grid();
int count_neighbors(int m_origin, int n_origin) const;
void apply_rules(int neighbors, int m, int n);

int width;
int height;
std::vector<std::vector<bool>> grid_current;
std::vector<std::vector<bool>> grid_next;
std::vector<sf::Vertex> grid_visual;
std::mt19937 rng;
sf::Color visual_alive = sf::Color::White;
sf::Color visual_dead = sf::Color::Black;
;


gol.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <cstddef>
#include <ctime>
#include <random>
#include <vector>

Gol::Gol(int width_, int height_)
: widthwidth_
, heightheight_
, grid_current
, grid_next
, grid_visual
, rng

std::random_device rd;
rng.seed(rd());

reset();


void Gol::display_grid(sf::RenderWindow& window) const
window.draw(&grid_visual[0], grid_visual.size(), sf::Points);


void Gol::update()
grid_current.swap(grid_next);
for (int m = 0; m < height; ++m)
for (int n = 0; n < width; ++n)
apply_rules(count_neighbors(m, n), m, n);




void Gol::reset()
grid_current.clear();
grid_current.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
grid_next.clear();
grid_next.resize(static_cast<std::size_t>(height),
std::vector<bool>(width));
init_visuals();
seed_grid();


void Gol::init_visuals()
grid_visual.clear();
grid_visual.resize(width * height);

int pos = 0;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
grid_visual[pos].position =
sf::Vector2f(static_cast<float>(x), static_cast<float>(y));
// postfix intended
grid_visual[pos++].color = visual_dead;




void Gol::seed_grid()
std::uniform_int_distribution<int> m_axis(0, height - 1);
std::uniform_int_distribution<int> n_axis(0, width - 1);
std::uniform_int_distribution<int> cells(width * height / 2,
width * height);

int initial_cell_nr = cells(rng);
while (initial_cell_nr > 0)
std::size_t m = m_axis(rng);
std::size_t n = n_axis(rng);
if (!grid_next[m][n])
grid_next[m][n] = true;
grid_visual[m * width + n].color = visual_alive;
--initial_cell_nr;




int Gol::count_neighbors(int m_origin, int n_origin) const
int neighbors = 0;
for (int m_current = m_origin - 1; m_current <= m_origin + 1; ++m_current)
for (int n_current = n_origin - 1; n_current <= n_origin + 1; ++n_current)
if (m_current < 0

return neighbors;


void Gol::apply_rules(int neighbors, int m, int n)
grid_next[m][n] = grid_current[m][n];

// Any live cell with fewer than two live neighbors dies,
// as if caused by under population.
if (grid_current[m][n] && neighbors < 2)
grid_next[m][n] = false;
grid_visual[m * width + n].color = visual_dead;


// Any live cell with two or three live neighbors lives on
if (grid_current[m][n] && (neighbors == 2


main.cpp:



#include "gol.h"

#include <SFML/Graphics.hpp>

#include <string>

int main()
constexpr int width = 1440;
constexpr int height = 900;
Gol golwidth, height;

sf::RenderWindow window(sf::VideoMode(width, height), "0",
sf::Style::Titlebar








share|improve this question










share|improve this question




share|improve this question









asked May 3 at 9:37









yuri

3,3852832




3,3852832











  • Make the grid an sf::Drawable and use an sf::VertexArray not a std::vector<sf::Vertex> as I'm pretty sure the vector won't be stored in the video memory. Not certain however.
    – pointerless
    May 3 at 10:35










  • @pointerless The linked tutorial explicitly mentions that a sf::VertexArray is just a std::vector<sf::Vertex> so it should make no difference unless it changed in recent versions.
    – yuri
    May 3 at 10:38










  • A very good point. Still make it a drawable and provide the draw method, it may not improve performance but it is more logical. Also what is your compile command? Maybe add some optimiser flags?
    – pointerless
    May 3 at 11:10











  • @pointerless I always compile with -O2 in this case I also tried -O3 and -Ofast neither of which changed the rather high CPU usage.
    – yuri
    May 3 at 11:37










  • I would rather simulate the game in a separate thread, store the results (or changes) in a container and use sfml for playback. This would also allow you to go to previous steps.
    – Timo
    May 3 at 11:48
















  • Make the grid an sf::Drawable and use an sf::VertexArray not a std::vector<sf::Vertex> as I'm pretty sure the vector won't be stored in the video memory. Not certain however.
    – pointerless
    May 3 at 10:35










  • @pointerless The linked tutorial explicitly mentions that a sf::VertexArray is just a std::vector<sf::Vertex> so it should make no difference unless it changed in recent versions.
    – yuri
    May 3 at 10:38










  • A very good point. Still make it a drawable and provide the draw method, it may not improve performance but it is more logical. Also what is your compile command? Maybe add some optimiser flags?
    – pointerless
    May 3 at 11:10











  • @pointerless I always compile with -O2 in this case I also tried -O3 and -Ofast neither of which changed the rather high CPU usage.
    – yuri
    May 3 at 11:37










  • I would rather simulate the game in a separate thread, store the results (or changes) in a container and use sfml for playback. This would also allow you to go to previous steps.
    – Timo
    May 3 at 11:48















Make the grid an sf::Drawable and use an sf::VertexArray not a std::vector<sf::Vertex> as I'm pretty sure the vector won't be stored in the video memory. Not certain however.
– pointerless
May 3 at 10:35




Make the grid an sf::Drawable and use an sf::VertexArray not a std::vector<sf::Vertex> as I'm pretty sure the vector won't be stored in the video memory. Not certain however.
– pointerless
May 3 at 10:35












@pointerless The linked tutorial explicitly mentions that a sf::VertexArray is just a std::vector<sf::Vertex> so it should make no difference unless it changed in recent versions.
– yuri
May 3 at 10:38




@pointerless The linked tutorial explicitly mentions that a sf::VertexArray is just a std::vector<sf::Vertex> so it should make no difference unless it changed in recent versions.
– yuri
May 3 at 10:38












A very good point. Still make it a drawable and provide the draw method, it may not improve performance but it is more logical. Also what is your compile command? Maybe add some optimiser flags?
– pointerless
May 3 at 11:10





A very good point. Still make it a drawable and provide the draw method, it may not improve performance but it is more logical. Also what is your compile command? Maybe add some optimiser flags?
– pointerless
May 3 at 11:10













@pointerless I always compile with -O2 in this case I also tried -O3 and -Ofast neither of which changed the rather high CPU usage.
– yuri
May 3 at 11:37




@pointerless I always compile with -O2 in this case I also tried -O3 and -Ofast neither of which changed the rather high CPU usage.
– yuri
May 3 at 11:37












I would rather simulate the game in a separate thread, store the results (or changes) in a container and use sfml for playback. This would also allow you to go to previous steps.
– Timo
May 3 at 11:48




I would rather simulate the game in a separate thread, store the results (or changes) in a container and use sfml for playback. This would also allow you to go to previous steps.
– Timo
May 3 at 11:48










2 Answers
2






active

oldest

votes

















up vote
3
down vote



accepted










I think that you are correct to be concerned about the coupling. I also think that decoupling your program would make it easy to test, debug, and profile for performance issues. With that in mind I suggest you use the MVC Pattern or closely related MVVM or MVP and the Game Loop.



The way I would implement the Update Method would be to build your GOL class as an application level class. At its core this pattern boils down to:



while(app.isRunning())

// handle all input from user (in your case p, u, and r)
// update all entities (here you would scan the neighbors and change your cells
// draw (here render your vertex array)




The MVC is what will help you decouple though. try to think of it this way. What if you wanted to switch libraries. Try to write your code so that the underlying data structure doesn't know or care how it is presented. Then you could theoretically switch from say SFML to SDL or some hand-rolled library or anything you'd like. You'd have to define your Cells in abstract manner. Maybe a 2d Matrix or a flattened matrix. The update method here would run your check_neighbors() or apply_rules functions and update all your cells accordingly.




It's also worth mentioning you have a good use of a double-buffer.




Now to build your View and Controller. In this case because its the same library and the Controller is so small I personally would make them the same class. Just shove your three user events into the input() function and move on. You will want to pass the Model as a reference to the View so it can know what to present. Here you will keep your sf::RenderWindow as well as your std::vector<sf::Vertex>.




This might seem like over-engineering and for such a small project it very well may be. However, you are having a performance issue that you can't narrow down and a little further abstraction will make that easier. Once you have a render() function you can profile and test it. You can do the same with an update() function.






share|improve this answer




























    up vote
    2
    down vote













    This is not an actual answer but rather an investigation that doesn't fit into the comment section.



    I just tested your code with VS2017 and let the profiler do its work.



    Build config c++17, sfml 2.4.2, release (O2), x64



    Rig:



    • OS: Windows 10 x64 (1709, 16299.371)

    • CPU: i7-6800K (6 cores @ 4.1GHz)

    • GPU: GTX 1080 Ti

    • RAM: 32 GB DDR4

    I'm aware that the rig is probably above average but this shouldn't matter that much when profiling the code and this is what the profiler gave me (don't mind the C.exe, that's the project name):



    performance profile



    As you can see, drawing is indeed not the problem, but updating the logic is the actual bottleneck. Your Gol::update function takes 30% of the entire execution time.



    Note



    Your game runs kinda fluent on my system (update cycles need 270ms, you limited it to 500ms).






    share|improve this answer























    • This is rather odd. After spending hours setting up microsoft bloat studio and testing this the display_grid function takes the top spot. I'm not familiar with using the MS profiler or VS in general so maybe I did something wrong when measuring?
      – yuri
      May 3 at 18:47










    • That's indeed interesting. Did you compile debug or release?
      – Timo
      May 3 at 20:27










    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193547%2fsimple-implementation-of-conways-game-of-life-with-sfml%23new-answer', 'question_page');

    );

    Post as a guest






























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    3
    down vote



    accepted










    I think that you are correct to be concerned about the coupling. I also think that decoupling your program would make it easy to test, debug, and profile for performance issues. With that in mind I suggest you use the MVC Pattern or closely related MVVM or MVP and the Game Loop.



    The way I would implement the Update Method would be to build your GOL class as an application level class. At its core this pattern boils down to:



    while(app.isRunning())

    // handle all input from user (in your case p, u, and r)
    // update all entities (here you would scan the neighbors and change your cells
    // draw (here render your vertex array)




    The MVC is what will help you decouple though. try to think of it this way. What if you wanted to switch libraries. Try to write your code so that the underlying data structure doesn't know or care how it is presented. Then you could theoretically switch from say SFML to SDL or some hand-rolled library or anything you'd like. You'd have to define your Cells in abstract manner. Maybe a 2d Matrix or a flattened matrix. The update method here would run your check_neighbors() or apply_rules functions and update all your cells accordingly.




    It's also worth mentioning you have a good use of a double-buffer.




    Now to build your View and Controller. In this case because its the same library and the Controller is so small I personally would make them the same class. Just shove your three user events into the input() function and move on. You will want to pass the Model as a reference to the View so it can know what to present. Here you will keep your sf::RenderWindow as well as your std::vector<sf::Vertex>.




    This might seem like over-engineering and for such a small project it very well may be. However, you are having a performance issue that you can't narrow down and a little further abstraction will make that easier. Once you have a render() function you can profile and test it. You can do the same with an update() function.






    share|improve this answer

























      up vote
      3
      down vote



      accepted










      I think that you are correct to be concerned about the coupling. I also think that decoupling your program would make it easy to test, debug, and profile for performance issues. With that in mind I suggest you use the MVC Pattern or closely related MVVM or MVP and the Game Loop.



      The way I would implement the Update Method would be to build your GOL class as an application level class. At its core this pattern boils down to:



      while(app.isRunning())

      // handle all input from user (in your case p, u, and r)
      // update all entities (here you would scan the neighbors and change your cells
      // draw (here render your vertex array)




      The MVC is what will help you decouple though. try to think of it this way. What if you wanted to switch libraries. Try to write your code so that the underlying data structure doesn't know or care how it is presented. Then you could theoretically switch from say SFML to SDL or some hand-rolled library or anything you'd like. You'd have to define your Cells in abstract manner. Maybe a 2d Matrix or a flattened matrix. The update method here would run your check_neighbors() or apply_rules functions and update all your cells accordingly.




      It's also worth mentioning you have a good use of a double-buffer.




      Now to build your View and Controller. In this case because its the same library and the Controller is so small I personally would make them the same class. Just shove your three user events into the input() function and move on. You will want to pass the Model as a reference to the View so it can know what to present. Here you will keep your sf::RenderWindow as well as your std::vector<sf::Vertex>.




      This might seem like over-engineering and for such a small project it very well may be. However, you are having a performance issue that you can't narrow down and a little further abstraction will make that easier. Once you have a render() function you can profile and test it. You can do the same with an update() function.






      share|improve this answer























        up vote
        3
        down vote



        accepted







        up vote
        3
        down vote



        accepted






        I think that you are correct to be concerned about the coupling. I also think that decoupling your program would make it easy to test, debug, and profile for performance issues. With that in mind I suggest you use the MVC Pattern or closely related MVVM or MVP and the Game Loop.



        The way I would implement the Update Method would be to build your GOL class as an application level class. At its core this pattern boils down to:



        while(app.isRunning())

        // handle all input from user (in your case p, u, and r)
        // update all entities (here you would scan the neighbors and change your cells
        // draw (here render your vertex array)




        The MVC is what will help you decouple though. try to think of it this way. What if you wanted to switch libraries. Try to write your code so that the underlying data structure doesn't know or care how it is presented. Then you could theoretically switch from say SFML to SDL or some hand-rolled library or anything you'd like. You'd have to define your Cells in abstract manner. Maybe a 2d Matrix or a flattened matrix. The update method here would run your check_neighbors() or apply_rules functions and update all your cells accordingly.




        It's also worth mentioning you have a good use of a double-buffer.




        Now to build your View and Controller. In this case because its the same library and the Controller is so small I personally would make them the same class. Just shove your three user events into the input() function and move on. You will want to pass the Model as a reference to the View so it can know what to present. Here you will keep your sf::RenderWindow as well as your std::vector<sf::Vertex>.




        This might seem like over-engineering and for such a small project it very well may be. However, you are having a performance issue that you can't narrow down and a little further abstraction will make that easier. Once you have a render() function you can profile and test it. You can do the same with an update() function.






        share|improve this answer













        I think that you are correct to be concerned about the coupling. I also think that decoupling your program would make it easy to test, debug, and profile for performance issues. With that in mind I suggest you use the MVC Pattern or closely related MVVM or MVP and the Game Loop.



        The way I would implement the Update Method would be to build your GOL class as an application level class. At its core this pattern boils down to:



        while(app.isRunning())

        // handle all input from user (in your case p, u, and r)
        // update all entities (here you would scan the neighbors and change your cells
        // draw (here render your vertex array)




        The MVC is what will help you decouple though. try to think of it this way. What if you wanted to switch libraries. Try to write your code so that the underlying data structure doesn't know or care how it is presented. Then you could theoretically switch from say SFML to SDL or some hand-rolled library or anything you'd like. You'd have to define your Cells in abstract manner. Maybe a 2d Matrix or a flattened matrix. The update method here would run your check_neighbors() or apply_rules functions and update all your cells accordingly.




        It's also worth mentioning you have a good use of a double-buffer.




        Now to build your View and Controller. In this case because its the same library and the Controller is so small I personally would make them the same class. Just shove your three user events into the input() function and move on. You will want to pass the Model as a reference to the View so it can know what to present. Here you will keep your sf::RenderWindow as well as your std::vector<sf::Vertex>.




        This might seem like over-engineering and for such a small project it very well may be. However, you are having a performance issue that you can't narrow down and a little further abstraction will make that easier. Once you have a render() function you can profile and test it. You can do the same with an update() function.







        share|improve this answer













        share|improve this answer



        share|improve this answer











        answered May 30 at 2:27









        bruglesco

        9321518




        9321518






















            up vote
            2
            down vote













            This is not an actual answer but rather an investigation that doesn't fit into the comment section.



            I just tested your code with VS2017 and let the profiler do its work.



            Build config c++17, sfml 2.4.2, release (O2), x64



            Rig:



            • OS: Windows 10 x64 (1709, 16299.371)

            • CPU: i7-6800K (6 cores @ 4.1GHz)

            • GPU: GTX 1080 Ti

            • RAM: 32 GB DDR4

            I'm aware that the rig is probably above average but this shouldn't matter that much when profiling the code and this is what the profiler gave me (don't mind the C.exe, that's the project name):



            performance profile



            As you can see, drawing is indeed not the problem, but updating the logic is the actual bottleneck. Your Gol::update function takes 30% of the entire execution time.



            Note



            Your game runs kinda fluent on my system (update cycles need 270ms, you limited it to 500ms).






            share|improve this answer























            • This is rather odd. After spending hours setting up microsoft bloat studio and testing this the display_grid function takes the top spot. I'm not familiar with using the MS profiler or VS in general so maybe I did something wrong when measuring?
              – yuri
              May 3 at 18:47










            • That's indeed interesting. Did you compile debug or release?
              – Timo
              May 3 at 20:27














            up vote
            2
            down vote













            This is not an actual answer but rather an investigation that doesn't fit into the comment section.



            I just tested your code with VS2017 and let the profiler do its work.



            Build config c++17, sfml 2.4.2, release (O2), x64



            Rig:



            • OS: Windows 10 x64 (1709, 16299.371)

            • CPU: i7-6800K (6 cores @ 4.1GHz)

            • GPU: GTX 1080 Ti

            • RAM: 32 GB DDR4

            I'm aware that the rig is probably above average but this shouldn't matter that much when profiling the code and this is what the profiler gave me (don't mind the C.exe, that's the project name):



            performance profile



            As you can see, drawing is indeed not the problem, but updating the logic is the actual bottleneck. Your Gol::update function takes 30% of the entire execution time.



            Note



            Your game runs kinda fluent on my system (update cycles need 270ms, you limited it to 500ms).






            share|improve this answer























            • This is rather odd. After spending hours setting up microsoft bloat studio and testing this the display_grid function takes the top spot. I'm not familiar with using the MS profiler or VS in general so maybe I did something wrong when measuring?
              – yuri
              May 3 at 18:47










            • That's indeed interesting. Did you compile debug or release?
              – Timo
              May 3 at 20:27












            up vote
            2
            down vote










            up vote
            2
            down vote









            This is not an actual answer but rather an investigation that doesn't fit into the comment section.



            I just tested your code with VS2017 and let the profiler do its work.



            Build config c++17, sfml 2.4.2, release (O2), x64



            Rig:



            • OS: Windows 10 x64 (1709, 16299.371)

            • CPU: i7-6800K (6 cores @ 4.1GHz)

            • GPU: GTX 1080 Ti

            • RAM: 32 GB DDR4

            I'm aware that the rig is probably above average but this shouldn't matter that much when profiling the code and this is what the profiler gave me (don't mind the C.exe, that's the project name):



            performance profile



            As you can see, drawing is indeed not the problem, but updating the logic is the actual bottleneck. Your Gol::update function takes 30% of the entire execution time.



            Note



            Your game runs kinda fluent on my system (update cycles need 270ms, you limited it to 500ms).






            share|improve this answer















            This is not an actual answer but rather an investigation that doesn't fit into the comment section.



            I just tested your code with VS2017 and let the profiler do its work.



            Build config c++17, sfml 2.4.2, release (O2), x64



            Rig:



            • OS: Windows 10 x64 (1709, 16299.371)

            • CPU: i7-6800K (6 cores @ 4.1GHz)

            • GPU: GTX 1080 Ti

            • RAM: 32 GB DDR4

            I'm aware that the rig is probably above average but this shouldn't matter that much when profiling the code and this is what the profiler gave me (don't mind the C.exe, that's the project name):



            performance profile



            As you can see, drawing is indeed not the problem, but updating the logic is the actual bottleneck. Your Gol::update function takes 30% of the entire execution time.



            Note



            Your game runs kinda fluent on my system (update cycles need 270ms, you limited it to 500ms).







            share|improve this answer















            share|improve this answer



            share|improve this answer








            edited May 3 at 16:25


























            answered May 3 at 16:11









            Timo

            1818




            1818











            • This is rather odd. After spending hours setting up microsoft bloat studio and testing this the display_grid function takes the top spot. I'm not familiar with using the MS profiler or VS in general so maybe I did something wrong when measuring?
              – yuri
              May 3 at 18:47










            • That's indeed interesting. Did you compile debug or release?
              – Timo
              May 3 at 20:27
















            • This is rather odd. After spending hours setting up microsoft bloat studio and testing this the display_grid function takes the top spot. I'm not familiar with using the MS profiler or VS in general so maybe I did something wrong when measuring?
              – yuri
              May 3 at 18:47










            • That's indeed interesting. Did you compile debug or release?
              – Timo
              May 3 at 20:27















            This is rather odd. After spending hours setting up microsoft bloat studio and testing this the display_grid function takes the top spot. I'm not familiar with using the MS profiler or VS in general so maybe I did something wrong when measuring?
            – yuri
            May 3 at 18:47




            This is rather odd. After spending hours setting up microsoft bloat studio and testing this the display_grid function takes the top spot. I'm not familiar with using the MS profiler or VS in general so maybe I did something wrong when measuring?
            – yuri
            May 3 at 18:47












            That's indeed interesting. Did you compile debug or release?
            – Timo
            May 3 at 20:27




            That's indeed interesting. Did you compile debug or release?
            – Timo
            May 3 at 20:27












             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193547%2fsimple-implementation-of-conways-game-of-life-with-sfml%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Python Lists

            Aion

            JavaScript Array Iteration Methods