SFML dice game with logic-presentation separation

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
8
down vote

favorite
1












I asked for feedback on my Dice class and I was suggested that I should separate logic from view. I decided to completely refactor my project and I'd like to share a small piece of it (the Dice class and connection between logic and view) and ask for feedback again.



So lets start with logic::Dice



namespace logic 
class Dice
std::default_random_engine rng;
int m_currentNumber;
public:
Dice()
rng.seed(static_cast<int>(std::chrono::system_clock::now().time_since_epoch().count()));

~Dice() = default;
void rollNewNumber();
int getCurrentNumber() const;
;


void logic::Dice::rollNewNumber()
std::uniform_int_distribution<int> dist(1, 6);
m_currentNumber = dist(rng);


int logic::Dice::getCurrentNumber() const
return m_currentNumber;



Now view::Dice



 namespace view 
class Dice
sf::Texture m_texture;
sf::RectangleShape m_shape;
sf::SoundBuffer m_buffer;
sf::Sound m_sound;
public:
Dice();
~Dice() = default;
sf::RectangleShape& get();
void changeTexture(int);
void playSound();
;


view::Dice::Dice()
m_shape.setSize(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT));

if (!m_texture.loadFromFile(DICE_TEXTURE)) std::cout << "Can't load texture.n";
m_shape.setTexture(&m_texture);
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));

if (!m_buffer.loadFromFile(DICE_SOUND)) std::cout << "Can't load sound.n";
m_sound.setBuffer(m_buffer);


sf::RectangleShape& view::Dice::get()
return m_shape;


void view::Dice::changeTexture(int newNumber)
switch (newNumber)
case 1:
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));
break;

case 2:
m_shape.setTextureRect(sf::IntRect(55, 0, 55, 55));
break;

case 3:
m_shape.setTextureRect(sf::IntRect(110, 0, 55, 55));
break;

case 4:
m_shape.setTextureRect(sf::IntRect(165, 0, 55, 55));
break;

case 5:
m_shape.setTextureRect(sf::IntRect(220, 0, 55, 55));
break;

case 6:
m_shape.setTextureRect(sf::IntRect(275, 0, 55, 55));
break;




void view::Dice::playSound()
m_sound.play();



Now to connect logic with presentation, I got 2 classes. view::GameState which derives from view::State that looks like that



namespace view 
class State
public:
virtual void initialise() = 0;
virtual void handleUserInput() = 0;
virtual void update(sf::Time dt) = 0;
virtual void draw() = 0;
;



And most important in this case part of my view::GameState



 class GameState : public State 
logic::Game m_game;
view::Dice m_diceOne, m_diceTwo;
;


And inside my view::GameState::handleUserInput() I check when "Roll the dice" button is clicked



 if (this->m_data->inputManager.isSpriteClicked(this->m_rollButton, evnt, this->m_data->window)) 
rollTheDice();



Which finnaly gets to connection between logic and presentation



void view::GameState::rollTheDice() 
m_game.rollTheDice();
m_diceOne.playSound();
m_diceOne.changeTexture(m_game.getDiceOne().getCurrentNumber());
m_diceTwo.changeTexture(m_game.getDiceTwo().getCurrentNumber());



That leads to logic::Game class which is kind of game engine, that does all the logic and has all the logic objects like Dice, Player, GameBoard



 namespace logic 
class Game
logic::Dice m_diceOne, m_diceTwo;

int m_throwsInCurrentTurn = 0;
int m_doublesInCurrentTurn = 0;
int m_totalRollResult = 0;
public:
void rollTheDice();
logic::Dice& getDiceOne();
logic::Dice& getDiceTwo();
;


void logic::Game::rollTheDice()
m_throwsInCurrentTurn++;
m_diceOne.rollNewNumber();
m_diceTwo.rollNewNumber();
int diceOneResult = m_diceOne.getCurrentNumber();
int diceTwoResult = m_diceTwo.getCurrentNumber();
m_totalRollResult += diceOneResult + diceTwoResult;
if (diceOneResult == diceTwoResult) m_doublesInCurrentTurn++;


logic::Dice& logic::Game::getDiceOne()
return m_diceOne;


logic::Dice & logic::Game::getDiceTwo()
return m_diceTwo;



I would appriciate your feedback and suggestions how to improve my code.







share|improve this question





















  • You might want to add a link to the original question.
    – pacmaninbw
    Jan 6 at 12:47






  • 1




    Here is the link: codereview.stackexchange.com/questions/184204/smfl-dice-class/… but I dont think that is actually relevant in this question
    – CleanCoder
    Jan 6 at 13:09

















up vote
8
down vote

favorite
1












I asked for feedback on my Dice class and I was suggested that I should separate logic from view. I decided to completely refactor my project and I'd like to share a small piece of it (the Dice class and connection between logic and view) and ask for feedback again.



So lets start with logic::Dice



namespace logic 
class Dice
std::default_random_engine rng;
int m_currentNumber;
public:
Dice()
rng.seed(static_cast<int>(std::chrono::system_clock::now().time_since_epoch().count()));

~Dice() = default;
void rollNewNumber();
int getCurrentNumber() const;
;


void logic::Dice::rollNewNumber()
std::uniform_int_distribution<int> dist(1, 6);
m_currentNumber = dist(rng);


int logic::Dice::getCurrentNumber() const
return m_currentNumber;



Now view::Dice



 namespace view 
class Dice
sf::Texture m_texture;
sf::RectangleShape m_shape;
sf::SoundBuffer m_buffer;
sf::Sound m_sound;
public:
Dice();
~Dice() = default;
sf::RectangleShape& get();
void changeTexture(int);
void playSound();
;


view::Dice::Dice()
m_shape.setSize(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT));

if (!m_texture.loadFromFile(DICE_TEXTURE)) std::cout << "Can't load texture.n";
m_shape.setTexture(&m_texture);
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));

if (!m_buffer.loadFromFile(DICE_SOUND)) std::cout << "Can't load sound.n";
m_sound.setBuffer(m_buffer);


sf::RectangleShape& view::Dice::get()
return m_shape;


void view::Dice::changeTexture(int newNumber)
switch (newNumber)
case 1:
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));
break;

case 2:
m_shape.setTextureRect(sf::IntRect(55, 0, 55, 55));
break;

case 3:
m_shape.setTextureRect(sf::IntRect(110, 0, 55, 55));
break;

case 4:
m_shape.setTextureRect(sf::IntRect(165, 0, 55, 55));
break;

case 5:
m_shape.setTextureRect(sf::IntRect(220, 0, 55, 55));
break;

case 6:
m_shape.setTextureRect(sf::IntRect(275, 0, 55, 55));
break;




void view::Dice::playSound()
m_sound.play();



Now to connect logic with presentation, I got 2 classes. view::GameState which derives from view::State that looks like that



namespace view 
class State
public:
virtual void initialise() = 0;
virtual void handleUserInput() = 0;
virtual void update(sf::Time dt) = 0;
virtual void draw() = 0;
;



And most important in this case part of my view::GameState



 class GameState : public State 
logic::Game m_game;
view::Dice m_diceOne, m_diceTwo;
;


And inside my view::GameState::handleUserInput() I check when "Roll the dice" button is clicked



 if (this->m_data->inputManager.isSpriteClicked(this->m_rollButton, evnt, this->m_data->window)) 
rollTheDice();



Which finnaly gets to connection between logic and presentation



void view::GameState::rollTheDice() 
m_game.rollTheDice();
m_diceOne.playSound();
m_diceOne.changeTexture(m_game.getDiceOne().getCurrentNumber());
m_diceTwo.changeTexture(m_game.getDiceTwo().getCurrentNumber());



That leads to logic::Game class which is kind of game engine, that does all the logic and has all the logic objects like Dice, Player, GameBoard



 namespace logic 
class Game
logic::Dice m_diceOne, m_diceTwo;

int m_throwsInCurrentTurn = 0;
int m_doublesInCurrentTurn = 0;
int m_totalRollResult = 0;
public:
void rollTheDice();
logic::Dice& getDiceOne();
logic::Dice& getDiceTwo();
;


void logic::Game::rollTheDice()
m_throwsInCurrentTurn++;
m_diceOne.rollNewNumber();
m_diceTwo.rollNewNumber();
int diceOneResult = m_diceOne.getCurrentNumber();
int diceTwoResult = m_diceTwo.getCurrentNumber();
m_totalRollResult += diceOneResult + diceTwoResult;
if (diceOneResult == diceTwoResult) m_doublesInCurrentTurn++;


logic::Dice& logic::Game::getDiceOne()
return m_diceOne;


logic::Dice & logic::Game::getDiceTwo()
return m_diceTwo;



I would appriciate your feedback and suggestions how to improve my code.







share|improve this question





















  • You might want to add a link to the original question.
    – pacmaninbw
    Jan 6 at 12:47






  • 1




    Here is the link: codereview.stackexchange.com/questions/184204/smfl-dice-class/… but I dont think that is actually relevant in this question
    – CleanCoder
    Jan 6 at 13:09













up vote
8
down vote

favorite
1









up vote
8
down vote

favorite
1






1





I asked for feedback on my Dice class and I was suggested that I should separate logic from view. I decided to completely refactor my project and I'd like to share a small piece of it (the Dice class and connection between logic and view) and ask for feedback again.



So lets start with logic::Dice



namespace logic 
class Dice
std::default_random_engine rng;
int m_currentNumber;
public:
Dice()
rng.seed(static_cast<int>(std::chrono::system_clock::now().time_since_epoch().count()));

~Dice() = default;
void rollNewNumber();
int getCurrentNumber() const;
;


void logic::Dice::rollNewNumber()
std::uniform_int_distribution<int> dist(1, 6);
m_currentNumber = dist(rng);


int logic::Dice::getCurrentNumber() const
return m_currentNumber;



Now view::Dice



 namespace view 
class Dice
sf::Texture m_texture;
sf::RectangleShape m_shape;
sf::SoundBuffer m_buffer;
sf::Sound m_sound;
public:
Dice();
~Dice() = default;
sf::RectangleShape& get();
void changeTexture(int);
void playSound();
;


view::Dice::Dice()
m_shape.setSize(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT));

if (!m_texture.loadFromFile(DICE_TEXTURE)) std::cout << "Can't load texture.n";
m_shape.setTexture(&m_texture);
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));

if (!m_buffer.loadFromFile(DICE_SOUND)) std::cout << "Can't load sound.n";
m_sound.setBuffer(m_buffer);


sf::RectangleShape& view::Dice::get()
return m_shape;


void view::Dice::changeTexture(int newNumber)
switch (newNumber)
case 1:
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));
break;

case 2:
m_shape.setTextureRect(sf::IntRect(55, 0, 55, 55));
break;

