Last active
December 27, 2015 18:29
-
-
Save MORTAL2000/9b8ac45d28abb97f6281 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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