Viewport for Card Match Game

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

favorite












This is the 2nd half of my Memory game. The first half can be found here. The repository can be found on Github.



I am using SFML 2.4.2. In this project I focused on a few key aspects of design and development.



  • Top-down Design

  • DRY & SRP

  • Encapsulation & Abstraction

In an effort to eliminate magic numbers I shoved all of my constexpr as well as my enums, a static logger and a static PRNG in a single header file. I don't know if this approach is best practice or not and would appreciate input. On one hand I could move the expressions to the appropriate headers where they are localized, but many of them are used repeatedly throughout the codebase. Should I localize the values that are used once and only keep a header file for the reused items (PRNG, Enums, etc...)?



note: in the previous answer it was recommended to me to use double over float. I chose not to do this as SFML takes floats for its parameters and the number of casts made the code unreadable. I could let the computer narrow implicitly but then I would need to turn of wX which I prefer to keep on. It would also lead to a significant number of warnings.



Expressions.h



#ifndef BRUGLESCO_MEMORY_EXPRESSIONS_H
#define BRUGLESCO_MEMORY_EXPRESSIONS_H

#include <fstream>
#include <random>

namespace bruglesco

static std::random_device rd;
static std::mt19937 generator(rd());
static std::ofstream debuggStream("DebugLog.txt"); // remove for release

constexpr unsigned screen_width = 1500u;
constexpr unsigned screen_height = 800u;

constexpr float menu_button_width = 256.f;
constexpr float menu_button_height = 256.f;

constexpr float play_button_x = 622.f;
constexpr float play_button_y = 96.f;
constexpr float six_button_x = 95.f;
constexpr float eight_button_x = 446.f;
constexpr float twelve_button_x = 797.f;
constexpr float sixteen_button_x = 1148.f;
constexpr float pair_button_y = 448.f;

constexpr float play_string_x_offset = 18.f;
constexpr float play_string_y_offset = 28.f;
constexpr float six_string_x_offset = 80.f;
constexpr float eight_string_x_offset = 40.f;
constexpr float twelve_string_x_offset = 15.f;
constexpr float sixteen_string_x_offset = 8.f;
constexpr float pair_string_x_offset = 40.f;
constexpr float pair_string_y_offset = 100.f;

constexpr float game_button_width = 128.f;
constexpr float game_button_height = 64.f;

constexpr float pause_x = 1244.f;
constexpr float reset_x = 1372.f;
constexpr float pause_offset = 12.f;
constexpr float reset_offset = 5.f;

constexpr float display_x = 1244.f;
constexpr float player_one_display_y = 64.f;
constexpr float player_two_display_y = 432.f;
constexpr float display_offset = 20.f;

constexpr float display_width = 256.f;
constexpr float display_height = 368.f;

constexpr float canvas_width = screen_width - display_width;
constexpr float canvas_height = static_cast<float>(screen_height);

constexpr float player_string_x = 1265.f;
constexpr float player_one_string_y = 72.f;
constexpr float player_two_string_y = 440.f;

constexpr float player_card_x = 1265.f;
constexpr float player_card_x_offset = 15.f;
constexpr float player_one_card_y = 120.f;
constexpr float player_two_card_y = 488.f;

constexpr float card_width = 128.f;
constexpr float card_height = 128.f;

constexpr unsigned max_pairs = 16u;

constexpr float win_width = screen_width - 300.f;
constexpr float win_height = screen_height - 150.f;
constexpr float win_x = 150.f;
constexpr float win_y = 75.f;
constexpr float winstring_x = 400.f;
constexpr float winstring_y = 100.f;
constexpr float player_winstring_x = winstring_x - 50.f;
constexpr float player_winstring_y = winstring_y + 200.f;

enum class DeckSize

six = 6,
eight = 8,
twelve = 12,
sixteen = 16
;

enum class CardState

unmatched,
checking,
matched
;

enum class menuMouseIn

none,
play,
six,
eight,
twelve,
sixteen
;

enum class gameMouseIn

none,
pause,
reset
;

enum class winState

none,
draw,
playerOne,
playerTwo
;


#endif // !BRUGLESCO_MEMORY_EXPRESSIONS_H



My main is intended to be small. Memory is the class that represents the entirety of the application.



Main.cpp



#include "Memory.h"

int main()

Memory memory;
memory.run();




The Memory class contains the ModelData and Viewport class. These classes represent the model logic and presentation logic respectively.



Memory.h



#ifndef BRUGLESCO_MEMORY_MEMORY_H
#define BRUGLESCO_MEMORY_MEMORY_H

#include "Expressions.h"
#include "ModelData.h"
#include "Viewport.h"

#include <SFMLGraphics.hpp>

class Memory

public:
void run();
private:
ModelData data;
Viewport view data ;
bool playing true ;

void input();

void update();

void draw();
;

#endif // !BRUGLESCO_MEMORY_MEMORY_H



The ModelData class was the main focus of the last post. In this one I'd like to focus on the Vieport



This class contains the window for rendering, as well as the MenuScreen and GameScreen classes. These are the only two screens in the program.



First the MenuScreen. It is a simple arrangement of buttons that are highlighted dynamically both on selection and mouseover. The trackMouse method is to check for mouseover, the input method responds to user input (in this case clicks), and the update method dynamically maintains your selected game size and highlights the buttons according to that selection and mouseover.



MenuScreen.h



#ifndef BRUGLESCO_MEMORY_MENUSCREEN_H
#define BRUGLESCO_MEMORY_MENUSCREEN_H

#include "Expressions.h"
#include "GameScreen.h"
#include "ModelData.h"

#include <SFMLGraphics.hpp>
class MenuScreen

public:
MenuScreen(ModelData& data, GameScreen& game, sf::Font& font);

void trackMouse(const sf::Vector2f& mousePos);

void input(const sf::Vector2f& mousePos);

void update();

void draw(sf::RenderWindow& window);
private:
ModelData& data;
GameScreen& game;
sf::Font& font;
sf::RectangleShape playButton sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
sf::Text playString "Play", font ;
sf::RectangleShape sixPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
sf::Text sixString "Six", font ;
sf::RectangleShape eightPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
sf::Text eightString "Eight", font ;
sf::RectangleShape twelvePairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
sf::Text twelveString "Twelve", font ;
sf::RectangleShape sixteenPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
sf::Text sixteenString "Sixteen", font ;
std::vector<sf::Text> pairString 4, "Pairs", font ;
bruglesco::menuMouseIn mouseIn bruglesco::menuMouseIn::none ;
bruglesco::DeckSize deckSize bruglesco::DeckSize::six ;

void highlightButton();
;
#endif // !BRUGLESCO_MEMORY_MENUSCREEN_H



MenuScreen.cpp



#include "MenuScreen.h"

MenuScreen::MenuScreen(ModelData& data, GameScreen& game, sf::Font& font) :
data data ,
game game ,
font font

playButton.setPosition(bruglesco::play_button_x, bruglesco::play_button_y);
playButton.setFillColor(sf::Color(120, 120, 120, 255));
playString.setPosition(bruglesco::play_button_x + bruglesco::play_string_x_offset, bruglesco::play_button_y + bruglesco::play_string_y_offset);
playString.setCharacterSize(150);
playString.setFillColor(sf::Color::Black);

sixPairs.setPosition(bruglesco::six_button_x, bruglesco::pair_button_y);
sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
sixString.setPosition(bruglesco::six_button_x + bruglesco::six_string_x_offset, bruglesco::pair_button_y);
sixString.setCharacterSize(100);
sixString.setFillColor(sf::Color::Red);
pairString[0].setPosition(bruglesco::six_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
pairString[0].setCharacterSize(100);
pairString[0].setFillColor(sf::Color::Red);

eightPairs.setPosition(bruglesco::eight_button_x, bruglesco::pair_button_y);
eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
eightString.setPosition(bruglesco::eight_button_x + bruglesco::eight_string_x_offset, bruglesco::pair_button_y);
eightString.setCharacterSize(100);
eightString.setFillColor(sf::Color::Black);
pairString[1].setPosition(bruglesco::eight_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
pairString[1].setCharacterSize(100);
pairString[1].setFillColor(sf::Color::Black);

twelvePairs.setPosition(bruglesco::twelve_button_x, bruglesco::pair_button_y);
twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
twelveString.setPosition(bruglesco::twelve_button_x + bruglesco::twelve_string_x_offset, bruglesco::pair_button_y);
twelveString.setCharacterSize(100);
twelveString.setFillColor(sf::Color::Black);
pairString[2].setPosition(bruglesco::twelve_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
pairString[2].setCharacterSize(100);
pairString[2].setFillColor(sf::Color::Black);

sixteenPairs.setPosition(bruglesco::sixteen_button_x, bruglesco::pair_button_y);
sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
sixteenString.setPosition(bruglesco::sixteen_button_x + bruglesco::sixteen_string_x_offset, bruglesco::pair_button_y);
sixteenString.setCharacterSize(100);
sixteenString.setFillColor(sf::Color::Black);
pairString[3].setPosition(bruglesco::sixteen_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
pairString[3].setCharacterSize(100);
pairString[3].setFillColor(sf::Color::Black);


void MenuScreen::trackMouse(const sf::Vector2f& mousePos)

if (playButton.getGlobalBounds().contains(mousePos))

mouseIn = bruglesco::menuMouseIn::play;

else if (sixPairs.getGlobalBounds().contains(mousePos))

mouseIn = bruglesco::menuMouseIn::six;

else if (eightPairs.getGlobalBounds().contains(mousePos))

mouseIn = bruglesco::menuMouseIn::eight;

else if (twelvePairs.getGlobalBounds().contains(mousePos))

mouseIn = bruglesco::menuMouseIn::twelve;

else if (sixteenPairs.getGlobalBounds().contains(mousePos))

mouseIn = bruglesco::menuMouseIn::sixteen;

else

mouseIn = bruglesco::menuMouseIn::none;



void MenuScreen::input(const sf::Vector2f& mousePos)

if (playButton.getGlobalBounds().contains(mousePos))

data.setSize(deckSize);
data.play();
game.setGame();
mouseIn = bruglesco::menuMouseIn::none;

else if (sixPairs.getGlobalBounds().contains(mousePos))

deckSize = bruglesco::DeckSize::six;
data.setSize(deckSize);

else if (eightPairs.getGlobalBounds().contains(mousePos))

deckSize = bruglesco::DeckSize::eight;
data.setSize(deckSize);

else if (twelvePairs.getGlobalBounds().contains(mousePos))

deckSize = bruglesco::DeckSize::twelve;
data.setSize(deckSize);

else if (sixteenPairs.getGlobalBounds().contains(mousePos))

deckSize = bruglesco::DeckSize::sixteen;
data.setSize(deckSize);



void MenuScreen::update()

highlightButton();


void MenuScreen::draw(sf::RenderWindow& window)

window.draw(playButton);
window.draw(playString);
window.draw(sixPairs);
window.draw(sixString);
window.draw(eightPairs);
window.draw(eightString);
window.draw(twelvePairs);
window.draw(twelveString);
window.draw(sixteenPairs);
window.draw(sixteenString);
for (std::vector<sf::Text>::iterator string = pairString.begin(); string != pairString.end(); ++string)

window.draw(*string);



void MenuScreen::highlightButton()

if (mouseIn == bruglesco::menuMouseIn::play)

playButton.setFillColor(sf::Color(60, 60, 60, 255));
playString.setFillColor(sf::Color::Red);

else

playButton.setFillColor(sf::Color(120, 120, 120, 255));
playString.setFillColor(sf::Color::Black);


if (mouseIn == bruglesco::menuMouseIn::six)

sixPairs.setFillColor(sf::Color(60, 60, 60, 255));
sixString.setFillColor(sf::Color::Red);
pairString[0].setFillColor(sf::Color::Red);

else if (deckSize == bruglesco::DeckSize::six)

sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
sixString.setFillColor(sf::Color::Red);
pairString[0].setFillColor(sf::Color::Red);

else

sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
sixString.setFillColor(sf::Color::Black);
pairString[0].setFillColor(sf::Color::Black);


if (mouseIn == bruglesco::menuMouseIn::eight)

eightPairs.setFillColor(sf::Color(60, 60, 60, 255));
eightString.setFillColor(sf::Color::Red);
pairString[1].setFillColor(sf::Color::Red);

else if (deckSize == bruglesco::DeckSize::eight)

eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
eightString.setFillColor(sf::Color::Red);
pairString[1].setFillColor(sf::Color::Red);

else

eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
eightString.setFillColor(sf::Color::Black);
pairString[1].setFillColor(sf::Color::Black);


if (mouseIn == bruglesco::menuMouseIn::twelve)

twelvePairs.setFillColor(sf::Color(60, 60, 60, 255));
twelveString.setFillColor(sf::Color::Red);
pairString[2].setFillColor(sf::Color::Red);

else if (deckSize == bruglesco::DeckSize::twelve)

twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
twelveString.setFillColor(sf::Color::Red);
pairString[2].setFillColor(sf::Color::Red);

else

twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
twelveString.setFillColor(sf::Color::Black);
pairString[2].setFillColor(sf::Color::Black);


if (mouseIn == bruglesco::menuMouseIn::sixteen)

sixteenPairs.setFillColor(sf::Color(60, 60, 60, 255));
sixteenString.setFillColor(sf::Color::Red);
pairString[3].setFillColor(sf::Color::Red);

else if (deckSize == bruglesco::DeckSize::sixteen)

sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
sixteenString.setFillColor(sf::Color::Red);
pairString[3].setFillColor(sf::Color::Red);

else

sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
sixteenString.setFillColor(sf::Color::Black);
pairString[3].setFillColor(sf::Color::Black);





Next is the GameScreen class. Here is the meat of the presentation logic. The HUDisplay class is a subsection that updates the players score as strings to display them. It also maintains a space to draw matched cards in a stack in the players "hand".



HUDisplay.h



#ifndef BRUGLESCO_MEMORY_HUDISPLAY_H
#define BRUGLESCO_MEMORY_HUDISPLAY_H

#include "Expressions.h"
#include "ModelData.h"

#include <SFMLGraphics.hpp>

#include <string>

class HUDisplay

public:
HUDisplay(ModelData& data, sf::Font& font);

void update();

void draw(sf::RenderWindow& window);
private:
ModelData& data;
sf::Font& font;
sf::RectangleShape playerOneDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
sf::RectangleShape playerTwoDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
unsigned playerOneScore 0 ;
sf::Text playerOne "Player One: " + std::to_string(playerOneScore), font ;
unsigned playerTwoScore 0 ;
sf::Text playerTwo "Player Two: " + std::to_string(playerTwoScore), font ;
;

#endif // !BRUGLESCO_MEMORY_HUDISPLAY_H



HUDisplay.cpp



#include "HUDisplay.h"

HUDisplay::HUDisplay(ModelData& data, sf::Font& font) :
data data ,
font font

playerOneDisplay.setPosition(bruglesco::display_x, bruglesco::player_one_display_y);
playerOneDisplay.setFillColor(sf::Color(120, 120, 120, 255));
playerTwoDisplay.setPosition(bruglesco::display_x, bruglesco::player_two_display_y);
playerTwoDisplay.setFillColor(sf::Color(120, 120, 120, 255));
playerOne.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_one_display_y);
playerOne.setCharacterSize(50);
playerOne.setFillColor(sf::Color::Red);
playerTwo.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_two_display_y);
playerTwo.setCharacterSize(50);
playerTwo.setFillColor(sf::Color::Black);


void HUDisplay::update()

if (data.playerOneTurn())

playerOneScore = data.getPlayer().getScore();
playerOne.setString("Player One: " + std::to_string(playerOneScore));
playerOne.setFillColor(sf::Color::Red);
playerTwo.setFillColor(sf::Color::Black);

else

playerTwoScore = data.getPlayer().getScore();
playerTwo.setString("Player Two: " + std::to_string(playerTwoScore));
playerOne.setFillColor(sf::Color::Black);
playerTwo.setFillColor(sf::Color::Red);



void HUDisplay::draw(sf::RenderWindow& window)

window.draw(playerOneDisplay);
window.draw(playerOne);
window.draw(playerTwoDisplay);
window.draw(playerTwo);




The trackMouse method here works the same as in the Menu. The input method is extended to registering clicks on card images. The logic is then passed over to the Model class which is maintained as a visitor to the View and passed in as a dependency. The positionCards function is able to position the cards dynamically based on the size of deck the player chose on the menu screen. The makeCards function creates the number of display objects based on deck size.
The matchFailDelay is an attempt at keeping the images displayed for a short period of time after a failed match. endFailDelay updates the Model so the display can update accordingly. moveMatched sets the position of matched cards according to the player who made the match and how many they've made. adjustTexture is done this way to consistently vary the images used and to swap back and forth between face up and face down.



GameScreen.h



#ifndef BRUGLESCO_MEMORY_GAMESCREEN_H
#define BRUGLESCO_MEMORY_GAMESCREEN_H

#include "Card.h"
#include "Expressions.h"
#include "HUDisplay.h"
#include "ModelData.h"

#include <SFMLGraphics.hpp>

#include <vector>

class GameScreen

public:
GameScreen(ModelData& data, sf::Font& font);

void setGame();

void trackMouse(const sf::Vector2f& mousePos);

void input(const sf::Vector2f& mousePos);

void update();

void draw(sf::RenderWindow& window);
private:
ModelData& data;
sf::Font& font;
sf::Texture cardMap;
sf::RectangleShape pauseButton sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
sf::Text pauseString "Pause", font ;
sf::RectangleShape returnToMain sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
sf::Text resetString "Return", font ;
HUDisplay hud data, font ;
sf::RectangleShape pauseForeground sf::Vector2f(static_cast<float>(bruglesco::screen_width), static_cast<float>(bruglesco::screen_height)) ;
sf::RectangleShape endGameForeground sf::Vector2f(bruglesco::win_width, bruglesco::win_height) ;
sf::Text winString "Winner", font ;
sf::Text playerWinString "", font ;
std::vector<sf::RectangleShape> deck;
bruglesco::gameMouseIn mouseIn bruglesco::gameMouseIn::none ;
bool paused false ;
std::vector<unsigned> imageIdentifiers;
unsigned delay 0 ;

void makeCards();

void positionCards();

void highlightButtons();

void matchFailDelay();

void endFailDelay();

void moveMatched();

void adjustTexture();
;

#endif // !BRUGLESCO_MEMORY_GAMESCREEN_H



GameScreen.cpp



#include "GameScreen.h"

GameScreen::GameScreen(ModelData& data, sf::Font& font) :
data data ,
font font

if (!cardMap.loadFromFile("spritesheet.png"))

bruglesco::debuggStream << "you aren't loading your textures"; // remove for release

pauseButton.setPosition(bruglesco::pause_x, 0);
pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
pauseString.setPosition(bruglesco::pause_x + bruglesco::pause_offset, 0);
pauseString.setCharacterSize(50);
pauseString.setFillColor(sf::Color::Black);
returnToMain.setPosition(bruglesco::reset_x, 0);
returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
resetString.setPosition(bruglesco::reset_x + bruglesco::reset_offset, 0);
resetString.setCharacterSize(50);
resetString.setFillColor(sf::Color::Black);
pauseForeground.setFillColor(sf::Color(120, 120, 120, 120));
endGameForeground.setPosition(bruglesco::win_x, bruglesco::win_y);
endGameForeground.setFillColor(sf::Color(90, 90, 90, 180));
winString.setPosition(bruglesco::winstring_x, bruglesco::winstring_y);
winString.setCharacterSize(250);
winString.setFillColor(sf::Color::Green);
playerWinString.setPosition(bruglesco::player_winstring_x, bruglesco::player_winstring_y);
playerWinString.setCharacterSize(250);
playerWinString.setFillColor(sf::Color::Green);
for (unsigned i = 0; i < bruglesco::max_pairs; ++i)

imageIdentifiers.push_back(i);



void GameScreen::setGame()

std::shuffle(std::begin(imageIdentifiers), std::end(imageIdentifiers), bruglesco::generator);
makeCards();
positionCards();


void GameScreen::trackMouse(const sf::Vector2f& mousePos)

if (pauseButton.getGlobalBounds().contains(mousePos))

mouseIn = bruglesco::gameMouseIn::pause;

else if (returnToMain.getGlobalBounds().contains(mousePos))

mouseIn = bruglesco::gameMouseIn::reset;

else

mouseIn = bruglesco::gameMouseIn::none;



void GameScreen::input(const sf::Vector2f& mousePos)

if (pauseButton.getGlobalBounds().contains(mousePos))

if (!data.isOver())

paused = !paused;


else if (!paused && returnToMain.getGlobalBounds().contains(mousePos))

data.quit();

else if (!paused && delay == 0)

for (std::pair<std::vector<Card>::iterator, std::vector<sf::RectangleShape>::iterator> card(data.getDeck().begin(), deck.begin());
card.first != data.getDeck().end() && card.second != deck.end(); ++card.first, ++card.second)

if (card.second->getGlobalBounds().contains(mousePos))

card.first->flip();





void GameScreen::update()

highlightButtons();
matchFailDelay();
moveMatched();
adjustTexture();
hud.update();

if (delay == 1)

endFailDelay();

if (delay > 0)

--delay;


if (data.isOver())

playerWinString.setString("Player " + std::to_string(data.getPlayer().getIdentity()));



void GameScreen::draw(sf::RenderWindow& window)

window.draw(pauseButton);
window.draw(pauseString);
window.draw(returnToMain);
window.draw(resetString);
hud.draw(window);
for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

window.draw(*card);


if (paused)

window.draw(pauseForeground);


if (data.isOver())

window.draw(endGameForeground);
window.draw(winString);
window.draw(playerWinString);



void GameScreen::makeCards()

deck.clear();
unsigned k = 0;
for (std::vector<Card>::iterator card = data.getDeck().begin(); card != data.getDeck().end(); ++card)

deck.push_back(sf::RectangleShape(sf::Vector2f(bruglesco::card_width, bruglesco::card_height)));
deck[k].setTexture(&cardMap);
deck[k].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));
++k;



void GameScreen::positionCards()

unsigned rows = 0;
for (unsigned i = 2; i * i <= deck.size(); ++i)

if (deck.size() % i == 0)

rows = i;


unsigned columns = deck.size() / rows;

float padding_x = (bruglesco::canvas_width - columns * 128.f) / (columns + 1);
float padding_y = (bruglesco::canvas_height - rows * 128.f) / (rows + 1);
float x = padding_x;
float y = padding_y;
unsigned counter = 1;
for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

card->setPosition(x, y);
if (counter < columns)

++counter;
x += padding_x + bruglesco::card_width;

else

counter = 1;
x = padding_x;
y += padding_y + bruglesco::card_height;




void GameScreen::highlightButtons()
mouseIn == bruglesco::gameMouseIn::pause)

pauseButton.setFillColor(sf::Color(60, 60, 60, 255));
pauseString.setFillColor(sf::Color::Red);

else

pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
pauseString.setFillColor(sf::Color::Black);


if (mouseIn == bruglesco::gameMouseIn::reset)

returnToMain.setFillColor(sf::Color(60, 60, 60, 255));
resetString.setFillColor(sf::Color::Red);

else

returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
resetString.setFillColor(sf::Color::Black);



void GameScreen::matchFailDelay()

if (delay == 0 && data.getFailedCards().size() == 2)

delay = 240;



void GameScreen::endFailDelay()

data.resetDeck();


void GameScreen::moveMatched()

if (data.getMatchedCards().size() == 2)

float x = bruglesco::player_card_x + bruglesco::player_card_x_offset * data.getPlayer().getScore();
float y;
if (data.playerOneTurn())

y = bruglesco::player_one_card_y;

else

y = bruglesco::player_two_card_y;


for (unsigned i = 0; i < data.getMatchedCards().size(); ++i)

deck[data.getMatchedCards()[i]].setPosition(x, y);

data.resetDeck();



void GameScreen::adjustTexture()

for (unsigned i = 0; i < data.getDeck().size(); ++i)

if (data.getDeck()[i].checkState() == bruglesco::CardState::unmatched)

deck[i].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));

else

deck[i].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[i].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));


for (unsigned i = 0; i < data.getFailedCards().size(); ++i)

deck[data.getFailedCards()[i]].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[data.getFailedCards()[i]].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));





I tried to be a little more thorough in my description of my code this time around but that is not a skill I am great at yet. I hope that if you need any further explanation you will leave a comment and I will provide it.



If I have any particular concerns for the code it would be the GameScreen class. It just seems to be large enough to worry. Should I try to abstract it further? any suggestions how? Of course I want any suggestions on any aspect of the code.