case 3:
m_shape.setTextureRect(sf::IntRect(110, 0, 55, 55));
break;

case 4:
m_shape.setTextureRect(sf::IntRect(165, 0, 55, 55));
break;

case 5:
m_shape.setTextureRect(sf::IntRect(220, 0, 55, 55));
break;

case 6:
m_shape.setTextureRect(sf::IntRect(275, 0, 55, 55));
break;




void view::Dice::playSound()
m_sound.play();



Now to connect logic with presentation, I got 2 classes. view::GameState which derives from view::State that looks like that



namespace view 
class State
public:
virtual void initialise() = 0;
virtual void handleUserInput() = 0;
virtual void update(sf::Time dt) = 0;
virtual void draw() = 0;
;



And most important in this case part of my view::GameState



 class GameState : public State 
logic::Game m_game;
view::Dice m_diceOne, m_diceTwo;
;


And inside my view::GameState::handleUserInput() I check when "Roll the dice" button is clicked



 if (this->m_data->inputManager.isSpriteClicked(this->m_rollButton, evnt, this->m_data->window)) 
rollTheDice();



Which finnaly gets to connection between logic and presentation



void view::GameState::rollTheDice() 
m_game.rollTheDice();
m_diceOne.playSound();
m_diceOne.changeTexture(m_game.getDiceOne().getCurrentNumber());
m_diceTwo.changeTexture(m_game.getDiceTwo().getCurrentNumber());



That leads to logic::Game class which is kind of game engine, that does all the logic and has all the logic objects like Dice, Player, GameBoard



 namespace logic 
class Game
logic::Dice m_diceOne, m_diceTwo;

int m_throwsInCurrentTurn = 0;
int m_doublesInCurrentTurn = 0;
int m_totalRollResult = 0;
public:
void rollTheDice();
logic::Dice& getDiceOne();
logic::Dice& getDiceTwo();
;


void logic::Game::rollTheDice()
m_throwsInCurrentTurn++;
m_diceOne.rollNewNumber();
m_diceTwo.rollNewNumber();
int diceOneResult = m_diceOne.getCurrentNumber();
int diceTwoResult = m_diceTwo.getCurrentNumber();
m_totalRollResult += diceOneResult + diceTwoResult;
if (diceOneResult == diceTwoResult) m_doublesInCurrentTurn++;


logic::Dice& logic::Game::getDiceOne()
return m_diceOne;


logic::Dice & logic::Game::getDiceTwo()
return m_diceTwo;



I would appriciate your feedback and suggestions how to improve my code.







share|improve this question













I asked for feedback on my Dice class and I was suggested that I should separate logic from view. I decided to completely refactor my project and I'd like to share a small piece of it (the Dice class and connection between logic and view) and ask for feedback again.



So lets start with logic::Dice



namespace logic 
class Dice
std::default_random_engine rng;
int m_currentNumber;
public:
Dice()
rng.seed(static_cast<int>(std::chrono::system_clock::now().time_since_epoch().count()));

~Dice() = default;
void rollNewNumber();
int getCurrentNumber() const;
;


void logic::Dice::rollNewNumber()
std::uniform_int_distribution<int> dist(1, 6);
m_currentNumber = dist(rng);


int logic::Dice::getCurrentNumber() const
return m_currentNumber;



Now view::Dice



 namespace view 
class Dice
sf::Texture m_texture;
sf::RectangleShape m_shape;
sf::SoundBuffer m_buffer;
sf::Sound m_sound;
public:
Dice();
~Dice() = default;
sf::RectangleShape& get();
void changeTexture(int);
void playSound();
;


view::Dice::Dice()
m_shape.setSize(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT));

if (!m_texture.loadFromFile(DICE_TEXTURE)) std::cout << "Can't load texture.n";
m_shape.setTexture(&m_texture);
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));

if (!m_buffer.loadFromFile(DICE_SOUND)) std::cout << "Can't load sound.n";
m_sound.setBuffer(m_buffer);


sf::RectangleShape& view::Dice::get()
return m_shape;


void view::Dice::changeTexture(int newNumber)
switch (newNumber)
case 1:
m_shape.setTextureRect(sf::IntRect(0, 0, 55, 55));
break;

case 2:
m_shape.setTextureRect(sf::IntRect(55, 0, 55, 55));
break;

case 3:
m_shape.setTextureRect(sf::IntRect(110, 0, 55, 55));
break;

case 4:
m_shape.setTextureRect(sf::IntRect(165, 0, 55, 55));
break;

case 5:
m_shape.setTextureRect(sf::IntRect(220, 0, 55, 55));
break;

case 6:
m_shape.setTextureRect(sf::IntRect(275, 0, 55, 55));
break;




void view::Dice::playSound()
m_sound.play();



Now to connect logic with presentation, I got 2 classes. view::GameState which derives from view::State that looks like that



namespace view 
class State
public:
virtual void initialise() = 0;
virtual void handleUserInput() = 0;
virtual void update(sf::Time dt) = 0;
virtual void draw() = 0;
;



And most important in this case part of my view::GameState



 class GameState : public State 
logic::Game m_game;
view::Dice m_diceOne, m_diceTwo;
;


And inside my view::GameState::handleUserInput() I check when "Roll the dice" button is clicked



 if (this->m_data->inputManager.isSpriteClicked(this->m_rollButton, evnt, this->m_data->window)) 
rollTheDice();



Which finnaly gets to connection between logic and presentation



