Skip to content

Instantly share code, notes, and snippets.

@MORTAL2000
Last active December 27, 2015 18:29
Show Gist options
  • Save MORTAL2000/9b8ac45d28abb97f6281 to your computer and use it in GitHub Desktop.
Save MORTAL2000/9b8ac45d28abb97f6281 to your computer and use it in GitHub Desktop.
#include <SFML/Graphics.hpp>
#include <array>
#include <random>
#include <iostream>
#include <algorithm>
#include <functional>
namespace
{
constexpr auto MaxCards = 16u;
template<class T = std::mt19937, std::size_t N = T::state_size>
auto ProperlySeededRandomEngine() -> std::enable_if_t<!!N, T>
{
std::array<T::result_type, N> seed_data;
thread_local std::random_device source;
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(source));
std::seed_seq seeds(std::begin(seed_data), std::end(seed_data));
thread_local T seeded_engine(seeds);
return seeded_engine;
}
}
class Text
{
public:
Text(sf::RenderWindow& window, sf::Font& font)
: window(window)
{
menu1.setFont(font);
menu1.setCharacterSize(80u);
menu1.setColor(sf::Color::Green);
menu1.setString("Memory");
menu1.setPosition(window.getSize().x / 2u - menu1.getGlobalBounds().width / 2u, 200.f);
menu2.setFont(font);
menu2.setCharacterSize(30u);
menu2.setColor(sf::Color::Green);
menu2.setString("Press SPACE to play");
menu2.setPosition(window.getSize().x / 2u - menu2.getGlobalBounds().width / 2u, 400.f);
endGame1.setFont(font);
endGame1.setCharacterSize(50u);
endGame1.setColor(sf::Color::Green);
endGame1.setString("You have beat the game!");
endGame1.setPosition(window.getSize().x / 2u - endGame1.getGlobalBounds().width / 2u, 250.f);
endGame2.setFont(font);
endGame2.setCharacterSize(30u);
endGame2.setColor(sf::Color::Green);
endGame2.setString("Press SPACE to play again");
endGame2.setPosition(window.getSize().x / 2u - endGame2.getGlobalBounds().width / 2u, 350.f);
}
void drawMenu()
{
window.draw(menu1);
window.draw(menu2);
}
void drawEndGame()
{
window.draw(endGame1);
window.draw(endGame2);
}
private:
sf::RenderWindow& window;
sf::Text menu1;
sf::Text menu2;
sf::Text endGame1;
sf::Text endGame2;
};
class Cards
{
enum State
{
Showing,
Picking,
ShowLast,
}state = Showing;
public:
Cards(sf::RenderWindow& window, sf::Font& font)
: window(window)
, randomEngine(ProperlySeededRandomEngine())
, backCardTexture()
, frontCardTexture()
, backCardSprite()
, frontCardSprite()
, deltaTime(sf::Time::Zero)
, clickedCard(-1)
, shownCardsCounter()
, gameRound(1)
{
if (!backCardTexture.loadFromFile("resources/backside.png"))
std::cerr << "can't load backCardTexture\n";
const auto cardsPerRow = 4u;
const auto horizontalSpacing = 120.f;
const auto verticalSpacing = 120.f;
const sf::Vector2f positionOfTopLeft(170.f, 180.f);
for (auto i = 0u; i < MaxCards; ++i)
{
if (!frontCardTexture[i].loadFromFile("resources/" + std::to_string(i) + ".png"))
std::cerr << "can't load frontCardTexture: " + std::to_string(i) + "\n";
sf::Vector2f position(horizontalSpacing * (i % cardsPerRow), verticalSpacing * (i / cardsPerRow));
backCardSprite[i].setTexture(backCardTexture);
backCardSprite[i].setPosition(positionOfTopLeft.x + position.x, positionOfTopLeft.y + position.y);
frontCardSprite[i].setTexture(frontCardTexture[i]);
frontCardSprite[i].setPosition(positionOfTopLeft.x + position.x, positionOfTopLeft.y + position.y);
}
std::shuffle(frontCardSprite.begin(), frontCardSprite.end(), randomEngine);
roundCounter.setFont(font);
roundCounter.setCharacterSize(35u);
roundCounter.setColor(sf::Color::Green);
roundCounter.setString("Round 0");
roundCounter.setPosition(window.getSize().x / 2u - roundCounter.getGlobalBounds().width / 2u, 70.f);
}
void updateInput()
{
if (state != Picking)
return;
sf::Vector2i position = sf::Mouse::getPosition(window);
sf::Vector2f mousePos = window.mapPixelToCoords(position);
if (clickCards(mousePos))
state = ShowLast;
}
bool draw(sf::Time elapsedTime)
{
for (const auto& card : backCardSprite)
window.draw(card);
roundCounter.setString("Round " + std::to_string(gameRound));
window.draw(roundCounter);
switch (state)
{
case Showing:
if (showCards(elapsedTime))
state = Picking;
break;
case Picking:
drawClickedCard(elapsedTime);
break;
case ShowLast:
if (drawClickedCard(elapsedTime))
{
if (gameRound == MaxCards)
return true;
else
state = Showing;
}
}
return false;
}
void reset()
{
gameRound = 1;
state = Showing;
std::shuffle(frontCardSprite.begin(), frontCardSprite.end(), randomEngine);
}
private:
bool showCards(sf::Time elapsedTime)
{
deltaTime += elapsedTime;
for (auto i = 0u; i <= gameRound; ++i)
{
if (deltaTime >= sf::seconds(static_cast<float>(i)) && deltaTime <= sf::seconds(static_cast<float>(i + 1)))
window.draw(frontCardSprite[i]);
}
if (deltaTime > sf::seconds(static_cast<float>(gameRound + 1)))
{
deltaTime = sf::Time::Zero;
shownCardsCounter = gameRound + 1;
return true;
}
return false;
}
bool drawClickedCard(sf::Time elapsedTime)
{
if (shownCardsCounter == 0)
{
deltaTime += elapsedTime;
if (deltaTime >= sf::seconds(1))
{
gameRound++;
clickedCard = -1;
deltaTime = sf::Time::Zero;
return true;
}
}
if (clickedCard != -1)
window.draw(frontCardSprite[clickedCard]);
return false;
}
bool clickCards(sf::Vector2f mousePos)
{
auto index = (gameRound + 1) - shownCardsCounter;
for (auto i = 0u; i <= gameRound; ++i)
{
if (frontCardSprite[i].getGlobalBounds().contains(mousePos) && i == index)
{
clickedCard = i;
shownCardsCounter--;
}
}
return shownCardsCounter == 0;
}
private:
sf::RenderWindow& window;
sf::Text roundCounter;
std::mt19937& randomEngine;
sf::Texture backCardTexture;
std::array<sf::Texture, MaxCards> frontCardTexture;
std::array<sf::Sprite, MaxCards> backCardSprite;
std::array<sf::Sprite, MaxCards> frontCardSprite;
sf::Time deltaTime;
int clickedCard;
int shownCardsCounter;
std::size_t gameRound;
};
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 720), "Memory");
window.setVerticalSyncEnabled(true);
sf::Font font;
if (!font.loadFromFile("resources/ARCADECLASSIC.TTF"))
std::cerr << "can't load fonts\n";
Text text(window, font);
Cards cards(window, font);
enum GameState
{
Menu,
Game,
End
}state = Menu;
sf::Clock clock;
while (window.isOpen())
{
sf::Time timeElapsed = clock.restart();
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyReleased:
if (event.key.code == sf::Keyboard::Space && state == Menu)
state = Game;
if (event.key.code == sf::Keyboard::Space && state == End)
{
cards.reset();
state = Menu;
}
break;
case sf::Event::MouseButtonPressed:
if (event.mouseButton.button == sf::Mouse::Left)
cards.updateInput();
break;
}
}
window.clear();
switch (state)
{
case Menu:
text.drawMenu();
break;
case Game:
if (cards.draw(timeElapsed))
state = End;
break;
case End:
text.drawEndGame();
}
window.display();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment