Files
Chinczyk188/Chinczyk188/Game.cpp

302 lines
8.7 KiB
C++

#include "Game.hpp"
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <filesystem> // sprawdzenie, czy plik istnieje
#include "ssp.hpp" // nagłówkowy parser plików .csv
#include "Engine.hpp"
void Game::printGameWelcomeText() const {
std::cout << "\n"
" .o88b. db db d888888b d8b db .o88b. d88888D db db db dD db .d888b. .d888b.\n"
"d8P Y8 88 88 `88' 888o 88 d8P Y8 YP d8' `8b d8' 88 ,8P' o88 88 8D 88 8D\n"
"8P 88ooo88 88 88V8o 88 8P d8' `8bd8' 88,8P 88 `VoooY' `VoooY'\n"
"8b 88~~~88 88 88 V8o88 8b d8' 88 88`8b 88 .d~~~b. .d~~~b.\n"
"Y8b d8 88 88 .88. 88 V888 Y8b d8 d8' db 88 88 `88. 88 88 8D 88 8D\n"
" `Y88P' YP YP Y888888P VP V8P `Y88P' d88888P YP YP YD VP `Y888P' `Y888P'\n"
"\n";
std::cout << "Witaj w Chinczyku!\nAutor: B.Z. (2 EF-DI, L01, 177188)\n";
std::cout << "Kod zrodlowy: https://gitea.7o7.cx/sherl/Chinczyk188 na licencji GPLv3\n";
std::cout << "Instrukcja gry powinna zostac dolaczona do tej kopii gry, w przeciwnym razie\n"
"jest ona dostepna w repozytorium git.\n\n";
}
void Game::readLeaderboards(std::string leaderboardLocation) {
try {
// Spróbuj wczytać plik z zapisanymi wynikami
ss::parser<ss::throw_on_error> p{leaderboardLocation, " "};
for (const auto& [csvName, csvWins]: p.iterate<std::string, unsigned int>()) {
this->leaderboardName.push_back(csvName);
this->leaderboardWins.push_back(csvWins);
}
}
catch (ss::exception& e) {
// Jeżeli nie udało się wczytać pliku, spróbuj wczytać kopię zapasową
if (std::filesystem::exists("." + leaderboardLocation + ".old")) {
ss::parser<ss::throw_on_error> p{"." + leaderboardLocation + ".old", " "};
for (const auto& [csvName, csvWins]: p.iterate<std::string, unsigned int>()) {
this->leaderboardName.push_back(csvName);
this->leaderboardWins.push_back(csvWins);
}
// Przekopiuj po cichu (verbose=false) dane do pliku głównego
this->dumpLeaderboards(leaderboardLocation, false);
return;
}
std::cerr << "Wystapil blad przy probie wczytania listy wynikow. \n"
"Gra sprobuje naprawic blad poprzez utworzenie nowego pliku z lista wynikow.\n"
"Nacisnij CTRL+C w przeciagu 10 sekund aby anulowac i wyjsc z programu.\n";
sf::sleep(sf::seconds(15));
// Utwórz plik
std::ofstream leaderboardFile;
leaderboardFile.open(leaderboardLocation);
leaderboardFile << "\n";
leaderboardFile.close();
// Przykładowe dane
this->leaderboardName.push_back("Anonymous");
this->leaderboardWins.push_back(1);
}
}
void Game::dumpLeaderboards(std::string leaderboardLocation, bool verbose) const {
// Otwórz strumień wyjściowy do ścieżki leaderboardLocation
std::ofstream leaderboardFile;
leaderboardFile.open(leaderboardLocation);
for (int i = 0; i < (this->leaderboardName.size() & this->leaderboardWins.size()); i++) {
// Zapisz dane z wektora w formacie csv, spacja to separator
leaderboardFile << this->leaderboardName[i] << " " << this->leaderboardWins[i] << "\n";
}
leaderboardFile.close();
// Zmienna verbose decyduje o tym, czy chcemy wyświetlić komunikat o zapisie
if (verbose) std::cout << "Zapisano dane do pliku " << leaderboardLocation << ".\n";
}
unsigned int Game::updateLeaderboards(std::string winnerNickname) {
bool updated = false;
unsigned int totalWins = 0;
for (int i = 0; i < (this->leaderboardName.size() & this->leaderboardWins.size()); i++) {
// Szukaj nick we wczytanych danych z pliku CSV
if (this->leaderboardName[i] == winnerNickname) {
totalWins = ++this->leaderboardWins[i];
updated = true;
break;
}
}
if (!updated) {
// Jeżeli to pierwsza wygrana tego gracza, to dodaj go do wektora
this->leaderboardName.push_back(winnerNickname);
this->leaderboardWins.push_back(1);
totalWins = 1;
}
// Zapisz po cichu zmiany do obu plików CSV
this->dumpLeaderboards(LEADERBOARD_FILE, false);
this->dumpLeaderboards(std::string(".") + LEADERBOARD_FILE + std::string(".old"), false);
return totalWins;
}
void Game::printLeaderboards() const {
// Zamienia dwa wektory w parę wektorów
// https://stackoverflow.com/a/18479184
std::vector<std::pair<std::string, int>> vectorPair(this->leaderboardName.size());
for (unsigned int i = 0; i < vectorPair.size(); i++) {
vectorPair[i] = std::make_pair(this->leaderboardName[i], this->leaderboardWins[i]);
}
// Sortuje malejąco parę wektorów biorąc pod uwagę wartości w tym drugim
// https://stackoverflow.com/a/279878
std::sort(vectorPair.begin(), vectorPair.end(), [](auto &left, auto &right) {
return left.second > right.second;
});
for (const auto& [name, wins]: vectorPair) {
std::cout << "- " << name << ", " << wins << " wygranych\n";
}
}
void Game::playStartTune() {
// Jeżli plik welcome_alt.ogg istnieje, załaduj go, a potem odtwórz.
if (!this->soundBuffer1.openFromFile("res/audio/welcome_alt.ogg"))
return;
// Aby dało się usłyszeć dźwięk, nie może on wyjść poza zakres (out of scope).
// Z tego powodu zapisujemy odnośnik do bufora w pamięci programu (w klasie Game).
this->soundBuffer1.play();
}
void Game::initVariables() {
// Ustaw wartości na domyślne.
this->leaderboardLocation = LEADERBOARD_FILE;
// Spróbuj wczytać listę wyników z pliku
this->readLeaderboards(leaderboardLocation);
// Zapisz po cichu (stąd false) kopię zapasową listy wyników
this->dumpLeaderboards("." + leaderboardLocation + ".old", false);
}
Game::Game() {
//:
// thread(std::thread([this]() {
// while (this->running()) {
// this->update();
// this->render();
// sf::sleep(sf::milliseconds(100));
// } })) {
// Konstruktor klasy. Klasę Game tworzymy poprzez
// ustawienie domyślnych wartości i stworzenie okna.
this->initVariables();
this->board.initVariables();
this->board.initWindow();
this->board.initBoard();
this->board.initView(); // widok musi być zainicjalizowany po oknie
// Puść melodyjkę
this->playStartTune();
// Wyświetl witający tekst
this->printGameWelcomeText();
}
Game::~Game() {
// W destruktorze zapisujemy dane i usuwamy okno.
this->dumpLeaderboards();
}
const bool Game::running() const {
// Akcesor dla isOpen()
//return this->window->isOpen();
return this->board.running();
}
void Game::run() {
Engine engine;
board.updateAndRender();
// this->update();
// this->render();
int numPlayers = 2;
std::cout << "Podaj prosze liczbe graczy (2-4):\n> ";
// std::cin >> numPlayers;
numPlayers = stoi(board.asyncStrCin());
std::cout << "\n";
if (numPlayers < 2) numPlayers = 2;
if (numPlayers > 4) numPlayers = 4;
const char* colorNames[] = {"czerwony", "niebieski", "zolty", "zielony"};
// Uzyskaj dane o użytkownikach
for (int i = 0; i < numPlayers; i++) {
std::string name;
std::string str_seed = std::to_string(std::time(nullptr)); // czas
std::cout << "Wpisz nazwe gracza " << (i + 1) << " (" << colorNames[i] << "): ";
//std::cin >> name;
name = board.asyncStrCin();
// Zignoruj zawartość bufora do \n, aby zapobiec nadpisywaniu ziarna
// poprzez podanie nazwy ze znakami białymi.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
#ifndef CHINCZYK188_IGNORE_USER_SEED
// Jeżeli nie została zdefiniowana flaga do ignorowania ziarna użytkownika
// (deterministyczne losowanie), to pozwól na wprowadzanie ziaren.
std::cout << "Wpisz ziarno gracza " << (i + 1) << " (" << colorNames[i] << "): ";
str_seed = board.asyncStrCin();
// std::cin >> str_seed;
#endif
std::cout << "\n";
short color = static_cast<short>(i);
unsigned int seed = std::hash<std::string_view>{}(str_seed);
engine.addPlayer(name, seed, color);
board.updateAndRender();
// Podobnie, jak wyżej
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// Przekaż ev i window, aby obsługiwać zdarzenia
// w Engine
engine.startGame(this->board);
// Główna pętla gry
while (board.running()) {
board.updateAndRender();
// this->update(); // do threadów
//this->render();
if (!engine.isGameOver()) {
engine.nextTurn();
} else {
// updateLeaderboard() inkrementuje i zwraca liczbę wygranych przekazanego gracza
std::cout << "To jego(/jej) "
<< updateLeaderboards(engine.winnerNickname) << ". wygrana!\n";
std::cout << "--------------------\n\n"
<< "Statystyki:\n";
this->printLeaderboards();
std::cout << "\n";
// Zapisz do pliku CSV
this->dumpLeaderboards(this->leaderboardLocation);
this->dumpLeaderboards("." + this->leaderboardLocation + ".old", false);
this->board.smartSleep(10000);
board.closeWindow();
}
}
}