void view::GameState::rollTheDice() 
m_game.rollTheDice();
m_diceOne.playSound();
m_diceOne.changeTexture(m_game.getDiceOne().getCurrentNumber());
m_diceTwo.changeTexture(m_game.getDiceTwo().getCurrentNumber());



That leads to logic::Game class which is kind of game engine, that does all the logic and has all the logic objects like Dice, Player, GameBoard



 namespace logic 
class Game
logic::Dice m_diceOne, m_diceTwo;

int m_throwsInCurrentTurn = 0;
int m_doublesInCurrentTurn = 0;
int m_totalRollResult = 0;
public:
void rollTheDice();
logic::Dice& getDiceOne();
logic::Dice& getDiceTwo();
;


void logic::Game::rollTheDice()
m_throwsInCurrentTurn++;
m_diceOne.rollNewNumber();
m_diceTwo.rollNewNumber();
int diceOneResult = m_diceOne.getCurrentNumber();
int diceTwoResult = m_diceTwo.getCurrentNumber();
m_totalRollResult += diceOneResult + diceTwoResult;
if (diceOneResult == diceTwoResult) m_doublesInCurrentTurn++;


logic::Dice& logic::Game::getDiceOne()
return m_diceOne;


logic::Dice & logic::Game::getDiceTwo()
return m_diceTwo;



I would appriciate your feedback and suggestions how to improve my code.









share|improve this question












share|improve this question




share|improve this question








edited Feb 27 at 15:01









200_success

123k14143401




123k14143401









asked Jan 6 at 9:46









CleanCoder

583




583











  • You might want to add a link to the original question.
    – pacmaninbw
    Jan 6 at 12:47






  • 1




    Here is the link: codereview.stackexchange.com/questions/184204/smfl-dice-class/… but I dont think that is actually relevant in this question
    – CleanCoder
    Jan 6 at 13:09

















  • You might want to add a link to the original question.
    – pacmaninbw
    Jan 6 at 12:47






  • 1




    Here is the link: codereview.stackexchange.com/questions/184204/smfl-dice-class/… but I dont think that is actually relevant in this question
    – CleanCoder
    Jan 6 at 13:09
















You might want to add a link to the original question.
– pacmaninbw
Jan 6 at 12:47




You might want to add a link to the original question.
– pacmaninbw
Jan 6 at 12:47




1




1




Here is the link: codereview.stackexchange.com/questions/184204/smfl-dice-class/… but I dont think that is actually relevant in this question
– CleanCoder
Jan 6 at 13:09





Here is the link: codereview.stackexchange.com/questions/184204/smfl-dice-class/… but I dont think that is actually relevant in this question
– CleanCoder
Jan 6 at 13:09











1 Answer
1






active

oldest

votes

















up vote
2
down vote













[code] m_



One of the main benefits of using the m_ prefix is not having to put this-> in front of members!




[design] overall



While separating logic and presentation are important, I'd suggest it has more to do with ensuring that a class does only one thing (the "Single Responsibility Principle"), rather than with overall organisation.



While the logic and view namespaces might be useful if there were lots of similar classes that needed to be split in exactly this way, I'm not sure they're necessary here. Also, I don't think that the State and GameState classes need to be in either (view::GameState contains both logic and view objects).



Throughout the code, there are many places where data is hard-coded into a class. This generally indicates "unfinished" design / programming, and leads to duplication and missed opportunities for code reuse.




[design] specifics



I'd recommend the following:




  • view and logic namespaces - remove them for the reasons mentioned above.


  • logic::Dice - rename to just Dice.


  • view::Dice -

    • move the dice-specific hard-coded data definitions outside of the class.

    • split the sf::Sound member into a separate SoundEffect class.

    • rename the remainder to TexturedQuad or similar.



  • logic::Game - rename to GameLogic.


  • view::State and view::GameState - move out of view namespace (it's hard to say more without seeing full code).

So we end up with the following architecture:



class TexturedQuad 
public:
TexturedQuad(sf::Vector2 size, std::string const& textureFileName, sf::IntRect const& textureRect);

void setPosition(sf::Vector2f const& position); // ??
void setTextureRect(sf::IntRect const& textureRect);
void draw(); // ??

private:
sf::Texture m_texture;
sf::RectangleShape m_shape;
;

class SoundEffect
public:
SoundEffect(std::string const& fileName);

void play();

private:
sf::Sound m_sound;
sf::SoundBuffer m_buffer;
;

class State ... ;
class GameState : public State
...

GameLogic m_game;
SoundEffect m_diceSoundEffect; // note, we only need one of these!
TexturedQuad m_diceTexturedQuad; // and (probably) only one of these!
;

class Dice ... ;
class GameLogic
...

Dice m_dice; // only one of these!

int m_rollOne = 0; // store the current rolls in the class instead...
int m_rollTwo = 0;
;


By separating out the data, and passing it into constructors where necessary, we end up with generic classes that can be reused. We've also uncovered some duplication of class instances:



  • We only need one dice sound effect instance, not two.

  • We only need one textured quad, which can be repositioned and the texture coordinates changed to draw both dice. (Note: I don't know sfml, and am assuming that changing the position and "textureRect" are trivial operations that can be done in the rendering loop. You may still need two quads, if this isn't the case.)

  • Unless there's a need to reproduce two separate runs of dice rolls in the random number generator (i.e. use two separate seeds), we only need one dice instance, which we can roll twice.

So the functions can be rewritten as:



void GameState::rollTheDice() 
m_game.rollTheDice();
m_diceSoundEffect.play();


void GameState::draw()

m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollOne));
m_diceTexturedQuad.setPosition(...);
m_diceTexturedQuad.draw();