share|improve this question



























    up vote
    4
    down vote

    favorite












    This is the 2nd half of my Memory game. The first half can be found here. The repository can be found on Github.



    I am using SFML 2.4.2. In this project I focused on a few key aspects of design and development.



    • Top-down Design

    • DRY & SRP

    • Encapsulation & Abstraction

    In an effort to eliminate magic numbers I shoved all of my constexpr as well as my enums, a static logger and a static PRNG in a single header file. I don't know if this approach is best practice or not and would appreciate input. On one hand I could move the expressions to the appropriate headers where they are localized, but many of them are used repeatedly throughout the codebase. Should I localize the values that are used once and only keep a header file for the reused items (PRNG, Enums, etc...)?



    note: in the previous answer it was recommended to me to use double over float. I chose not to do this as SFML takes floats for its parameters and the number of casts made the code unreadable. I could let the computer narrow implicitly but then I would need to turn of wX which I prefer to keep on. It would also lead to a significant number of warnings.



    Expressions.h



    #ifndef BRUGLESCO_MEMORY_EXPRESSIONS_H
    #define BRUGLESCO_MEMORY_EXPRESSIONS_H

    #include <fstream>
    #include <random>

    namespace bruglesco

    static std::random_device rd;
    static std::mt19937 generator(rd());
    static std::ofstream debuggStream("DebugLog.txt"); // remove for release

    constexpr unsigned screen_width = 1500u;
    constexpr unsigned screen_height = 800u;

    constexpr float menu_button_width = 256.f;
    constexpr float menu_button_height = 256.f;

    constexpr float play_button_x = 622.f;
    constexpr float play_button_y = 96.f;
    constexpr float six_button_x = 95.f;
    constexpr float eight_button_x = 446.f;
    constexpr float twelve_button_x = 797.f;
    constexpr float sixteen_button_x = 1148.f;
    constexpr float pair_button_y = 448.f;

    constexpr float play_string_x_offset = 18.f;
    constexpr float play_string_y_offset = 28.f;
    constexpr float six_string_x_offset = 80.f;
    constexpr float eight_string_x_offset = 40.f;
    constexpr float twelve_string_x_offset = 15.f;
    constexpr float sixteen_string_x_offset = 8.f;
    constexpr float pair_string_x_offset = 40.f;
    constexpr float pair_string_y_offset = 100.f;

    constexpr float game_button_width = 128.f;
    constexpr float game_button_height = 64.f;

    constexpr float pause_x = 1244.f;
    constexpr float reset_x = 1372.f;
    constexpr float pause_offset = 12.f;
    constexpr float reset_offset = 5.f;

    constexpr float display_x = 1244.f;
    constexpr float player_one_display_y = 64.f;
    constexpr float player_two_display_y = 432.f;
    constexpr float display_offset = 20.f;

    constexpr float display_width = 256.f;
    constexpr float display_height = 368.f;

    constexpr float canvas_width = screen_width - display_width;
    constexpr float canvas_height = static_cast<float>(screen_height);

    constexpr float player_string_x = 1265.f;
    constexpr float player_one_string_y = 72.f;
    constexpr float player_two_string_y = 440.f;

    constexpr float player_card_x = 1265.f;
    constexpr float player_card_x_offset = 15.f;
    constexpr float player_one_card_y = 120.f;
    constexpr float player_two_card_y = 488.f;

    constexpr float card_width = 128.f;
    constexpr float card_height = 128.f;

    constexpr unsigned max_pairs = 16u;

    constexpr float win_width = screen_width - 300.f;
    constexpr float win_height = screen_height - 150.f;
    constexpr float win_x = 150.f;
    constexpr float win_y = 75.f;
    constexpr float winstring_x = 400.f;
    constexpr float winstring_y = 100.f;
    constexpr float player_winstring_x = winstring_x - 50.f;
    constexpr float player_winstring_y = winstring_y + 200.f;

    enum class DeckSize

    six = 6,
    eight = 8,
    twelve = 12,
    sixteen = 16
    ;

    enum class CardState

    unmatched,
    checking,
    matched
    ;

    enum class menuMouseIn

    none,
    play,
    six,
    eight,
    twelve,
    sixteen
    ;

    enum class gameMouseIn

    none,
    pause,
    reset
    ;

    enum class winState

    none,
    draw,
    playerOne,
    playerTwo
    ;


    #endif // !BRUGLESCO_MEMORY_EXPRESSIONS_H



    My main is intended to be small. Memory is the class that represents the entirety of the application.



    Main.cpp



    #include "Memory.h"

    int main()

    Memory memory;
    memory.run();




    The Memory class contains the ModelData and Viewport class. These classes represent the model logic and presentation logic respectively.



    Memory.h



    #ifndef BRUGLESCO_MEMORY_MEMORY_H
    #define BRUGLESCO_MEMORY_MEMORY_H

    #include "Expressions.h"
    #include "ModelData.h"
    #include "Viewport.h"

    #include <SFMLGraphics.hpp>

    class Memory

    public:
    void run();
    private:
    ModelData data;
    Viewport view data ;
    bool playing true ;

    void input();

    void update();

    void draw();
    ;

    #endif // !BRUGLESCO_MEMORY_MEMORY_H



    The ModelData class was the main focus of the last post. In this one I'd like to focus on the Vieport



    This class contains the window for rendering, as well as the MenuScreen and GameScreen classes. These are the only two screens in the program.



    First the MenuScreen. It is a simple arrangement of buttons that are highlighted dynamically both on selection and mouseover. The trackMouse method is to check for mouseover, the input method responds to user input (in this case clicks), and the update method dynamically maintains your selected game size and highlights the buttons according to that selection and mouseover.



    MenuScreen.h



    #ifndef BRUGLESCO_MEMORY_MENUSCREEN_H
    #define BRUGLESCO_MEMORY_MENUSCREEN_H

    #include "Expressions.h"
    #include "GameScreen.h"
    #include "ModelData.h"

    #include <SFMLGraphics.hpp>
    class MenuScreen

    public:
    MenuScreen(ModelData& data, GameScreen& game, sf::Font& font);

    void trackMouse(const sf::Vector2f& mousePos);

    void input(const sf::Vector2f& mousePos);

    void update();

    void draw(sf::RenderWindow& window);
    private:
    ModelData& data;
    GameScreen& game;
    sf::Font& font;
    sf::RectangleShape playButton sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
    sf::Text playString "Play", font ;
    sf::RectangleShape sixPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
    sf::Text sixString "Six", font ;
    sf::RectangleShape eightPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
    sf::Text eightString "Eight", font ;
    sf::RectangleShape twelvePairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
    sf::Text twelveString "Twelve", font ;
    sf::RectangleShape sixteenPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
    sf::Text sixteenString "Sixteen", font ;
    std::vector<sf::Text> pairString 4, "Pairs", font ;
    bruglesco::menuMouseIn mouseIn bruglesco::menuMouseIn::none ;
    bruglesco::DeckSize deckSize bruglesco::DeckSize::six ;

    void highlightButton();
    ;
    #endif // !BRUGLESCO_MEMORY_MENUSCREEN_H



    MenuScreen.cpp



    #include "MenuScreen.h"

    MenuScreen::MenuScreen(ModelData& data, GameScreen& game, sf::Font& font) :
    data data ,
    game game ,
    font font

    playButton.setPosition(bruglesco::play_button_x, bruglesco::play_button_y);
    playButton.setFillColor(sf::Color(120, 120, 120, 255));
    playString.setPosition(bruglesco::play_button_x + bruglesco::play_string_x_offset, bruglesco::play_button_y + bruglesco::play_string_y_offset);
    playString.setCharacterSize(150);
    playString.setFillColor(sf::Color::Black);

    sixPairs.setPosition(bruglesco::six_button_x, bruglesco::pair_button_y);
    sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
    sixString.setPosition(bruglesco::six_button_x + bruglesco::six_string_x_offset, bruglesco::pair_button_y);
    sixString.setCharacterSize(100);
    sixString.setFillColor(sf::Color::Red);
    pairString[0].setPosition(bruglesco::six_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
    pairString[0].setCharacterSize(100);
    pairString[0].setFillColor(sf::Color::Red);

    eightPairs.setPosition(bruglesco::eight_button_x, bruglesco::pair_button_y);
    eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
    eightString.setPosition(bruglesco::eight_button_x + bruglesco::eight_string_x_offset, bruglesco::pair_button_y);
    eightString.setCharacterSize(100);
    eightString.setFillColor(sf::Color::Black);
    pairString[1].setPosition(bruglesco::eight_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
    pairString[1].setCharacterSize(100);
    pairString[1].setFillColor(sf::Color::Black);

    twelvePairs.setPosition(bruglesco::twelve_button_x, bruglesco::pair_button_y);
    twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
    twelveString.setPosition(bruglesco::twelve_button_x + bruglesco::twelve_string_x_offset, bruglesco::pair_button_y);
    twelveString.setCharacterSize(100);
    twelveString.setFillColor(sf::Color::Black);
    pairString[2].setPosition(bruglesco::twelve_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
    pairString[2].setCharacterSize(100);
    pairString[2].setFillColor(sf::Color::Black);

    sixteenPairs.setPosition(bruglesco::sixteen_button_x, bruglesco::pair_button_y);
    sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
    sixteenString.setPosition(bruglesco::sixteen_button_x + bruglesco::sixteen_string_x_offset, bruglesco::pair_button_y);
    sixteenString.setCharacterSize(100);
    sixteenString.setFillColor(sf::Color::Black);
    pairString[3].setPosition(bruglesco::sixteen_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
    pairString[3].setCharacterSize(100);
    pairString[3].setFillColor(sf::Color::Black);


    void MenuScreen::trackMouse(const sf::Vector2f& mousePos)

    if (playButton.getGlobalBounds().contains(mousePos))

    mouseIn = bruglesco::menuMouseIn::play;

    else if (sixPairs.getGlobalBounds().contains(mousePos))

    mouseIn = bruglesco::menuMouseIn::six;

    else if (eightPairs.getGlobalBounds().contains(mousePos))

    mouseIn = bruglesco::menuMouseIn::eight;

    else if (twelvePairs.getGlobalBounds().contains(mousePos))

    mouseIn = bruglesco::menuMouseIn::twelve;

    else if (sixteenPairs.getGlobalBounds().contains(mousePos))

    mouseIn = bruglesco::menuMouseIn::sixteen;

    else

    mouseIn = bruglesco::menuMouseIn::none;



    void MenuScreen::input(const sf::Vector2f& mousePos)

    if (playButton.getGlobalBounds().contains(mousePos))

    data.setSize(deckSize);
    data.play();
    game.setGame();
    mouseIn = bruglesco::menuMouseIn::none;

    else if (sixPairs.getGlobalBounds().contains(mousePos))

    deckSize = bruglesco::DeckSize::six;
    data.setSize(deckSize);

    else if (eightPairs.getGlobalBounds().contains(mousePos))

    deckSize = bruglesco::DeckSize::eight;
    data.setSize(deckSize);

    else if (twelvePairs.getGlobalBounds().contains(mousePos))

    deckSize = bruglesco::DeckSize::twelve;
    data.setSize(deckSize);

    else if (sixteenPairs.getGlobalBounds().contains(mousePos))

    deckSize = bruglesco::DeckSize::sixteen;
    data.setSize(deckSize);



    void MenuScreen::update()

    highlightButton();


    void MenuScreen::draw(sf::RenderWindow& window)

    window.draw(playButton);
    window.draw(playString);
    window.draw(sixPairs);
    window.draw(sixString);
    window.draw(eightPairs);
    window.draw(eightString);
    window.draw(twelvePairs);
    window.draw(twelveString);
    window.draw(sixteenPairs);
    window.draw(sixteenString);
    for (std::vector<sf::Text>::iterator string = pairString.begin(); string != pairString.end(); ++string)

    window.draw(*string);



    void MenuScreen::highlightButton()

    if (mouseIn == bruglesco::menuMouseIn::play)

    playButton.setFillColor(sf::Color(60, 60, 60, 255));
    playString.setFillColor(sf::Color::Red);

    else

    playButton.setFillColor(sf::Color(120, 120, 120, 255));
    playString.setFillColor(sf::Color::Black);


    if (mouseIn == bruglesco::menuMouseIn::six)

    sixPairs.setFillColor(sf::Color(60, 60, 60, 255));
    sixString.setFillColor(sf::Color::Red);
    pairString[0].setFillColor(sf::Color::Red);

    else if (deckSize == bruglesco::DeckSize::six)

    sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
    sixString.setFillColor(sf::Color::Red);
    pairString[0].setFillColor(sf::Color::Red);

    else

    sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
    sixString.setFillColor(sf::Color::Black);
    pairString[0].setFillColor(sf::Color::Black);


    if (mouseIn == bruglesco::menuMouseIn::eight)

    eightPairs.setFillColor(sf::Color(60, 60, 60, 255));
    eightString.setFillColor(sf::Color::Red);
    pairString[1].setFillColor(sf::Color::Red);

    else if (deckSize == bruglesco::DeckSize::eight)

    eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
    eightString.setFillColor(sf::Color::Red);
    pairString[1].setFillColor(sf::Color::Red);

    else

    eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
    eightString.setFillColor(sf::Color::Black);
    pairString[1].setFillColor(sf::Color::Black);


    if (mouseIn == bruglesco::menuMouseIn::twelve)

    twelvePairs.setFillColor(sf::Color(60, 60, 60, 255));
    twelveString.setFillColor(sf::Color::Red);
    pairString[2].setFillColor(sf::Color::Red);

    else if (deckSize == bruglesco::DeckSize::twelve)

    twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
    twelveString.setFillColor(sf::Color::Red);
    pairString[2].setFillColor(sf::Color::Red);

    else

    twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
    twelveString.setFillColor(sf::Color::Black);
    pairString[2].setFillColor(sf::Color::Black);


    if (mouseIn == bruglesco::menuMouseIn::sixteen)

    sixteenPairs.setFillColor(sf::Color(60, 60, 60, 255));
    sixteenString.setFillColor(sf::Color::Red);
    pairString[3].setFillColor(sf::Color::Red);

    else if (deckSize == bruglesco::DeckSize::sixteen)

    sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
    sixteenString.setFillColor(sf::Color::Red);
    pairString[3].setFillColor(sf::Color::Red);

    else

    sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
    sixteenString.setFillColor(sf::Color::Black);
    pairString[3].setFillColor(sf::Color::Black);





    Next is the GameScreen class. Here is the meat of the presentation logic. The HUDisplay class is a subsection that updates the players score as strings to display them. It also maintains a space to draw matched cards in a stack in the players "hand".



    HUDisplay.h



    #ifndef BRUGLESCO_MEMORY_HUDISPLAY_H
    #define BRUGLESCO_MEMORY_HUDISPLAY_H

    #include "Expressions.h"
    #include "ModelData.h"

    #include <SFMLGraphics.hpp>

    #include <string>

    class HUDisplay

    public:
    HUDisplay(ModelData& data, sf::Font& font);

    void update();

    void draw(sf::RenderWindow& window);
    private:
    ModelData& data;
    sf::Font& font;
    sf::RectangleShape playerOneDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
    sf::RectangleShape playerTwoDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
    unsigned playerOneScore 0 ;
    sf::Text playerOne "Player One: " + std::to_string(playerOneScore), font ;
    unsigned playerTwoScore 0 ;
    sf::Text playerTwo "Player Two: " + std::to_string(playerTwoScore), font ;
    ;

    #endif // !BRUGLESCO_MEMORY_HUDISPLAY_H



    HUDisplay.cpp



    #include "HUDisplay.h"

    HUDisplay::HUDisplay(ModelData& data, sf::Font& font) :
    data data ,
    font font

    playerOneDisplay.setPosition(bruglesco::display_x, bruglesco::player_one_display_y);
    playerOneDisplay.setFillColor(sf::Color(120, 120, 120, 255));
    playerTwoDisplay.setPosition(bruglesco::display_x, bruglesco::player_two_display_y);
    playerTwoDisplay.setFillColor(sf::Color(120, 120, 120, 255));
    playerOne.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_one_display_y);
    playerOne.setCharacterSize(50);
    playerOne.setFillColor(sf::Color::Red);
    playerTwo.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_two_display_y);
    playerTwo.setCharacterSize(50);
    playerTwo.setFillColor(sf::Color::Black);


    void HUDisplay::update()

    if (data.playerOneTurn())

    playerOneScore = data.getPlayer().getScore();
    playerOne.setString("Player One: " + std::to_string(playerOneScore));
    playerOne.setFillColor(sf::Color::Red);
    playerTwo.setFillColor(sf::Color::Black);

    else

    playerTwoScore = data.getPlayer().getScore();
    playerTwo.setString("Player Two: " + std::to_string(playerTwoScore));
    playerOne.setFillColor(sf::Color::Black);
    playerTwo.setFillColor(sf::Color::Red);



    void HUDisplay::draw(sf::RenderWindow& window)

    window.draw(playerOneDisplay);
    window.draw(playerOne);
    window.draw(playerTwoDisplay);
    window.draw(playerTwo);




    The trackMouse method here works the same as in the Menu. The input method is extended to registering clicks on card images. The logic is then passed over to the Model class which is maintained as a visitor to the View and passed in as a dependency. The positionCards function is able to position the cards dynamically based on the size of deck the player chose on the menu screen. The makeCards function creates the number of display objects based on deck size.
    The matchFailDelay is an attempt at keeping the images displayed for a short period of time after a failed match. endFailDelay updates the Model so the display can update accordingly. moveMatched sets the position of matched cards according to the player who made the match and how many they've made. adjustTexture is done this way to consistently vary the images used and to swap back and forth between face up and face down.



    GameScreen.h



    #ifndef BRUGLESCO_MEMORY_GAMESCREEN_H
    #define BRUGLESCO_MEMORY_GAMESCREEN_H

    #include "Card.h"
    #include "Expressions.h"
    #include "HUDisplay.h"
    #include "ModelData.h"

    #include <SFMLGraphics.hpp>

    #include <vector>

    class GameScreen

    public:
    GameScreen(ModelData& data, sf::Font& font);

    void setGame();

    void trackMouse(const sf::Vector2f& mousePos);

    void input(const sf::Vector2f& mousePos);

    void update();

    void draw(sf::RenderWindow& window);
    private:
    ModelData& data;
    sf::Font& font;
    sf::Texture cardMap;
    sf::RectangleShape pauseButton sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
    sf::Text pauseString "Pause", font ;
    sf::RectangleShape returnToMain sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
    sf::Text resetString "Return", font ;
    HUDisplay hud data, font ;
    sf::RectangleShape pauseForeground sf::Vector2f(static_cast<float>(bruglesco::screen_width), static_cast<float>(bruglesco::screen_height)) ;
    sf::RectangleShape endGameForeground sf::Vector2f(bruglesco::win_width, bruglesco::win_height) ;
    sf::Text winString "Winner", font ;
    sf::Text playerWinString "", font ;
    std::vector<sf::RectangleShape> deck;
    bruglesco::gameMouseIn mouseIn bruglesco::gameMouseIn::none ;
    bool paused false ;
    std::vector<unsigned> imageIdentifiers;
    unsigned delay 0 ;

    void makeCards();

    void positionCards();

    void highlightButtons();

    void matchFailDelay();

    void endFailDelay();

    void moveMatched();

    void adjustTexture();
    ;

    #endif // !BRUGLESCO_MEMORY_GAMESCREEN_H



    GameScreen.cpp



    #include "GameScreen.h"

    GameScreen::GameScreen(ModelData& data, sf::Font& font) :
    data data ,
    font font

    if (!cardMap.loadFromFile("spritesheet.png"))

    bruglesco::debuggStream << "you aren't loading your textures"; // remove for release

    pauseButton.setPosition(bruglesco::pause_x, 0);
    pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
    pauseString.setPosition(bruglesco::pause_x + bruglesco::pause_offset, 0);
    pauseString.setCharacterSize(50);
    pauseString.setFillColor(sf::Color::Black);
    returnToMain.setPosition(bruglesco::reset_x, 0);
    returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
    resetString.setPosition(bruglesco::reset_x + bruglesco::reset_offset, 0);
    resetString.setCharacterSize(50);
    resetString.setFillColor(sf::Color::Black);
    pauseForeground.setFillColor(sf::Color(120, 120, 120, 120));
    endGameForeground.setPosition(bruglesco::win_x, bruglesco::win_y);
    endGameForeground.setFillColor(sf::Color(90, 90, 90, 180));
    winString.setPosition(bruglesco::winstring_x, bruglesco::winstring_y);
    winString.setCharacterSize(250);
    winString.setFillColor(sf::Color::Green);
    playerWinString.setPosition(bruglesco::player_winstring_x, bruglesco::player_winstring_y);
    playerWinString.setCharacterSize(250);
    playerWinString.setFillColor(sf::Color::Green);
    for (unsigned i = 0; i < bruglesco::max_pairs; ++i)

    imageIdentifiers.push_back(i);



    void GameScreen::setGame()

    std::shuffle(std::begin(imageIdentifiers), std::end(imageIdentifiers), bruglesco::generator);
    makeCards();
    positionCards();


    void GameScreen::trackMouse(const sf::Vector2f& mousePos)

    if (pauseButton.getGlobalBounds().contains(mousePos))

    mouseIn = bruglesco::gameMouseIn::pause;

    else if (returnToMain.getGlobalBounds().contains(mousePos))

    mouseIn = bruglesco::gameMouseIn::reset;

    else

    mouseIn = bruglesco::gameMouseIn::none;



    void GameScreen::input(const sf::Vector2f& mousePos)

    if (pauseButton.getGlobalBounds().contains(mousePos))

    if (!data.isOver())

    paused = !paused;


    else if (!paused && returnToMain.getGlobalBounds().contains(mousePos))

    data.quit();

    else if (!paused && delay == 0)

    for (std::pair<std::vector<Card>::iterator, std::vector<sf::RectangleShape>::iterator> card(data.getDeck().begin(), deck.begin());
    card.first != data.getDeck().end() && card.second != deck.end(); ++card.first, ++card.second)

    if (card.second->getGlobalBounds().contains(mousePos))

    card.first->flip();





    void GameScreen::update()

    highlightButtons();
    matchFailDelay();
    moveMatched();
    adjustTexture();
    hud.update();

    if (delay == 1)

    endFailDelay();

    if (delay > 0)

    --delay;


    if (data.isOver())

    playerWinString.setString("Player " + std::to_string(data.getPlayer().getIdentity()));



    void GameScreen::draw(sf::RenderWindow& window)

    window.draw(pauseButton);
    window.draw(pauseString);
    window.draw(returnToMain);
    window.draw(resetString);
    hud.draw(window);
    for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

    window.draw(*card);


    if (paused)

    window.draw(pauseForeground);


    if (data.isOver())

    window.draw(endGameForeground);
    window.draw(winString);
    window.draw(playerWinString);



    void GameScreen::makeCards()

    deck.clear();
    unsigned k = 0;
    for (std::vector<Card>::iterator card = data.getDeck().begin(); card != data.getDeck().end(); ++card)

    deck.push_back(sf::RectangleShape(sf::Vector2f(bruglesco::card_width, bruglesco::card_height)));
    deck[k].setTexture(&cardMap);
    deck[k].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));
    ++k;



    void GameScreen::positionCards()

    unsigned rows = 0;
    for (unsigned i = 2; i * i <= deck.size(); ++i)

    if (deck.size() % i == 0)

    rows = i;


    unsigned columns = deck.size() / rows;

    float padding_x = (bruglesco::canvas_width - columns * 128.f) / (columns + 1);
    float padding_y = (bruglesco::canvas_height - rows * 128.f) / (rows + 1);
    float x = padding_x;
    float y = padding_y;
    unsigned counter = 1;
    for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

    card->setPosition(x, y);
    if (counter < columns)

    ++counter;
    x += padding_x + bruglesco::card_width;

    else

    counter = 1;
    x = padding_x;
    y += padding_y + bruglesco::card_height;




    void GameScreen::highlightButtons()
    mouseIn == bruglesco::gameMouseIn::pause)

    pauseButton.setFillColor(sf::Color(60, 60, 60, 255));
    pauseString.setFillColor(sf::Color::Red);

    else

    pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
    pauseString.setFillColor(sf::Color::Black);


    if (mouseIn == bruglesco::gameMouseIn::reset)

    returnToMain.setFillColor(sf::Color(60, 60, 60, 255));
    resetString.setFillColor(sf::Color::Red);

    else

    returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
    resetString.setFillColor(sf::Color::Black);



    void GameScreen::matchFailDelay()

    if (delay == 0 && data.getFailedCards().size() == 2)

    delay = 240;



    void GameScreen::endFailDelay()

    data.resetDeck();


    void GameScreen::moveMatched()

    if (data.getMatchedCards().size() == 2)

    float x = bruglesco::player_card_x + bruglesco::player_card_x_offset * data.getPlayer().getScore();
    float y;
    if (data.playerOneTurn())

    y = bruglesco::player_one_card_y;

    else

    y = bruglesco::player_two_card_y;


    for (unsigned i = 0; i < data.getMatchedCards().size(); ++i)

    deck[data.getMatchedCards()[i]].setPosition(x, y);

    data.resetDeck();



    void GameScreen::adjustTexture()

    for (unsigned i = 0; i < data.getDeck().size(); ++i)

    if (data.getDeck()[i].checkState() == bruglesco::CardState::unmatched)

    deck[i].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));

    else

    deck[i].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[i].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));


    for (unsigned i = 0; i < data.getFailedCards().size(); ++i)

    deck[data.getFailedCards()[i]].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[data.getFailedCards()[i]].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));





    I tried to be a little more thorough in my description of my code this time around but that is not a skill I am great at yet. I hope that if you need any further explanation you will leave a comment and I will provide it.



    If I have any particular concerns for the code it would be the GameScreen class. It just seems to be large enough to worry. Should I try to abstract it further? any suggestions how? Of course I want any suggestions on any aspect of the code.







    share|improve this question























      up vote
      4
      down vote

      favorite









      up vote
      4
      down vote

      favorite











      This is the 2nd half of my Memory game. The first half can be found here. The repository can be found on Github.



      I am using SFML 2.4.2. In this project I focused on a few key aspects of design and development.



      • Top-down Design

      • DRY & SRP

      • Encapsulation & Abstraction

      In an effort to eliminate magic numbers I shoved all of my constexpr as well as my enums, a static logger and a static PRNG in a single header file. I don't know if this approach is best practice or not and would appreciate input. On one hand I could move the expressions to the appropriate headers where they are localized, but many of them are used repeatedly throughout the codebase. Should I localize the values that are used once and only keep a header file for the reused items (PRNG, Enums, etc...)?



      note: in the previous answer it was recommended to me to use double over float. I chose not to do this as SFML takes floats for its parameters and the number of casts made the code unreadable. I could let the computer narrow implicitly but then I would need to turn of wX which I prefer to keep on. It would also lead to a significant number of warnings.



      Expressions.h



      #ifndef BRUGLESCO_MEMORY_EXPRESSIONS_H
      #define BRUGLESCO_MEMORY_EXPRESSIONS_H

      #include <fstream>
      #include <random>

      namespace bruglesco

      static std::random_device rd;
      static std::mt19937 generator(rd());
      static std::ofstream debuggStream("DebugLog.txt"); // remove for release

      constexpr unsigned screen_width = 1500u;
      constexpr unsigned screen_height = 800u;

      constexpr float menu_button_width = 256.f;
      constexpr float menu_button_height = 256.f;

      constexpr float play_button_x = 622.f;
      constexpr float play_button_y = 96.f;
      constexpr float six_button_x = 95.f;
      constexpr float eight_button_x = 446.f;
      constexpr float twelve_button_x = 797.f;
      constexpr float sixteen_button_x = 1148.f;
      constexpr float pair_button_y = 448.f;

      constexpr float play_string_x_offset = 18.f;
      constexpr float play_string_y_offset = 28.f;
      constexpr float six_string_x_offset = 80.f;
      constexpr float eight_string_x_offset = 40.f;
      constexpr float twelve_string_x_offset = 15.f;
      constexpr float sixteen_string_x_offset = 8.f;
      constexpr float pair_string_x_offset = 40.f;
      constexpr float pair_string_y_offset = 100.f;

      constexpr float game_button_width = 128.f;
      constexpr float game_button_height = 64.f;

      constexpr float pause_x = 1244.f;
      constexpr float reset_x = 1372.f;
      constexpr float pause_offset = 12.f;
      constexpr float reset_offset = 5.f;

      constexpr float display_x = 1244.f;
      constexpr float player_one_display_y = 64.f;
      constexpr float player_two_display_y = 432.f;
      constexpr float display_offset = 20.f;

      constexpr float display_width = 256.f;
      constexpr float display_height = 368.f;

      constexpr float canvas_width = screen_width - display_width;
      constexpr float canvas_height = static_cast<float>(screen_height);

      constexpr float player_string_x = 1265.f;
      constexpr float player_one_string_y = 72.f;
      constexpr float player_two_string_y = 440.f;

      constexpr float player_card_x = 1265.f;
      constexpr float player_card_x_offset = 15.f;
      constexpr float player_one_card_y = 120.f;
      constexpr float player_two_card_y = 488.f;

      constexpr float card_width = 128.f;
      constexpr float card_height = 128.f;

      constexpr unsigned max_pairs = 16u;

      constexpr float win_width = screen_width - 300.f;
      constexpr float win_height = screen_height - 150.f;
      constexpr float win_x = 150.f;
      constexpr float win_y = 75.f;
      constexpr float winstring_x = 400.f;
      constexpr float winstring_y = 100.f;
      constexpr float player_winstring_x = winstring_x - 50.f;
      constexpr float player_winstring_y = winstring_y + 200.f;

      enum class DeckSize

      six = 6,
      eight = 8,
      twelve = 12,
      sixteen = 16
      ;

      enum class CardState

      unmatched,
      checking,
      matched
      ;

      enum class menuMouseIn

      none,
      play,
      six,
      eight,
      twelve,
      sixteen
      ;

      enum class gameMouseIn

      none,
      pause,
      reset
      ;

      enum class winState

      none,
      draw,
      playerOne,
      playerTwo
      ;


      #endif // !BRUGLESCO_MEMORY_EXPRESSIONS_H



      My main is intended to be small. Memory is the class that represents the entirety of the application.



      Main.cpp



      #include "Memory.h"

      int main()

      Memory memory;
      memory.run();




      The Memory class contains the ModelData and Viewport class. These classes represent the model logic and presentation logic respectively.



      Memory.h



      #ifndef BRUGLESCO_MEMORY_MEMORY_H
      #define BRUGLESCO_MEMORY_MEMORY_H

      #include "Expressions.h"
      #include "ModelData.h"
      #include "Viewport.h"

      #include <SFMLGraphics.hpp>

      class Memory

      public:
      void run();
      private:
      ModelData data;
      Viewport view data ;
      bool playing true ;

      void input();

      void update();

      void draw();
      ;

      #endif // !BRUGLESCO_MEMORY_MEMORY_H



      The ModelData class was the main focus of the last post. In this one I'd like to focus on the Vieport



      This class contains the window for rendering, as well as the MenuScreen and GameScreen classes. These are the only two screens in the program.



      First the MenuScreen. It is a simple arrangement of buttons that are highlighted dynamically both on selection and mouseover. The trackMouse method is to check for mouseover, the input method responds to user input (in this case clicks), and the update method dynamically maintains your selected game size and highlights the buttons according to that selection and mouseover.



      MenuScreen.h



      #ifndef BRUGLESCO_MEMORY_MENUSCREEN_H
      #define BRUGLESCO_MEMORY_MENUSCREEN_H

      #include "Expressions.h"
      #include "GameScreen.h"
      #include "ModelData.h"

      #include <SFMLGraphics.hpp>
      class MenuScreen

      public:
      MenuScreen(ModelData& data, GameScreen& game, sf::Font& font);

      void trackMouse(const sf::Vector2f& mousePos);

      void input(const sf::Vector2f& mousePos);

      void update();

      void draw(sf::RenderWindow& window);
      private:
      ModelData& data;
      GameScreen& game;
      sf::Font& font;
      sf::RectangleShape playButton sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text playString "Play", font ;
      sf::RectangleShape sixPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text sixString "Six", font ;
      sf::RectangleShape eightPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text eightString "Eight", font ;
      sf::RectangleShape twelvePairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text twelveString "Twelve", font ;
      sf::RectangleShape sixteenPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text sixteenString "Sixteen", font ;
      std::vector<sf::Text> pairString 4, "Pairs", font ;
      bruglesco::menuMouseIn mouseIn bruglesco::menuMouseIn::none ;
      bruglesco::DeckSize deckSize bruglesco::DeckSize::six ;

      void highlightButton();
      ;
      #endif // !BRUGLESCO_MEMORY_MENUSCREEN_H



      MenuScreen.cpp



      #include "MenuScreen.h"

      MenuScreen::MenuScreen(ModelData& data, GameScreen& game, sf::Font& font) :
      data data ,
      game game ,
      font font

      playButton.setPosition(bruglesco::play_button_x, bruglesco::play_button_y);
      playButton.setFillColor(sf::Color(120, 120, 120, 255));
      playString.setPosition(bruglesco::play_button_x + bruglesco::play_string_x_offset, bruglesco::play_button_y + bruglesco::play_string_y_offset);
      playString.setCharacterSize(150);
      playString.setFillColor(sf::Color::Black);

      sixPairs.setPosition(bruglesco::six_button_x, bruglesco::pair_button_y);
      sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixString.setPosition(bruglesco::six_button_x + bruglesco::six_string_x_offset, bruglesco::pair_button_y);
      sixString.setCharacterSize(100);
      sixString.setFillColor(sf::Color::Red);
      pairString[0].setPosition(bruglesco::six_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[0].setCharacterSize(100);
      pairString[0].setFillColor(sf::Color::Red);

      eightPairs.setPosition(bruglesco::eight_button_x, bruglesco::pair_button_y);
      eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
      eightString.setPosition(bruglesco::eight_button_x + bruglesco::eight_string_x_offset, bruglesco::pair_button_y);
      eightString.setCharacterSize(100);
      eightString.setFillColor(sf::Color::Black);
      pairString[1].setPosition(bruglesco::eight_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[1].setCharacterSize(100);
      pairString[1].setFillColor(sf::Color::Black);

      twelvePairs.setPosition(bruglesco::twelve_button_x, bruglesco::pair_button_y);
      twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
      twelveString.setPosition(bruglesco::twelve_button_x + bruglesco::twelve_string_x_offset, bruglesco::pair_button_y);
      twelveString.setCharacterSize(100);
      twelveString.setFillColor(sf::Color::Black);
      pairString[2].setPosition(bruglesco::twelve_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[2].setCharacterSize(100);
      pairString[2].setFillColor(sf::Color::Black);

      sixteenPairs.setPosition(bruglesco::sixteen_button_x, bruglesco::pair_button_y);
      sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixteenString.setPosition(bruglesco::sixteen_button_x + bruglesco::sixteen_string_x_offset, bruglesco::pair_button_y);
      sixteenString.setCharacterSize(100);
      sixteenString.setFillColor(sf::Color::Black);
      pairString[3].setPosition(bruglesco::sixteen_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[3].setCharacterSize(100);
      pairString[3].setFillColor(sf::Color::Black);


      void MenuScreen::trackMouse(const sf::Vector2f& mousePos)

      if (playButton.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::play;

      else if (sixPairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::six;

      else if (eightPairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::eight;

      else if (twelvePairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::twelve;

      else if (sixteenPairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::sixteen;

      else

      mouseIn = bruglesco::menuMouseIn::none;



      void MenuScreen::input(const sf::Vector2f& mousePos)

      if (playButton.getGlobalBounds().contains(mousePos))

      data.setSize(deckSize);
      data.play();
      game.setGame();
      mouseIn = bruglesco::menuMouseIn::none;

      else if (sixPairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::six;
      data.setSize(deckSize);

      else if (eightPairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::eight;
      data.setSize(deckSize);

      else if (twelvePairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::twelve;
      data.setSize(deckSize);

      else if (sixteenPairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::sixteen;
      data.setSize(deckSize);



      void MenuScreen::update()

      highlightButton();


      void MenuScreen::draw(sf::RenderWindow& window)

      window.draw(playButton);
      window.draw(playString);
      window.draw(sixPairs);
      window.draw(sixString);
      window.draw(eightPairs);
      window.draw(eightString);
      window.draw(twelvePairs);
      window.draw(twelveString);
      window.draw(sixteenPairs);
      window.draw(sixteenString);
      for (std::vector<sf::Text>::iterator string = pairString.begin(); string != pairString.end(); ++string)

      window.draw(*string);



      void MenuScreen::highlightButton()

      if (mouseIn == bruglesco::menuMouseIn::play)

      playButton.setFillColor(sf::Color(60, 60, 60, 255));
      playString.setFillColor(sf::Color::Red);

      else

      playButton.setFillColor(sf::Color(120, 120, 120, 255));
      playString.setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::six)

      sixPairs.setFillColor(sf::Color(60, 60, 60, 255));
      sixString.setFillColor(sf::Color::Red);
      pairString[0].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::six)

      sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixString.setFillColor(sf::Color::Red);
      pairString[0].setFillColor(sf::Color::Red);

      else

      sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixString.setFillColor(sf::Color::Black);
      pairString[0].setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::eight)

      eightPairs.setFillColor(sf::Color(60, 60, 60, 255));
      eightString.setFillColor(sf::Color::Red);
      pairString[1].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::eight)

      eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
      eightString.setFillColor(sf::Color::Red);
      pairString[1].setFillColor(sf::Color::Red);

      else

      eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
      eightString.setFillColor(sf::Color::Black);
      pairString[1].setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::twelve)

      twelvePairs.setFillColor(sf::Color(60, 60, 60, 255));
      twelveString.setFillColor(sf::Color::Red);
      pairString[2].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::twelve)

      twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
      twelveString.setFillColor(sf::Color::Red);
      pairString[2].setFillColor(sf::Color::Red);

      else

      twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
      twelveString.setFillColor(sf::Color::Black);
      pairString[2].setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::sixteen)

      sixteenPairs.setFillColor(sf::Color(60, 60, 60, 255));
      sixteenString.setFillColor(sf::Color::Red);
      pairString[3].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::sixteen)

      sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixteenString.setFillColor(sf::Color::Red);
      pairString[3].setFillColor(sf::Color::Red);

      else

      sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixteenString.setFillColor(sf::Color::Black);
      pairString[3].setFillColor(sf::Color::Black);





      Next is the GameScreen class. Here is the meat of the presentation logic. The HUDisplay class is a subsection that updates the players score as strings to display them. It also maintains a space to draw matched cards in a stack in the players "hand".



      HUDisplay.h



      #ifndef BRUGLESCO_MEMORY_HUDISPLAY_H
      #define BRUGLESCO_MEMORY_HUDISPLAY_H

      #include "Expressions.h"
      #include "ModelData.h"

      #include <SFMLGraphics.hpp>

      #include <string>

      class HUDisplay

      public:
      HUDisplay(ModelData& data, sf::Font& font);

      void update();

      void draw(sf::RenderWindow& window);
      private:
      ModelData& data;
      sf::Font& font;
      sf::RectangleShape playerOneDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
      sf::RectangleShape playerTwoDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
      unsigned playerOneScore 0 ;
      sf::Text playerOne "Player One: " + std::to_string(playerOneScore), font ;
      unsigned playerTwoScore 0 ;
      sf::Text playerTwo "Player Two: " + std::to_string(playerTwoScore), font ;
      ;

      #endif // !BRUGLESCO_MEMORY_HUDISPLAY_H



      HUDisplay.cpp



      #include "HUDisplay.h"

      HUDisplay::HUDisplay(ModelData& data, sf::Font& font) :
      data data ,
      font font

      playerOneDisplay.setPosition(bruglesco::display_x, bruglesco::player_one_display_y);
      playerOneDisplay.setFillColor(sf::Color(120, 120, 120, 255));
      playerTwoDisplay.setPosition(bruglesco::display_x, bruglesco::player_two_display_y);
      playerTwoDisplay.setFillColor(sf::Color(120, 120, 120, 255));
      playerOne.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_one_display_y);
      playerOne.setCharacterSize(50);
      playerOne.setFillColor(sf::Color::Red);
      playerTwo.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_two_display_y);
      playerTwo.setCharacterSize(50);
      playerTwo.setFillColor(sf::Color::Black);


      void HUDisplay::update()

      if (data.playerOneTurn())

      playerOneScore = data.getPlayer().getScore();
      playerOne.setString("Player One: " + std::to_string(playerOneScore));
      playerOne.setFillColor(sf::Color::Red);
      playerTwo.setFillColor(sf::Color::Black);

      else

      playerTwoScore = data.getPlayer().getScore();
      playerTwo.setString("Player Two: " + std::to_string(playerTwoScore));
      playerOne.setFillColor(sf::Color::Black);
      playerTwo.setFillColor(sf::Color::Red);



      void HUDisplay::draw(sf::RenderWindow& window)

      window.draw(playerOneDisplay);
      window.draw(playerOne);
      window.draw(playerTwoDisplay);
      window.draw(playerTwo);




      The trackMouse method here works the same as in the Menu. The input method is extended to registering clicks on card images. The logic is then passed over to the Model class which is maintained as a visitor to the View and passed in as a dependency. The positionCards function is able to position the cards dynamically based on the size of deck the player chose on the menu screen. The makeCards function creates the number of display objects based on deck size.
      The matchFailDelay is an attempt at keeping the images displayed for a short period of time after a failed match. endFailDelay updates the Model so the display can update accordingly. moveMatched sets the position of matched cards according to the player who made the match and how many they've made. adjustTexture is done this way to consistently vary the images used and to swap back and forth between face up and face down.



      GameScreen.h



      #ifndef BRUGLESCO_MEMORY_GAMESCREEN_H
      #define BRUGLESCO_MEMORY_GAMESCREEN_H

      #include "Card.h"
      #include "Expressions.h"
      #include "HUDisplay.h"
      #include "ModelData.h"

      #include <SFMLGraphics.hpp>

      #include <vector>

      class GameScreen

      public:
      GameScreen(ModelData& data, sf::Font& font);

      void setGame();

      void trackMouse(const sf::Vector2f& mousePos);

      void input(const sf::Vector2f& mousePos);

      void update();

      void draw(sf::RenderWindow& window);
      private:
      ModelData& data;
      sf::Font& font;
      sf::Texture cardMap;
      sf::RectangleShape pauseButton sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
      sf::Text pauseString "Pause", font ;
      sf::RectangleShape returnToMain sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
      sf::Text resetString "Return", font ;
      HUDisplay hud data, font ;
      sf::RectangleShape pauseForeground sf::Vector2f(static_cast<float>(bruglesco::screen_width), static_cast<float>(bruglesco::screen_height)) ;
      sf::RectangleShape endGameForeground sf::Vector2f(bruglesco::win_width, bruglesco::win_height) ;
      sf::Text winString "Winner", font ;
      sf::Text playerWinString "", font ;
      std::vector<sf::RectangleShape> deck;
      bruglesco::gameMouseIn mouseIn bruglesco::gameMouseIn::none ;
      bool paused false ;
      std::vector<unsigned> imageIdentifiers;
      unsigned delay 0 ;

      void makeCards();

      void positionCards();

      void highlightButtons();

      void matchFailDelay();

      void endFailDelay();

      void moveMatched();

      void adjustTexture();
      ;

      #endif // !BRUGLESCO_MEMORY_GAMESCREEN_H



      GameScreen.cpp



      #include "GameScreen.h"

      GameScreen::GameScreen(ModelData& data, sf::Font& font) :
      data data ,
      font font

      if (!cardMap.loadFromFile("spritesheet.png"))

      bruglesco::debuggStream << "you aren't loading your textures"; // remove for release

      pauseButton.setPosition(bruglesco::pause_x, 0);
      pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
      pauseString.setPosition(bruglesco::pause_x + bruglesco::pause_offset, 0);
      pauseString.setCharacterSize(50);
      pauseString.setFillColor(sf::Color::Black);
      returnToMain.setPosition(bruglesco::reset_x, 0);
      returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
      resetString.setPosition(bruglesco::reset_x + bruglesco::reset_offset, 0);
      resetString.setCharacterSize(50);
      resetString.setFillColor(sf::Color::Black);
      pauseForeground.setFillColor(sf::Color(120, 120, 120, 120));
      endGameForeground.setPosition(bruglesco::win_x, bruglesco::win_y);
      endGameForeground.setFillColor(sf::Color(90, 90, 90, 180));
      winString.setPosition(bruglesco::winstring_x, bruglesco::winstring_y);
      winString.setCharacterSize(250);
      winString.setFillColor(sf::Color::Green);
      playerWinString.setPosition(bruglesco::player_winstring_x, bruglesco::player_winstring_y);
      playerWinString.setCharacterSize(250);
      playerWinString.setFillColor(sf::Color::Green);
      for (unsigned i = 0; i < bruglesco::max_pairs; ++i)

      imageIdentifiers.push_back(i);



      void GameScreen::setGame()

      std::shuffle(std::begin(imageIdentifiers), std::end(imageIdentifiers), bruglesco::generator);
      makeCards();
      positionCards();


      void GameScreen::trackMouse(const sf::Vector2f& mousePos)

      if (pauseButton.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::gameMouseIn::pause;

      else if (returnToMain.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::gameMouseIn::reset;

      else

      mouseIn = bruglesco::gameMouseIn::none;



      void GameScreen::input(const sf::Vector2f& mousePos)

      if (pauseButton.getGlobalBounds().contains(mousePos))

      if (!data.isOver())

      paused = !paused;


      else if (!paused && returnToMain.getGlobalBounds().contains(mousePos))

      data.quit();

      else if (!paused && delay == 0)

      for (std::pair<std::vector<Card>::iterator, std::vector<sf::RectangleShape>::iterator> card(data.getDeck().begin(), deck.begin());
      card.first != data.getDeck().end() && card.second != deck.end(); ++card.first, ++card.second)

      if (card.second->getGlobalBounds().contains(mousePos))

      card.first->flip();





      void GameScreen::update()

      highlightButtons();
      matchFailDelay();
      moveMatched();
      adjustTexture();
      hud.update();

      if (delay == 1)

      endFailDelay();

      if (delay > 0)

      --delay;


      if (data.isOver())

      playerWinString.setString("Player " + std::to_string(data.getPlayer().getIdentity()));



      void GameScreen::draw(sf::RenderWindow& window)

      window.draw(pauseButton);
      window.draw(pauseString);
      window.draw(returnToMain);
      window.draw(resetString);
      hud.draw(window);
      for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

      window.draw(*card);


      if (paused)

      window.draw(pauseForeground);


      if (data.isOver())

      window.draw(endGameForeground);
      window.draw(winString);
      window.draw(playerWinString);



      void GameScreen::makeCards()

      deck.clear();
      unsigned k = 0;
      for (std::vector<Card>::iterator card = data.getDeck().begin(); card != data.getDeck().end(); ++card)

      deck.push_back(sf::RectangleShape(sf::Vector2f(bruglesco::card_width, bruglesco::card_height)));
      deck[k].setTexture(&cardMap);
      deck[k].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));
      ++k;



      void GameScreen::positionCards()

      unsigned rows = 0;
      for (unsigned i = 2; i * i <= deck.size(); ++i)

      if (deck.size() % i == 0)

      rows = i;


      unsigned columns = deck.size() / rows;

      float padding_x = (bruglesco::canvas_width - columns * 128.f) / (columns + 1);
      float padding_y = (bruglesco::canvas_height - rows * 128.f) / (rows + 1);
      float x = padding_x;
      float y = padding_y;
      unsigned counter = 1;
      for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

      card->setPosition(x, y);
      if (counter < columns)

      ++counter;
      x += padding_x + bruglesco::card_width;

      else

      counter = 1;
      x = padding_x;
      y += padding_y + bruglesco::card_height;




      void GameScreen::highlightButtons()
      mouseIn == bruglesco::gameMouseIn::pause)

      pauseButton.setFillColor(sf::Color(60, 60, 60, 255));
      pauseString.setFillColor(sf::Color::Red);

      else

      pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
      pauseString.setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::gameMouseIn::reset)

      returnToMain.setFillColor(sf::Color(60, 60, 60, 255));
      resetString.setFillColor(sf::Color::Red);

      else

      returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
      resetString.setFillColor(sf::Color::Black);



      void GameScreen::matchFailDelay()

      if (delay == 0 && data.getFailedCards().size() == 2)

      delay = 240;



      void GameScreen::endFailDelay()

      data.resetDeck();


      void GameScreen::moveMatched()

      if (data.getMatchedCards().size() == 2)

      float x = bruglesco::player_card_x + bruglesco::player_card_x_offset * data.getPlayer().getScore();
      float y;
      if (data.playerOneTurn())

      y = bruglesco::player_one_card_y;

      else

      y = bruglesco::player_two_card_y;


      for (unsigned i = 0; i < data.getMatchedCards().size(); ++i)

      deck[data.getMatchedCards()[i]].setPosition(x, y);

      data.resetDeck();



      void GameScreen::adjustTexture()

      for (unsigned i = 0; i < data.getDeck().size(); ++i)

      if (data.getDeck()[i].checkState() == bruglesco::CardState::unmatched)

      deck[i].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));

      else

      deck[i].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[i].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));


      for (unsigned i = 0; i < data.getFailedCards().size(); ++i)

      deck[data.getFailedCards()[i]].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[data.getFailedCards()[i]].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));





      I tried to be a little more thorough in my description of my code this time around but that is not a skill I am great at yet. I hope that if you need any further explanation you will leave a comment and I will provide it.



      If I have any particular concerns for the code it would be the GameScreen class. It just seems to be large enough to worry. Should I try to abstract it further? any suggestions how? Of course I want any suggestions on any aspect of the code.







      share|improve this question













      This is the 2nd half of my Memory game. The first half can be found here. The repository can be found on Github.



      I am using SFML 2.4.2. In this project I focused on a few key aspects of design and development.



      • Top-down Design

      • DRY & SRP

      • Encapsulation & Abstraction

      In an effort to eliminate magic numbers I shoved all of my constexpr as well as my enums, a static logger and a static PRNG in a single header file. I don't know if this approach is best practice or not and would appreciate input. On one hand I could move the expressions to the appropriate headers where they are localized, but many of them are used repeatedly throughout the codebase. Should I localize the values that are used once and only keep a header file for the reused items (PRNG, Enums, etc...)?



      note: in the previous answer it was recommended to me to use double over float. I chose not to do this as SFML takes floats for its parameters and the number of casts made the code unreadable. I could let the computer narrow implicitly but then I would need to turn of wX which I prefer to keep on. It would also lead to a significant number of warnings.



      Expressions.h



      #ifndef BRUGLESCO_MEMORY_EXPRESSIONS_H
      #define BRUGLESCO_MEMORY_EXPRESSIONS_H

      #include <fstream>
      #include <random>

      namespace bruglesco

      static std::random_device rd;
      static std::mt19937 generator(rd());
      static std::ofstream debuggStream("DebugLog.txt"); // remove for release

      constexpr unsigned screen_width = 1500u;
      constexpr unsigned screen_height = 800u;

      constexpr float menu_button_width = 256.f;
      constexpr float menu_button_height = 256.f;

      constexpr float play_button_x = 622.f;
      constexpr float play_button_y = 96.f;
      constexpr float six_button_x = 95.f;
      constexpr float eight_button_x = 446.f;
      constexpr float twelve_button_x = 797.f;
      constexpr float sixteen_button_x = 1148.f;
      constexpr float pair_button_y = 448.f;

      constexpr float play_string_x_offset = 18.f;
      constexpr float play_string_y_offset = 28.f;
      constexpr float six_string_x_offset = 80.f;
      constexpr float eight_string_x_offset = 40.f;
      constexpr float twelve_string_x_offset = 15.f;
      constexpr float sixteen_string_x_offset = 8.f;
      constexpr float pair_string_x_offset = 40.f;
      constexpr float pair_string_y_offset = 100.f;

      constexpr float game_button_width = 128.f;
      constexpr float game_button_height = 64.f;

      constexpr float pause_x = 1244.f;
      constexpr float reset_x = 1372.f;
      constexpr float pause_offset = 12.f;
      constexpr float reset_offset = 5.f;

      constexpr float display_x = 1244.f;
      constexpr float player_one_display_y = 64.f;
      constexpr float player_two_display_y = 432.f;
      constexpr float display_offset = 20.f;

      constexpr float display_width = 256.f;
      constexpr float display_height = 368.f;

      constexpr float canvas_width = screen_width - display_width;
      constexpr float canvas_height = static_cast<float>(screen_height);

      constexpr float player_string_x = 1265.f;
      constexpr float player_one_string_y = 72.f;
      constexpr float player_two_string_y = 440.f;

      constexpr float player_card_x = 1265.f;
      constexpr float player_card_x_offset = 15.f;
      constexpr float player_one_card_y = 120.f;
      constexpr float player_two_card_y = 488.f;

      constexpr float card_width = 128.f;
      constexpr float card_height = 128.f;

      constexpr unsigned max_pairs = 16u;

      constexpr float win_width = screen_width - 300.f;
      constexpr float win_height = screen_height - 150.f;
      constexpr float win_x = 150.f;
      constexpr float win_y = 75.f;
      constexpr float winstring_x = 400.f;
      constexpr float winstring_y = 100.f;
      constexpr float player_winstring_x = winstring_x - 50.f;
      constexpr float player_winstring_y = winstring_y + 200.f;

      enum class DeckSize

      six = 6,
      eight = 8,
      twelve = 12,
      sixteen = 16
      ;

      enum class CardState

      unmatched,
      checking,
      matched
      ;

      enum class menuMouseIn

      none,
      play,
      six,
      eight,
      twelve,
      sixteen
      ;

      enum class gameMouseIn

      none,
      pause,
      reset
      ;

      enum class winState

      none,
      draw,
      playerOne,
      playerTwo
      ;


      #endif // !BRUGLESCO_MEMORY_EXPRESSIONS_H



      My main is intended to be small. Memory is the class that represents the entirety of the application.



      Main.cpp



      #include "Memory.h"

      int main()

      Memory memory;
      memory.run();




      The Memory class contains the ModelData and Viewport class. These classes represent the model logic and presentation logic respectively.



      Memory.h



      #ifndef BRUGLESCO_MEMORY_MEMORY_H
      #define BRUGLESCO_MEMORY_MEMORY_H

      #include "Expressions.h"
      #include "ModelData.h"
      #include "Viewport.h"

      #include <SFMLGraphics.hpp>

      class Memory

      public:
      void run();
      private:
      ModelData data;
      Viewport view data ;
      bool playing true ;

      void input();

      void update();

      void draw();
      ;

      #endif // !BRUGLESCO_MEMORY_MEMORY_H



      The ModelData class was the main focus of the last post. In this one I'd like to focus on the Vieport



      This class contains the window for rendering, as well as the MenuScreen and GameScreen classes. These are the only two screens in the program.



      First the MenuScreen. It is a simple arrangement of buttons that are highlighted dynamically both on selection and mouseover. The trackMouse method is to check for mouseover, the input method responds to user input (in this case clicks), and the update method dynamically maintains your selected game size and highlights the buttons according to that selection and mouseover.



      MenuScreen.h



      #ifndef BRUGLESCO_MEMORY_MENUSCREEN_H
      #define BRUGLESCO_MEMORY_MENUSCREEN_H

      #include "Expressions.h"
      #include "GameScreen.h"
      #include "ModelData.h"

      #include <SFMLGraphics.hpp>
      class MenuScreen

      public:
      MenuScreen(ModelData& data, GameScreen& game, sf::Font& font);

      void trackMouse(const sf::Vector2f& mousePos);

      void input(const sf::Vector2f& mousePos);

      void update();

      void draw(sf::RenderWindow& window);
      private:
      ModelData& data;
      GameScreen& game;
      sf::Font& font;
      sf::RectangleShape playButton sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text playString "Play", font ;
      sf::RectangleShape sixPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text sixString "Six", font ;
      sf::RectangleShape eightPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text eightString "Eight", font ;
      sf::RectangleShape twelvePairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text twelveString "Twelve", font ;
      sf::RectangleShape sixteenPairs sf::Vector2f(bruglesco::menu_button_width, bruglesco::menu_button_height) ;
      sf::Text sixteenString "Sixteen", font ;
      std::vector<sf::Text> pairString 4, "Pairs", font ;
      bruglesco::menuMouseIn mouseIn bruglesco::menuMouseIn::none ;
      bruglesco::DeckSize deckSize bruglesco::DeckSize::six ;

      void highlightButton();
      ;
      #endif // !BRUGLESCO_MEMORY_MENUSCREEN_H



      MenuScreen.cpp



      #include "MenuScreen.h"

      MenuScreen::MenuScreen(ModelData& data, GameScreen& game, sf::Font& font) :
      data data ,
      game game ,
      font font

      playButton.setPosition(bruglesco::play_button_x, bruglesco::play_button_y);
      playButton.setFillColor(sf::Color(120, 120, 120, 255));
      playString.setPosition(bruglesco::play_button_x + bruglesco::play_string_x_offset, bruglesco::play_button_y + bruglesco::play_string_y_offset);
      playString.setCharacterSize(150);
      playString.setFillColor(sf::Color::Black);

      sixPairs.setPosition(bruglesco::six_button_x, bruglesco::pair_button_y);
      sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixString.setPosition(bruglesco::six_button_x + bruglesco::six_string_x_offset, bruglesco::pair_button_y);
      sixString.setCharacterSize(100);
      sixString.setFillColor(sf::Color::Red);
      pairString[0].setPosition(bruglesco::six_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[0].setCharacterSize(100);
      pairString[0].setFillColor(sf::Color::Red);

      eightPairs.setPosition(bruglesco::eight_button_x, bruglesco::pair_button_y);
      eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
      eightString.setPosition(bruglesco::eight_button_x + bruglesco::eight_string_x_offset, bruglesco::pair_button_y);
      eightString.setCharacterSize(100);
      eightString.setFillColor(sf::Color::Black);
      pairString[1].setPosition(bruglesco::eight_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[1].setCharacterSize(100);
      pairString[1].setFillColor(sf::Color::Black);

      twelvePairs.setPosition(bruglesco::twelve_button_x, bruglesco::pair_button_y);
      twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
      twelveString.setPosition(bruglesco::twelve_button_x + bruglesco::twelve_string_x_offset, bruglesco::pair_button_y);
      twelveString.setCharacterSize(100);
      twelveString.setFillColor(sf::Color::Black);
      pairString[2].setPosition(bruglesco::twelve_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[2].setCharacterSize(100);
      pairString[2].setFillColor(sf::Color::Black);

      sixteenPairs.setPosition(bruglesco::sixteen_button_x, bruglesco::pair_button_y);
      sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixteenString.setPosition(bruglesco::sixteen_button_x + bruglesco::sixteen_string_x_offset, bruglesco::pair_button_y);
      sixteenString.setCharacterSize(100);
      sixteenString.setFillColor(sf::Color::Black);
      pairString[3].setPosition(bruglesco::sixteen_button_x + bruglesco::pair_string_x_offset, bruglesco::pair_button_y + bruglesco::pair_string_y_offset);
      pairString[3].setCharacterSize(100);
      pairString[3].setFillColor(sf::Color::Black);


      void MenuScreen::trackMouse(const sf::Vector2f& mousePos)

      if (playButton.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::play;

      else if (sixPairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::six;

      else if (eightPairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::eight;

      else if (twelvePairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::twelve;

      else if (sixteenPairs.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::menuMouseIn::sixteen;

      else

      mouseIn = bruglesco::menuMouseIn::none;



      void MenuScreen::input(const sf::Vector2f& mousePos)

      if (playButton.getGlobalBounds().contains(mousePos))

      data.setSize(deckSize);
      data.play();
      game.setGame();
      mouseIn = bruglesco::menuMouseIn::none;

      else if (sixPairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::six;
      data.setSize(deckSize);

      else if (eightPairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::eight;
      data.setSize(deckSize);

      else if (twelvePairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::twelve;
      data.setSize(deckSize);

      else if (sixteenPairs.getGlobalBounds().contains(mousePos))

      deckSize = bruglesco::DeckSize::sixteen;
      data.setSize(deckSize);



      void MenuScreen::update()

      highlightButton();


      void MenuScreen::draw(sf::RenderWindow& window)

      window.draw(playButton);
      window.draw(playString);
      window.draw(sixPairs);
      window.draw(sixString);
      window.draw(eightPairs);
      window.draw(eightString);
      window.draw(twelvePairs);
      window.draw(twelveString);
      window.draw(sixteenPairs);
      window.draw(sixteenString);
      for (std::vector<sf::Text>::iterator string = pairString.begin(); string != pairString.end(); ++string)

      window.draw(*string);



      void MenuScreen::highlightButton()

      if (mouseIn == bruglesco::menuMouseIn::play)

      playButton.setFillColor(sf::Color(60, 60, 60, 255));
      playString.setFillColor(sf::Color::Red);

      else

      playButton.setFillColor(sf::Color(120, 120, 120, 255));
      playString.setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::six)

      sixPairs.setFillColor(sf::Color(60, 60, 60, 255));
      sixString.setFillColor(sf::Color::Red);
      pairString[0].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::six)

      sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixString.setFillColor(sf::Color::Red);
      pairString[0].setFillColor(sf::Color::Red);

      else

      sixPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixString.setFillColor(sf::Color::Black);
      pairString[0].setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::eight)

      eightPairs.setFillColor(sf::Color(60, 60, 60, 255));
      eightString.setFillColor(sf::Color::Red);
      pairString[1].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::eight)

      eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
      eightString.setFillColor(sf::Color::Red);
      pairString[1].setFillColor(sf::Color::Red);

      else

      eightPairs.setFillColor(sf::Color(120, 120, 120, 255));
      eightString.setFillColor(sf::Color::Black);
      pairString[1].setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::twelve)

      twelvePairs.setFillColor(sf::Color(60, 60, 60, 255));
      twelveString.setFillColor(sf::Color::Red);
      pairString[2].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::twelve)

      twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
      twelveString.setFillColor(sf::Color::Red);
      pairString[2].setFillColor(sf::Color::Red);

      else

      twelvePairs.setFillColor(sf::Color(120, 120, 120, 255));
      twelveString.setFillColor(sf::Color::Black);
      pairString[2].setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::menuMouseIn::sixteen)

      sixteenPairs.setFillColor(sf::Color(60, 60, 60, 255));
      sixteenString.setFillColor(sf::Color::Red);
      pairString[3].setFillColor(sf::Color::Red);

      else if (deckSize == bruglesco::DeckSize::sixteen)

      sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixteenString.setFillColor(sf::Color::Red);
      pairString[3].setFillColor(sf::Color::Red);

      else

      sixteenPairs.setFillColor(sf::Color(120, 120, 120, 255));
      sixteenString.setFillColor(sf::Color::Black);
      pairString[3].setFillColor(sf::Color::Black);





      Next is the GameScreen class. Here is the meat of the presentation logic. The HUDisplay class is a subsection that updates the players score as strings to display them. It also maintains a space to draw matched cards in a stack in the players "hand".



      HUDisplay.h



      #ifndef BRUGLESCO_MEMORY_HUDISPLAY_H
      #define BRUGLESCO_MEMORY_HUDISPLAY_H

      #include "Expressions.h"
      #include "ModelData.h"

      #include <SFMLGraphics.hpp>

      #include <string>

      class HUDisplay

      public:
      HUDisplay(ModelData& data, sf::Font& font);

      void update();

      void draw(sf::RenderWindow& window);
      private:
      ModelData& data;
      sf::Font& font;
      sf::RectangleShape playerOneDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
      sf::RectangleShape playerTwoDisplay sf::Vector2f(bruglesco::display_width, bruglesco::display_height) ;
      unsigned playerOneScore 0 ;
      sf::Text playerOne "Player One: " + std::to_string(playerOneScore), font ;
      unsigned playerTwoScore 0 ;
      sf::Text playerTwo "Player Two: " + std::to_string(playerTwoScore), font ;
      ;

      #endif // !BRUGLESCO_MEMORY_HUDISPLAY_H



      HUDisplay.cpp



      #include "HUDisplay.h"

      HUDisplay::HUDisplay(ModelData& data, sf::Font& font) :
      data data ,
      font font

      playerOneDisplay.setPosition(bruglesco::display_x, bruglesco::player_one_display_y);
      playerOneDisplay.setFillColor(sf::Color(120, 120, 120, 255));
      playerTwoDisplay.setPosition(bruglesco::display_x, bruglesco::player_two_display_y);
      playerTwoDisplay.setFillColor(sf::Color(120, 120, 120, 255));
      playerOne.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_one_display_y);
      playerOne.setCharacterSize(50);
      playerOne.setFillColor(sf::Color::Red);
      playerTwo.setPosition(bruglesco::display_x + bruglesco::display_offset, bruglesco::player_two_display_y);
      playerTwo.setCharacterSize(50);
      playerTwo.setFillColor(sf::Color::Black);


      void HUDisplay::update()

      if (data.playerOneTurn())

      playerOneScore = data.getPlayer().getScore();
      playerOne.setString("Player One: " + std::to_string(playerOneScore));
      playerOne.setFillColor(sf::Color::Red);
      playerTwo.setFillColor(sf::Color::Black);

      else

      playerTwoScore = data.getPlayer().getScore();
      playerTwo.setString("Player Two: " + std::to_string(playerTwoScore));
      playerOne.setFillColor(sf::Color::Black);
      playerTwo.setFillColor(sf::Color::Red);



      void HUDisplay::draw(sf::RenderWindow& window)

      window.draw(playerOneDisplay);
      window.draw(playerOne);
      window.draw(playerTwoDisplay);
      window.draw(playerTwo);




      The trackMouse method here works the same as in the Menu. The input method is extended to registering clicks on card images. The logic is then passed over to the Model class which is maintained as a visitor to the View and passed in as a dependency. The positionCards function is able to position the cards dynamically based on the size of deck the player chose on the menu screen. The makeCards function creates the number of display objects based on deck size.
      The matchFailDelay is an attempt at keeping the images displayed for a short period of time after a failed match. endFailDelay updates the Model so the display can update accordingly. moveMatched sets the position of matched cards according to the player who made the match and how many they've made. adjustTexture is done this way to consistently vary the images used and to swap back and forth between face up and face down.



      GameScreen.h



      #ifndef BRUGLESCO_MEMORY_GAMESCREEN_H
      #define BRUGLESCO_MEMORY_GAMESCREEN_H

      #include "Card.h"
      #include "Expressions.h"
      #include "HUDisplay.h"
      #include "ModelData.h"

      #include <SFMLGraphics.hpp>

      #include <vector>

      class GameScreen

      public:
      GameScreen(ModelData& data, sf::Font& font);

      void setGame();

      void trackMouse(const sf::Vector2f& mousePos);

      void input(const sf::Vector2f& mousePos);

      void update();

      void draw(sf::RenderWindow& window);
      private:
      ModelData& data;
      sf::Font& font;
      sf::Texture cardMap;
      sf::RectangleShape pauseButton sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
      sf::Text pauseString "Pause", font ;
      sf::RectangleShape returnToMain sf::Vector2f(bruglesco::game_button_width, bruglesco::game_button_height) ;
      sf::Text resetString "Return", font ;
      HUDisplay hud data, font ;
      sf::RectangleShape pauseForeground sf::Vector2f(static_cast<float>(bruglesco::screen_width), static_cast<float>(bruglesco::screen_height)) ;
      sf::RectangleShape endGameForeground sf::Vector2f(bruglesco::win_width, bruglesco::win_height) ;
      sf::Text winString "Winner", font ;
      sf::Text playerWinString "", font ;
      std::vector<sf::RectangleShape> deck;
      bruglesco::gameMouseIn mouseIn bruglesco::gameMouseIn::none ;
      bool paused false ;
      std::vector<unsigned> imageIdentifiers;
      unsigned delay 0 ;

      void makeCards();

      void positionCards();

      void highlightButtons();

      void matchFailDelay();

      void endFailDelay();

      void moveMatched();

      void adjustTexture();
      ;

      #endif // !BRUGLESCO_MEMORY_GAMESCREEN_H



      GameScreen.cpp



      #include "GameScreen.h"

      GameScreen::GameScreen(ModelData& data, sf::Font& font) :
      data data ,
      font font

      if (!cardMap.loadFromFile("spritesheet.png"))

      bruglesco::debuggStream << "you aren't loading your textures"; // remove for release

      pauseButton.setPosition(bruglesco::pause_x, 0);
      pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
      pauseString.setPosition(bruglesco::pause_x + bruglesco::pause_offset, 0);
      pauseString.setCharacterSize(50);
      pauseString.setFillColor(sf::Color::Black);
      returnToMain.setPosition(bruglesco::reset_x, 0);
      returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
      resetString.setPosition(bruglesco::reset_x + bruglesco::reset_offset, 0);
      resetString.setCharacterSize(50);
      resetString.setFillColor(sf::Color::Black);
      pauseForeground.setFillColor(sf::Color(120, 120, 120, 120));
      endGameForeground.setPosition(bruglesco::win_x, bruglesco::win_y);
      endGameForeground.setFillColor(sf::Color(90, 90, 90, 180));
      winString.setPosition(bruglesco::winstring_x, bruglesco::winstring_y);
      winString.setCharacterSize(250);
      winString.setFillColor(sf::Color::Green);
      playerWinString.setPosition(bruglesco::player_winstring_x, bruglesco::player_winstring_y);
      playerWinString.setCharacterSize(250);
      playerWinString.setFillColor(sf::Color::Green);
      for (unsigned i = 0; i < bruglesco::max_pairs; ++i)

      imageIdentifiers.push_back(i);



      void GameScreen::setGame()

      std::shuffle(std::begin(imageIdentifiers), std::end(imageIdentifiers), bruglesco::generator);
      makeCards();
      positionCards();


      void GameScreen::trackMouse(const sf::Vector2f& mousePos)

      if (pauseButton.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::gameMouseIn::pause;

      else if (returnToMain.getGlobalBounds().contains(mousePos))

      mouseIn = bruglesco::gameMouseIn::reset;

      else

      mouseIn = bruglesco::gameMouseIn::none;



      void GameScreen::input(const sf::Vector2f& mousePos)

      if (pauseButton.getGlobalBounds().contains(mousePos))

      if (!data.isOver())

      paused = !paused;


      else if (!paused && returnToMain.getGlobalBounds().contains(mousePos))

      data.quit();

      else if (!paused && delay == 0)

      for (std::pair<std::vector<Card>::iterator, std::vector<sf::RectangleShape>::iterator> card(data.getDeck().begin(), deck.begin());
      card.first != data.getDeck().end() && card.second != deck.end(); ++card.first, ++card.second)

      if (card.second->getGlobalBounds().contains(mousePos))

      card.first->flip();





      void GameScreen::update()

      highlightButtons();
      matchFailDelay();
      moveMatched();
      adjustTexture();
      hud.update();

      if (delay == 1)

      endFailDelay();

      if (delay > 0)

      --delay;


      if (data.isOver())

      playerWinString.setString("Player " + std::to_string(data.getPlayer().getIdentity()));



      void GameScreen::draw(sf::RenderWindow& window)

      window.draw(pauseButton);
      window.draw(pauseString);
      window.draw(returnToMain);
      window.draw(resetString);
      hud.draw(window);
      for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

      window.draw(*card);


      if (paused)

      window.draw(pauseForeground);


      if (data.isOver())

      window.draw(endGameForeground);
      window.draw(winString);
      window.draw(playerWinString);



      void GameScreen::makeCards()

      deck.clear();
      unsigned k = 0;
      for (std::vector<Card>::iterator card = data.getDeck().begin(); card != data.getDeck().end(); ++card)

      deck.push_back(sf::RectangleShape(sf::Vector2f(bruglesco::card_width, bruglesco::card_height)));
      deck[k].setTexture(&cardMap);
      deck[k].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));
      ++k;



      void GameScreen::positionCards()

      unsigned rows = 0;
      for (unsigned i = 2; i * i <= deck.size(); ++i)

      if (deck.size() % i == 0)

      rows = i;


      unsigned columns = deck.size() / rows;

      float padding_x = (bruglesco::canvas_width - columns * 128.f) / (columns + 1);
      float padding_y = (bruglesco::canvas_height - rows * 128.f) / (rows + 1);
      float x = padding_x;
      float y = padding_y;
      unsigned counter = 1;
      for (std::vector<sf::RectangleShape>::iterator card = deck.begin(); card != deck.end(); ++card)

      card->setPosition(x, y);
      if (counter < columns)

      ++counter;
      x += padding_x + bruglesco::card_width;

      else

      counter = 1;
      x = padding_x;
      y += padding_y + bruglesco::card_height;




      void GameScreen::highlightButtons()
      mouseIn == bruglesco::gameMouseIn::pause)

      pauseButton.setFillColor(sf::Color(60, 60, 60, 255));
      pauseString.setFillColor(sf::Color::Red);

      else

      pauseButton.setFillColor(sf::Color(120, 120, 120, 255));
      pauseString.setFillColor(sf::Color::Black);


      if (mouseIn == bruglesco::gameMouseIn::reset)

      returnToMain.setFillColor(sf::Color(60, 60, 60, 255));
      resetString.setFillColor(sf::Color::Red);

      else

      returnToMain.setFillColor(sf::Color(120, 120, 120, 255));
      resetString.setFillColor(sf::Color::Black);



      void GameScreen::matchFailDelay()

      if (delay == 0 && data.getFailedCards().size() == 2)

      delay = 240;



      void GameScreen::endFailDelay()

      data.resetDeck();


      void GameScreen::moveMatched()

      if (data.getMatchedCards().size() == 2)

      float x = bruglesco::player_card_x + bruglesco::player_card_x_offset * data.getPlayer().getScore();
      float y;
      if (data.playerOneTurn())

      y = bruglesco::player_one_card_y;

      else

      y = bruglesco::player_two_card_y;


      for (unsigned i = 0; i < data.getMatchedCards().size(); ++i)

      deck[data.getMatchedCards()[i]].setPosition(x, y);

      data.resetDeck();



      void GameScreen::adjustTexture()

      for (unsigned i = 0; i < data.getDeck().size(); ++i)

      if (data.getDeck()[i].checkState() == bruglesco::CardState::unmatched)

      deck[i].setTextureRect(sf::IntRect(2048, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));

      else

      deck[i].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[i].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));


      for (unsigned i = 0; i < data.getFailedCards().size(); ++i)

      deck[data.getFailedCards()[i]].setTextureRect(sf::IntRect(imageIdentifiers[data.getDeck()[data.getFailedCards()[i]].getMatch()] * 128, 0, static_cast<int>(bruglesco::card_width), static_cast<int>(bruglesco::card_height)));





      I tried to be a little more thorough in my description of my code this time around but that is not a skill I am great at yet. I hope that if you need any further explanation you will leave a comment and I will provide it.



      If I have any particular concerns for the code it would be the GameScreen class. It just seems to be large enough to worry. Should I try to abstract it further? any suggestions how? Of course I want any suggestions on any aspect of the code.









      share|improve this question












      share|improve this question




      share|improve this question








      edited May 25 at 17:24
























      asked May 25 at 15:52









      bruglesco

      9321518




      9321518

























          active

          oldest

          votes











          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%2f195167%2fviewport-for-card-match-game%23new-answer', 'question_page');

          );

          Post as a guest



































          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes










           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195167%2fviewport-for-card-match-game%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Python Lists

          Aion

          JavaScript Array Iteration Methods