m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollTwo));
m_diceTexturedQuad.setPosition(...);
m_diceTexturedQuad.draw();

...


void GameLogic::rollTheDice()
m_throwsInCurrentTurn++;

m_rollOne = m_dice.roll();
m_rollTwo = m_dice.roll();

m_totalRollResult += m_rollOne + m_rollTwo;

if (m_rollOne == m_rollTwo)
m_doublesInCurrentTurn++;



and constructor initializer lists used to pass data into our generic classes, e.g.:



GameState::GameState():
m_game(),
m_diceSoundEffect(DICE_SOUND),
m_diceTexturedQuad(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT), DICE_TEXTURE, sf::IntRect(0, 0, 55, 55))



[design] dice class



The same principles of removing data from the class allow us to change the dice class so it can be used for a die with any number of sides:



class Dice 
public:

using rng_t = std::mt19937; // default_random_engine or whatever...
using output_t = rng_t::result_type;

Dice(output_t sides, output_t seed = std::random_device()()):
m_distribution(output_t 1 , sides),
m_rng(seed)

output_t roll()
return m_distribution(m_rng);


private:
std::uniform_int_distribution<output_t> m_distribution;
rng_t m_rng;
;

...

Dice d(6);
std::cout << d.roll() << std::endl;


I believe the standard way to obtain a random seed is to use std::random_device as above.




[naming]



Technically, the word "dice" is a plural of the singular "die". I'd be tempted to rename Dice to Die, as the class represents only one of the things. I'd expect a "Dice" class to provide an interface for multiple dice, such as 2d6 or 3d4 rolls, etc.



But others might disagree... :S






share|improve this answer























    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%2f184431%2fsfml-dice-game-with-logic-presentation-separation%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    2
    down vote













    [code] m_



    One of the main benefits of using the m_ prefix is not having to put this-> in front of members!




    [design] overall



    While separating logic and presentation are important, I'd suggest it has more to do with ensuring that a class does only one thing (the "Single Responsibility Principle"), rather than with overall organisation.



    While the logic and view namespaces might be useful if there were lots of similar classes that needed to be split in exactly this way, I'm not sure they're necessary here. Also, I don't think that the State and GameState classes need to be in either (view::GameState contains both logic and view objects).



    Throughout the code, there are many places where data is hard-coded into a class. This generally indicates "unfinished" design / programming, and leads to duplication and missed opportunities for code reuse.




    [design] specifics



    I'd recommend the following:




    • view and logic namespaces - remove them for the reasons mentioned above.


    • logic::Dice - rename to just Dice.


    • view::Dice -

      • move the dice-specific hard-coded data definitions outside of the class.

      • split the sf::Sound member into a separate SoundEffect class.

      • rename the remainder to TexturedQuad or similar.



    • logic::Game - rename to GameLogic.


    • view::State and view::GameState - move out of view namespace (it's hard to say more without seeing full code).

    So we end up with the following architecture:



    class TexturedQuad 
    public:
    TexturedQuad(sf::Vector2 size, std::string const& textureFileName, sf::IntRect const& textureRect);

    void setPosition(sf::Vector2f const& position); // ??
    void setTextureRect(sf::IntRect const& textureRect);
    void draw(); // ??

    private:
    sf::Texture m_texture;
    sf::RectangleShape m_shape;
    ;

    class SoundEffect
    public:
    SoundEffect(std::string const& fileName);

    void play();

    private:
    sf::Sound m_sound;
    sf::SoundBuffer m_buffer;
    ;

    class State ... ;
    class GameState : public State
    ...

    GameLogic m_game;
    SoundEffect m_diceSoundEffect; // note, we only need one of these!
    TexturedQuad m_diceTexturedQuad; // and (probably) only one of these!
    ;

    class Dice ... ;
    class GameLogic
    ...

    Dice m_dice; // only one of these!

    int m_rollOne = 0; // store the current rolls in the class instead...
    int m_rollTwo = 0;
    ;


    By separating out the data, and passing it into constructors where necessary, we end up with generic classes that can be reused. We've also uncovered some duplication of class instances:



    • We only need one dice sound effect instance, not two.

    • We only need one textured quad, which can be repositioned and the texture coordinates changed to draw both dice. (Note: I don't know sfml, and am assuming that changing the position and "textureRect" are trivial operations that can be done in the rendering loop. You may still need two quads, if this isn't the case.)

    • Unless there's a need to reproduce two separate runs of dice rolls in the random number generator (i.e. use two separate seeds), we only need one dice instance, which we can roll twice.

    So the functions can be rewritten as:



    void GameState::rollTheDice() 
    m_game.rollTheDice();
    m_diceSoundEffect.play();


    void GameState::draw()

    m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollOne));
    m_diceTexturedQuad.setPosition(...);
    m_diceTexturedQuad.draw();

    m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollTwo));
    m_diceTexturedQuad.setPosition(...);
    m_diceTexturedQuad.draw();

    ...


    void GameLogic::rollTheDice()
    m_throwsInCurrentTurn++;

    m_rollOne = m_dice.roll();
    m_rollTwo = m_dice.roll();

    m_totalRollResult += m_rollOne + m_rollTwo;

    if (m_rollOne == m_rollTwo)
    m_doublesInCurrentTurn++;



    and constructor initializer lists used to pass data into our generic classes, e.g.:



    GameState::GameState():
    m_game(),
    m_diceSoundEffect(DICE_SOUND),
    m_diceTexturedQuad(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT), DICE_TEXTURE, sf::IntRect(0, 0, 55, 55))



    [design] dice class



    The same principles of removing data from the class allow us to change the dice class so it can be used for a die with any number of sides:



    class Dice 
    public:

    using rng_t = std::mt19937; // default_random_engine or whatever...
    using output_t = rng_t::result_type;

    Dice(output_t sides, output_t seed = std::random_device()()):
    m_distribution(output_t 1 , sides),
    m_rng(seed)

    output_t roll()
    return m_distribution(m_rng);


    private:
    std::uniform_int_distribution<output_t> m_distribution;
    rng_t m_rng;
    ;

    ...

    Dice d(6);
    std::cout << d.roll() << std::endl;


    I believe the standard way to obtain a random seed is to use std::random_device as above.




    [naming]



    Technically, the word "dice" is a plural of the singular "die". I'd be tempted to rename Dice to Die, as the class represents only one of the things. I'd expect a "Dice" class to provide an interface for multiple dice, such as 2d6 or 3d4 rolls, etc.



    But others might disagree... :S






    share|improve this answer



























      up vote
      2
      down vote













      [code] m_



      One of the main benefits of using the m_ prefix is not having to put this-> in front of members!




      [design] overall



      While separating logic and presentation are important, I'd suggest it has more to do with ensuring that a class does only one thing (the "Single Responsibility Principle"), rather than with overall organisation.



      While the logic and view namespaces might be useful if there were lots of similar classes that needed to be split in exactly this way, I'm not sure they're necessary here. Also, I don't think that the State and GameState classes need to be in either (view::GameState contains both logic and view objects).



      Throughout the code, there are many places where data is hard-coded into a class. This generally indicates "unfinished" design / programming, and leads to duplication and missed opportunities for code reuse.




      [design] specifics



      I'd recommend the following:




      • view and logic namespaces - remove them for the reasons mentioned above.


      • logic::Dice - rename to just Dice.


      • view::Dice -

        • move the dice-specific hard-coded data definitions outside of the class.

        • split the sf::Sound member into a separate SoundEffect class.

        • rename the remainder to TexturedQuad or similar.



      • logic::Game - rename to GameLogic.


      • view::State and view::GameState - move out of view namespace (it's hard to say more without seeing full code).

      So we end up with the following architecture:



      class TexturedQuad 
      public:
      TexturedQuad(sf::Vector2 size, std::string const& textureFileName, sf::IntRect const& textureRect);

      void setPosition(sf::Vector2f const& position); // ??
      void setTextureRect(sf::IntRect const& textureRect);
      void draw(); // ??

      private:
      sf::Texture m_texture;
      sf::RectangleShape m_shape;
      ;

      class SoundEffect
      public:
      SoundEffect(std::string const& fileName);

      void play();

      private:
      sf::Sound m_sound;
      sf::SoundBuffer m_buffer;
      ;

      class State ... ;
      class GameState : public State
      ...

      GameLogic m_game;
      SoundEffect m_diceSoundEffect; // note, we only need one of these!
      TexturedQuad m_diceTexturedQuad; // and (probably) only one of these!
      ;

      class Dice ... ;
      class GameLogic
      ...

      Dice m_dice; // only one of these!

      int m_rollOne = 0; // store the current rolls in the class instead...
      int m_rollTwo = 0;
      ;


      By separating out the data, and passing it into constructors where necessary, we end up with generic classes that can be reused. We've also uncovered some duplication of class instances:



      • We only need one dice sound effect instance, not two.

      • We only need one textured quad, which can be repositioned and the texture coordinates changed to draw both dice. (Note: I don't know sfml, and am assuming that changing the position and "textureRect" are trivial operations that can be done in the rendering loop. You may still need two quads, if this isn't the case.)

      • Unless there's a need to reproduce two separate runs of dice rolls in the random number generator (i.e. use two separate seeds), we only need one dice instance, which we can roll twice.

      So the functions can be rewritten as:



      void GameState::rollTheDice() 
      m_game.rollTheDice();
      m_diceSoundEffect.play();


      void GameState::draw()

      m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollOne));
      m_diceTexturedQuad.setPosition(...);
      m_diceTexturedQuad.draw();

      m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollTwo));
      m_diceTexturedQuad.setPosition(...);
      m_diceTexturedQuad.draw();

      ...


      void GameLogic::rollTheDice()
      m_throwsInCurrentTurn++;

      m_rollOne = m_dice.roll();
      m_rollTwo = m_dice.roll();

      m_totalRollResult += m_rollOne + m_rollTwo;

      if (m_rollOne == m_rollTwo)
      m_doublesInCurrentTurn++;



      and constructor initializer lists used to pass data into our generic classes, e.g.:



      GameState::GameState():
      m_game(),
      m_diceSoundEffect(DICE_SOUND),
      m_diceTexturedQuad(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT), DICE_TEXTURE, sf::IntRect(0, 0, 55, 55))



      [design] dice class



      The same principles of removing data from the class allow us to change the dice class so it can be used for a die with any number of sides:



      class Dice 
      public:

      using rng_t = std::mt19937; // default_random_engine or whatever...
      using output_t = rng_t::result_type;

      Dice(output_t sides, output_t seed = std::random_device()()):
      m_distribution(output_t 1 , sides),
      m_rng(seed)

      output_t roll()
      return m_distribution(m_rng);


      private:
      std::uniform_int_distribution<output_t> m_distribution;
      rng_t m_rng;
      ;

      ...

      Dice d(6);
      std::cout << d.roll() << std::endl;


      I believe the standard way to obtain a random seed is to use std::random_device as above.




      [naming]



      Technically, the word "dice" is a plural of the singular "die". I'd be tempted to rename Dice to Die, as the class represents only one of the things. I'd expect a "Dice" class to provide an interface for multiple dice, such as 2d6 or 3d4 rolls, etc.



      But others might disagree... :S






      share|improve this answer

























        up vote
        2
        down vote










        up vote
        2
        down vote









        [code] m_



        One of the main benefits of using the m_ prefix is not having to put this-> in front of members!




        [design] overall



        While separating logic and presentation are important, I'd suggest it has more to do with ensuring that a class does only one thing (the "Single Responsibility Principle"), rather than with overall organisation.



        While the logic and view namespaces might be useful if there were lots of similar classes that needed to be split in exactly this way, I'm not sure they're necessary here. Also, I don't think that the State and GameState classes need to be in either (view::GameState contains both logic and view objects).



        Throughout the code, there are many places where data is hard-coded into a class. This generally indicates "unfinished" design / programming, and leads to duplication and missed opportunities for code reuse.




        [design] specifics



        I'd recommend the following:




        • view and logic namespaces - remove them for the reasons mentioned above.


        • logic::Dice - rename to just Dice.


        • view::Dice -

          • move the dice-specific hard-coded data definitions outside of the class.

          • split the sf::Sound member into a separate SoundEffect class.

          • rename the remainder to TexturedQuad or similar.



        • logic::Game - rename to GameLogic.


        • view::State and view::GameState - move out of view namespace (it's hard to say more without seeing full code).

        So we end up with the following architecture:



        class TexturedQuad 
        public:
        TexturedQuad(sf::Vector2 size, std::string const& textureFileName, sf::IntRect const& textureRect);

        void setPosition(sf::Vector2f const& position); // ??
        void setTextureRect(sf::IntRect const& textureRect);
        void draw(); // ??

        private:
        sf::Texture m_texture;
        sf::RectangleShape m_shape;
        ;

        class SoundEffect
        public:
        SoundEffect(std::string const& fileName);

        void play();

        private:
        sf::Sound m_sound;
        sf::SoundBuffer m_buffer;
        ;

        class State ... ;
        class GameState : public State
        ...

        GameLogic m_game;
        SoundEffect m_diceSoundEffect; // note, we only need one of these!
        TexturedQuad m_diceTexturedQuad; // and (probably) only one of these!
        ;

        class Dice ... ;
        class GameLogic
        ...

        Dice m_dice; // only one of these!

        int m_rollOne = 0; // store the current rolls in the class instead...
        int m_rollTwo = 0;
        ;


        By separating out the data, and passing it into constructors where necessary, we end up with generic classes that can be reused. We've also uncovered some duplication of class instances:



        • We only need one dice sound effect instance, not two.

        • We only need one textured quad, which can be repositioned and the texture coordinates changed to draw both dice. (Note: I don't know sfml, and am assuming that changing the position and "textureRect" are trivial operations that can be done in the rendering loop. You may still need two quads, if this isn't the case.)

        • Unless there's a need to reproduce two separate runs of dice rolls in the random number generator (i.e. use two separate seeds), we only need one dice instance, which we can roll twice.

        So the functions can be rewritten as:



        void GameState::rollTheDice() 
        m_game.rollTheDice();
        m_diceSoundEffect.play();


        void GameState::draw()

        m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollOne));
        m_diceTexturedQuad.setPosition(...);
        m_diceTexturedQuad.draw();

        m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollTwo));
        m_diceTexturedQuad.setPosition(...);
        m_diceTexturedQuad.draw();

        ...


        void GameLogic::rollTheDice()
        m_throwsInCurrentTurn++;

        m_rollOne = m_dice.roll();
        m_rollTwo = m_dice.roll();

        m_totalRollResult += m_rollOne + m_rollTwo;

        if (m_rollOne == m_rollTwo)
        m_doublesInCurrentTurn++;



        and constructor initializer lists used to pass data into our generic classes, e.g.:



        GameState::GameState():
        m_game(),
        m_diceSoundEffect(DICE_SOUND),
        m_diceTexturedQuad(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT), DICE_TEXTURE, sf::IntRect(0, 0, 55, 55))



        [design] dice class



        The same principles of removing data from the class allow us to change the dice class so it can be used for a die with any number of sides:



        class Dice 
        public:

        using rng_t = std::mt19937; // default_random_engine or whatever...
        using output_t = rng_t::result_type;

        Dice(output_t sides, output_t seed = std::random_device()()):
        m_distribution(output_t 1 , sides),
        m_rng(seed)

        output_t roll()
        return m_distribution(m_rng);


        private:
        std::uniform_int_distribution<output_t> m_distribution;
        rng_t m_rng;
        ;

        ...

        Dice d(6);
        std::cout << d.roll() << std::endl;


        I believe the standard way to obtain a random seed is to use std::random_device as above.




        [naming]



        Technically, the word "dice" is a plural of the singular "die". I'd be tempted to rename Dice to Die, as the class represents only one of the things. I'd expect a "Dice" class to provide an interface for multiple dice, such as 2d6 or 3d4 rolls, etc.



        But others might disagree... :S






        share|improve this answer















        [code] m_



        One of the main benefits of using the m_ prefix is not having to put this-> in front of members!




        [design] overall



        While separating logic and presentation are important, I'd suggest it has more to do with ensuring that a class does only one thing (the "Single Responsibility Principle"), rather than with overall organisation.



        While the logic and view namespaces might be useful if there were lots of similar classes that needed to be split in exactly this way, I'm not sure they're necessary here. Also, I don't think that the State and GameState classes need to be in either (view::GameState contains both logic and view objects).



        Throughout the code, there are many places where data is hard-coded into a class. This generally indicates "unfinished" design / programming, and leads to duplication and missed opportunities for code reuse.




        [design] specifics



        I'd recommend the following:




        • view and logic namespaces - remove them for the reasons mentioned above.


        • logic::Dice - rename to just Dice.


        • view::Dice -

          • move the dice-specific hard-coded data definitions outside of the class.

          • split the sf::Sound member into a separate SoundEffect class.

          • rename the remainder to TexturedQuad or similar.



        • logic::Game - rename to GameLogic.


        • view::State and view::GameState - move out of view namespace (it's hard to say more without seeing full code).

        So we end up with the following architecture:



        class TexturedQuad 
        public:
        TexturedQuad(sf::Vector2 size, std::string const& textureFileName, sf::IntRect const& textureRect);

        void setPosition(sf::Vector2f const& position); // ??
        void setTextureRect(sf::IntRect const& textureRect);
        void draw(); // ??

        private:
        sf::Texture m_texture;
        sf::RectangleShape m_shape;
        ;

        class SoundEffect
        public:
        SoundEffect(std::string const& fileName);

        void play();

        private:
        sf::Sound m_sound;
        sf::SoundBuffer m_buffer;
        ;

        class State ... ;
        class GameState : public State
        ...

        GameLogic m_game;
        SoundEffect m_diceSoundEffect; // note, we only need one of these!
        TexturedQuad m_diceTexturedQuad; // and (probably) only one of these!
        ;

        class Dice ... ;
        class GameLogic
        ...

        Dice m_dice; // only one of these!

        int m_rollOne = 0; // store the current rolls in the class instead...
        int m_rollTwo = 0;
        ;


        By separating out the data, and passing it into constructors where necessary, we end up with generic classes that can be reused. We've also uncovered some duplication of class instances:



        • We only need one dice sound effect instance, not two.

        • We only need one textured quad, which can be repositioned and the texture coordinates changed to draw both dice. (Note: I don't know sfml, and am assuming that changing the position and "textureRect" are trivial operations that can be done in the rendering loop. You may still need two quads, if this isn't the case.)

        • Unless there's a need to reproduce two separate runs of dice rolls in the random number generator (i.e. use two separate seeds), we only need one dice instance, which we can roll twice.

        So the functions can be rewritten as:



        void GameState::rollTheDice() 
        m_game.rollTheDice();
        m_diceSoundEffect.play();


        void GameState::draw()

        m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollOne));
        m_diceTexturedQuad.setPosition(...);
        m_diceTexturedQuad.draw();

        m_diceTexturedQuad.setTextureRect(getTextureRectFromDiceRoll(m_game.m_rollTwo));
        m_diceTexturedQuad.setPosition(...);
        m_diceTexturedQuad.draw();

        ...


        void GameLogic::rollTheDice()
        m_throwsInCurrentTurn++;

        m_rollOne = m_dice.roll();
        m_rollTwo = m_dice.roll();

        m_totalRollResult += m_rollOne + m_rollTwo;

        if (m_rollOne == m_rollTwo)
        m_doublesInCurrentTurn++;



        and constructor initializer lists used to pass data into our generic classes, e.g.:



        GameState::GameState():
        m_game(),
        m_diceSoundEffect(DICE_SOUND),
        m_diceTexturedQuad(sf::Vector2f(DICE_WIDTH, DICE_HEIGHT), DICE_TEXTURE, sf::IntRect(0, 0, 55, 55))



        [design] dice class



        The same principles of removing data from the class allow us to change the dice class so it can be used for a die with any number of sides:



        class Dice 
        public:

        using rng_t = std::mt19937; // default_random_engine or whatever...
        using output_t = rng_t::result_type;

        Dice(output_t sides, output_t seed = std::random_device()()):
        m_distribution(output_t 1 , sides),
        m_rng(seed)

        output_t roll()
        return m_distribution(m_rng);


        private:
        std::uniform_int_distribution<output_t> m_distribution;
        rng_t m_rng;
        ;

        ...

        Dice d(6);
        std::cout << d.roll() << std::endl;


        I believe the standard way to obtain a random seed is to use std::random_device as above.




        [naming]



        Technically, the word "dice" is a plural of the singular "die". I'd be tempted to rename Dice to Die, as the class represents only one of the things. I'd expect a "Dice" class to provide an interface for multiple dice, such as 2d6 or 3d4 rolls, etc.



        But others might disagree... :S







        share|improve this answer















        share|improve this answer



        share|improve this answer








        edited Feb 27 at 11:41


























        answered Feb 27 at 11:35









        user673679

        1,092518




        1,092518






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f184431%2fsfml-dice-game-with-logic-presentation-separation%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Greedy Best First Search implementation in Rust

            Function to Return a JSON Like Objects Using VBA Collections and Arrays

            C++11 CLH Lock Implementation