game engine implementation, rendering tasks offloaded to Board from Game

This commit is contained in:
2025-01-13 01:17:42 +01:00
parent 7fff0f7f0b
commit 25377a10d7
20 changed files with 1130 additions and 28 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
# EN: The following is a list of files/catalogues, which should be ommited from commiting to the git repository. # EN: The following is a list of files/catalogues, which should be ommited from commiting to the git repository.
# PL: W tym pliku zawarta została lista plików/ścieżek, które powinny zostać pominięte w commitach do repozytorium git. # PL: W tym pliku zawarta została lista plików/ścieżek, które powinny zostać pominięte w commitach do repozytorium git.
Chinczyk188/.tablica_wynikow.csv.old
# EN: Visual Studio-related directories. # EN: Visual Studio-related directories.
# PL: Katalogi związane z Visual Studio. # PL: Katalogi związane z Visual Studio.

177
Chinczyk188/Board.cpp Normal file
View File

@@ -0,0 +1,177 @@
#include "Board.hpp"
#include <iostream>
#include <vector>
#include <string>
#include "Engine.hpp"
void Board::initVariables() {
// Ustaw wartości na domyślne.
this->window = nullptr;
// Wczytaj tło - bitmapę planszy.
// loadFromFile() zwraca True w przypadku pomyślnego
// wczytania tekstury.
if (!this->boardTexture.loadFromFile("res/sprites/board.png")) {
// Jeśli nie uda nam się wczytać tekstury:
std::cerr << "Uwaga: Nie udalo sie wczytac "
"wymaganej tekstury planszy z \"res/sprites/board.png\"!\n"
"Upewnij sie, ze plik .exe jest we wlasciwym katalogu, "
"a gra zostala w pelni wypakowana wraz z folderem \"res\".\n";
sf::sleep(sf::seconds(10));
this->window->close();
}
}
void Board::initWindow() {
// Dwuwymiarowy, wektor typu (u)nsigned int.
sf::Vector2u textureSize = this->boardTexture.getSize();
// Aby okno zmieściło się na większości wyświetlaczy,
// sprawmy, aby zajmowało ono około 450x450 pikseli,
// czyli dokładnie połowę z rozdzielczości tekstury planszy.
this->videoMode.width = textureSize.x / 2;
this->videoMode.height = textureSize.y / 2;
// Stwórz okno.
this->window = new sf::RenderWindow(
this->videoMode,
"Okno planszy - Chinczyk188",
sf::Style::Default);
}
void Board::initBoard() {
// Wczytaj wymaganą teksturę planszy
this->boardSprite.setTexture(this->boardTexture);
// Ustaw pozycję grafiki.
this->boardSprite.setPosition(0.0f, 0.0f);
}
void Board::initView() {
// Ze względu na to, że długość krawędzi okna jest dwa razy mniejsza
// od tekstury planszy, powinniśmy to zrównoważyć powiększając
// widok o tą samą wartość.
sf::Vector2f viewSize(
static_cast<float>(2 * this->videoMode.width),
static_cast<float>(2 * this->videoMode.height)
);
this->view.setSize(viewSize);
this->view.setCenter(viewSize.x / 2.0f, viewSize.y / 2.0f);
// Ustawiamy widok na wyznaczone wartości.
this->window->setView(this->view);
}
void Board::handleResize(unsigned int newWidth, unsigned int newHeight) {
// Aby zapobiec sytuacji, w której po zmianie rozmiaru
// okno byłoby większe od rozdzielczości ekranu,
// niech krawędź okna wynosi minimum z nowej szerokości i wysokości,
// zachowując przy tym proporcję 1:1 planszy.
unsigned int newSize = std::min(newWidth, newHeight);
this->window->setSize(sf::Vector2u(newSize, newSize));
}
bool Board::running() const {
// Akcesor dla isOpen()
return this->window->isOpen();
}
void Board::pollEvents() {
while (this->window->pollEvent(this->ev)) {
switch (this->ev.type) {
case sf::Event::Closed:
this->window->close();
break;
case sf::Event::Resized:
this->handleResize(this->ev.size.width, this->ev.size.height);
break;
case sf::Event::KeyPressed:
if (this->ev.key.code == sf::Keyboard::Escape)
this->window->close();
break;
}
}
}
void Board::update() {
// Obecnie wrapper dla metody
// pollEvents(), nasłuchiwanie wydarzeń.
this->pollEvents();
}
bool Board::manualUpdate() {
this->update();
this->render();
bool retval = this->window->pollEvent(this->ev);
this->lastEvent = this->ev;
return retval;
}
void Board::render() {
// Mechanizm renderowania
// Czyszczenie ekranu
this->window->clear(sf::Color::White);
// Rysowanie tła
this->window->draw(this->boardSprite);
// Mechanizm wyświetlania
// W celu wyświetlenia na ekranie wyniku renderowania
this->window->display();
}
void Board::updateAndRender() {
this->render();
this->update();
}
void Board::runAsThread() {
//this->thread = std::thread([this]() {this->updateAndRender();});
//this->threads.push_back(std::thread([this]() { this->updateAndRender(); }));
// this->thread = std::thread(&Board::updateAndRender, this);
//this->threads.front().launch();
}
void Board::closeWindow() {
this->window->close();
}
void Board::run() {
}

54
Chinczyk188/Board.hpp Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
#include <SFML/System/Thread.hpp>
#include <SFML/Window/Event.hpp>
#include <thread>
#include <SFML/Graphics.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
class Board {
private:
// Wskaźnik na okno.
sf::RenderWindow* window;
sf::VideoMode videoMode;
sf::Texture boardTexture;
sf::Sprite boardSprite;
public:
// Aby przekazać je do Engine
sf::Event ev;
sf::View view;
// Konstruktor, destruktor
void closeWindow();
// Akcesor
bool running() const;
//Board(): thread(&Board::updateAndRender, this) {};
// Metody klasy
void pollEvents();
void update();
void render();
void updateAndRender();
void run();
bool manualUpdate();
sf::Event lastEvent;
//std::vector<std::thread> threads;
//std::vector<sf::Thread> threads;
//sf::Thread thread;
void runAsThread();
// Sterowanie oknem
void initVariables();
void initWindow();
void initBoard();
void initView();
void handleResize(unsigned int newWidth, unsigned int newHeight);
};

View File

@@ -102,14 +102,16 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>SFML_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;SFML_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)res\SFML\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(ProjectDir)res\SFML\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>sfml-graphics-s-d.lib;opengl32.lib;freetype.lib;sfml-window-s-d.lib;winmm.lib;gdi32.lib;sfml-system-s-d.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>sfml-graphics-s-d.lib;opengl32.lib;freetype.lib;sfml-window-s-d.lib;winmm.lib;gdi32.lib;sfml-system-s-d.lib;sfml-audio-s-d.lib;openal32.lib;flac.lib;ogg.lib;vorbis.lib;vorbisenc.lib;vorbisfile.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)res\SFML\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)res\SFML\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
@@ -119,21 +121,36 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>SFML_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;SFML_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)res\SFML\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(ProjectDir)res\SFML\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>sfml-graphics-s.lib;opengl32.lib;freetype.lib;sfml-window-s.lib;winmm.lib;gdi32.lib;sfml-system-s.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>sfml-graphics-s.lib;opengl32.lib;freetype.lib;sfml-window-s.lib;winmm.lib;gdi32.lib;sfml-system-s.lib;sfml-audio-s.lib;openal32.lib;flac.lib;ogg.lib;vorbis.lib;vorbisenc.lib;vorbisfile.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)res\SFML\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)res\SFML\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Board.cpp" />
<ClCompile Include="Engine.cpp" />
<ClCompile Include="Game.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="Pawn.cpp" />
<ClCompile Include="Player.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Board.hpp" />
<ClInclude Include="Engine.hpp" />
<ClInclude Include="Game.hpp" />
<ClInclude Include="Pawn.hpp" />
<ClInclude Include="Player.hpp" />
<ClInclude Include="ssp.hpp" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@@ -18,5 +18,40 @@
<ClCompile Include="main.cpp"> <ClCompile Include="main.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Game.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Engine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Pawn.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Player.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Board.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Game.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ssp.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Engine.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Pawn.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Player.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Board.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

215
Chinczyk188/Engine.cpp Normal file
View File

@@ -0,0 +1,215 @@
#include "Engine.hpp"
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
#include <SFML/Window/Event.hpp>
#include <iostream>
#include <random>
const char* colorNames[] = {"czerwony", "niebieski", "zolty", "zielony"};
Engine::Engine(): currentPlayerIndex(0) {
if (!this->dicerollBuffer.openFromFile("res/audio/wardoctor17_diceroll.ogg")) {
std::cerr << "Nie udalo sie zaladowac efektu dzwiekowego rzutu kostka.\n\n";
this->dicerollLoaded = false;
}
if (!this->pawnmoveBuffer.openFromFile("res/audio/PeteBarry_pawnmove.ogg")) {
std::cerr << "Nie udalo sie zaladowac efektu dzwiekowego ruchu pionkow.\n\n";
this->pawnmoveLoaded = false;
}
}
void Engine::addPlayer(const std::string& name, unsigned int seed, short color) {
this->players.emplace_back(name, seed, color);
}
void Engine::initializeGame() {
int totalPawnsPerPlayer = 4; // 4 pionki na gracza
for (auto& player: players) {
player.initializePawns(totalPawnsPerPlayer);
}
}
void Engine::startGame(Board board) {
initializeGame();
this->board = board;
}
void Engine::nextTurn() {
this->turnCounter++;
std::cout << "-------------------- \n"
<< this->turnCounter << "\n"
<< "-------------------- \n";
Player& currentPlayer = players[currentPlayerIndex];
std::cout << "Ruch gracza " << currentPlayer.getName()
<< " (" << colorNames[currentPlayer.getColor()] << ").\n";
// Rzut kostką
if (dicerollLoaded) this->dicerollBuffer.play();
int diceRoll = rollDice(currentPlayer.getSeed(), currentPlayer.getRollCount());
std::cout << "Wylosowano " << diceRoll << ".\n";
currentPlayer.incrementRollCount();
sf::sleep(sf::milliseconds(1000));
// Wybieranie pionka
bool pickAPlace = false;
// Wylosowanie szóstki to specjalny przypadek
if (diceRoll == 6) {
if (currentPlayer.pawnsActive == 0) {
// Musi wyjść pionkiem
std::cout << currentPlayer.getName() << " wychodzi pierwszym pionkiem z bazy.\n";
currentPlayer.pawnsAtBase--;
currentPlayer.pawnsActive++;
this->pawnmoveBuffer.play();
sf::sleep(sf::milliseconds(2000));
} else if (currentPlayer.pawnsActive > 0 && currentPlayer.pawnsActive < 4) {
// Może wyjść z bazy albo ruszyć pionek
char choice;
std::cout << "Co chcesz zrobic?\n";
std::cout << "a) Wyjsc pionkiem z bazy\n"
<< "b) Wykonac ruch wyciagnietym wczesniej pionkiem\n"
<< "> ";
while (true) {
std::cin >> choice;
choice |= 32;
if (choice == 'a') {
pickAPlace = false;
std::cout << currentPlayer.getName() << " wychodzi "
<< currentPlayer.pawnsActive + 1 << ". pionkiem z bazy.\n";
currentPlayer.pawnsAtBase--;
currentPlayer.pawnsActive++;
this->pawnmoveBuffer.play();
sf::sleep(sf::milliseconds(2000));
break;
} else if (choice == 'b') {
if (currentPlayer.pawnsActive > 1) {
pickAPlace = true;
} else {
std::cout << currentPlayer.getName() << " rusza jedyny pionek "
<< diceRoll << " pol do przodu.\n";
this->pawnmoveBuffer.play();
sf::sleep(sf::milliseconds(2000));
}
break;
} else std::cout << "Podaj jedna z wymienionych odpowiedzi!\n> ";
}
} else if (currentPlayer.pawnsActive == 4) {
// Musi ruszyć pionek, o ile może
// TODO
pickAPlace = true;
this->pawnmoveBuffer.play();
}
} else {
// Musi ruszyć któryś z pionków
pickAPlace = true;
if (currentPlayer.pawnsActive == 1) {
// Jedyny pionek możemy ruszyć za niego
pickAPlace = false;
std::cout << currentPlayer.getName() << " rusza jedyny pionek "
<< diceRoll << " pol do przodu.\n";
this->pawnmoveBuffer.play();
sf::sleep(sf::milliseconds(2000));
}
}
if (currentPlayer.pawnsActive > 1 && pickAPlace) {
std::cout << "Wybierz prosze pionek na planszy ktorym chcesz wykonac ruch.\n";
}
// Użytkownik wybiera pionek na planszy, którym chce się ruszyć
int pickStatus = 0;
if (currentPlayer.pawnsActive == 0 || !pickAPlace) pickStatus = 8; // pomiń pętlę
while (pickStatus != 8) {
while (board.manualUpdate()) {
sf::Event currentEvent = board.lastEvent;
std::cout << "this-evtype: " << currentEvent.type << "\n";
switch (currentEvent.type) {
case sf::Event::Closed:
board.closeWindow();
// Zamknięcie okna powinno wyjść z tej pętli
pickStatus = 8;
break;
case sf::Event::MouseButtonPressed:
std::cout << "pretend i'm selecting a piece\n";
pickStatus = 4;
break;
case sf::Event::MouseButtonReleased:
std::cout << "pretend i'm releasing a piece\n";
this->pawnmoveBuffer.play();
sf::sleep(sf::seconds(1.0f));
if (pickStatus == 4) pickStatus = 8;
break;
case sf::Event::Resized:
std::cout << "resized triggered: " << currentEvent.size.width << " " << currentEvent.size.height << "\n";
board.handleResize(currentEvent.size.width, currentEvent.size.height);
break;
}
}
}
// TEST
// currentPlayer.tryMovingPawn(0, 0);
// if (!pickAPlace) {
// std::cout << "Brak ruchu.\n";
// }
if (currentPlayer.hasWon()) {
// Zostanie wykorzystane do zwiększenia liczby wygranych
this->winnerNickname = currentPlayer.getName();
announceWinner(currentPlayer);
} else {
// Iteruj po wektorze z graczami
currentPlayerIndex = (currentPlayerIndex + 1) % players.size();
}
sf::sleep(sf::milliseconds(1000));
std::cout << "\n";
return;
}
// Deterministycznie (lub nie) generuj kolejne rzuty kostką
int Engine::rollDice(unsigned int seed, unsigned int rollNumber) {
// Podane ziarno gracza oraz numer losowania zostaną użyte
// do wylosowania liczby od 1 do 6.
std::mt19937 rng(seed + rollNumber);
std::uniform_int_distribution<int> dist(1, 6);
return dist(rng);
}
bool Engine::isGameOver() const {
for (const auto& player: players) {
if (player.hasWon()) {
return true;
}
}
return false;
}
void Engine::announceWinner(const Player& player) const {
std::cout << "\n\n--------------------\n"
<< "Koniec gry!\n"
<< player.getName()
<< " (" << colorNames[player.getColor()] << ") wygrywa!\n";
}

47
Chinczyk188/Engine.hpp Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include "Board.hpp"
#include "Player.hpp"
#include <SFML/Graphics/View.hpp>
#include <vector>
#include <SFML/Audio/Music.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
#include <SFML/Window/Event.hpp>
class Engine {
public:
Engine();
// Przygotowanie zmiennych
void addPlayer(const std::string& name, unsigned int seed, short color);
void initializeGame();
// Pętla gry
void startGame(Board board);
bool isGameOver() const;
// Logika gry
void nextTurn();
int rollDice(unsigned int seed, unsigned int moveNumber);
// Audio
bool dicerollLoaded = true;
bool pawnmoveLoaded = true;
sf::Music dicerollBuffer;
sf::Music pawnmoveBuffer;
// Pomocnicze
std::string winnerNickname = "";
unsigned int turnCounter = 0;
// Z Game
Board board;
private:
std::vector<Player> players;
short currentPlayerIndex;
// Pomocnicze
void announceWinner(const Player& player) const;
};

View File

@@ -1,71 +1,410 @@
#include "Game.hpp" #include "Game.hpp"
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
#include <iostream> #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() { void Game::initVariables() {
// Ustaw wartości na domyślne.
this->window = nullptr; this->window = nullptr;
this->leaderboardLocation = LEADERBOARD_FILE;
// Wczytaj tło - bitmapę planszy.
// loadFromFile() zwraca True w przypadku pomyślnego
// wczytania tekstury.
if (!this->boardTexture.loadFromFile("res/sprites/board.png")) {
// Jeśli nie uda nam się wczytać tekstury:
std::cerr << "Uwaga: Nie udalo sie wczytac "
"wymaganej tekstury planszy z \"res/sprites/board.png\"!\n"
"Upewnij sie, ze plik .exe jest we wlasciwym katalogu, "
"a gra zostala w pelni wypakowana wraz z folderem \"res\".\n";
sf::sleep(sf::seconds(10));
this->window->close();
}
// 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);
} }
void Game::initWindow() { void Game::initWindow() {
this->videoMode = sf::VideoMode::getDesktopMode(); // Dwuwymiarowy, wektor typu (u)nsigned int.
this->videoMode.width /= 2; sf::Vector2u textureSize = this->boardTexture.getSize();
this->videoMode.height /= 2;
this->window = new sf::RenderWindow(this->videoMode, "Chinczyk188", sf::Style::Default); // Aby okno zmieściło się na większości wyświetlaczy,
// sprawmy, aby zajmowało ono około 450x450 pikseli,
// czyli dokładnie połowę z rozdzielczości tekstury planszy.
this->videoMode.width = textureSize.x / 2;
this->videoMode.height = textureSize.y / 2;
// Stwórz okno.
this->window = new sf::RenderWindow(
this->videoMode,
"Okno planszy - Chinczyk188",
sf::Style::Default);
}
void Game::initBoard() {
// Wczytaj wymaganą teksturę planszy
this->boardSprite.setTexture(this->boardTexture);
// Ustaw pozycję grafiki.
this->boardSprite.setPosition(0.0f, 0.0f);
}
void Game::initView() {
// Ze względu na to, że długość krawędzi okna jest dwa razy mniejsza
// od tekstury planszy, powinniśmy to zrównoważyć powiększając
// widok o tą samą wartość.
sf::Vector2f viewSize(
static_cast<float>(2 * this->videoMode.width),
static_cast<float>(2 * this->videoMode.height)
);
this->view.setSize(viewSize);
this->view.setCenter(viewSize.x / 2.0f, viewSize.y / 2.0f);
// Ustawiamy widok na wyznaczone wartości.
this->window->setView(this->view);
}
void Game::handleResize(unsigned int newWidth, unsigned int newHeight) {
// Aby zapobiec sytuacji, w której po zmianie rozmiaru
// okno byłoby większe od rozdzielczości ekranu,
// niech krawędź okna wynosi minimum z nowej szerokości i wysokości,
// zachowując przy tym proporcję 1:1 planszy.
unsigned int newSize = std::min(newWidth, newHeight);
this->window->setSize(sf::Vector2u(newSize, newSize));
} }
Game::Game() { Game::Game() {
//:
// thread(std::thread([this]() {
// while (this->running()) {
// this->update();
// this->render();
// sf::sleep(sf::milliseconds(100));
// } })) {
this->initVariables(); // Konstruktor klasy. Klasę Game tworzymy poprzez
this->initWindow(); // ustawienie domyślnych wartości i stworzenie okna.
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() { Game::~Game() {
delete this->window; // W destruktorze zapisujemy dane i usuwamy okno.
this->dumpLeaderboards();
delete this->window; // alternatywnie: this->window->close();
} }
const bool Game::running() const { const bool Game::running() const {
return this->window->isOpen(); // Akcesor dla isOpen()
//return this->window->isOpen();
return this->board.running();
} }
void Game::pollEvents() { void Game::pollEvents() {
while (this->window->pollEvent(this->ev)) { while (this->window->pollEvent(this->ev)) {
switch (this->ev.type) { switch (this->ev.type) {
case sf::Event::Closed: case sf::Event::Closed:
this->window->close(); this->window->close();
break; break;
case sf::Event::KeyPressed:
if (this->ev.key.code == sf::Keyboard::Escape) this->window->close(); case sf::Event::Resized:
this->handleResize(this->ev.size.width, this->ev.size.height);
break; break;
case sf::Event::KeyPressed:
if (this->ev.key.code == sf::Keyboard::Escape)
this->window->close();
break;
} }
} }
} }
void Game::update() { void Game::update() {
// Obecnie wrapper dla metody
// pollEvents(), nasłuchiwanie wydarzeń.
this->pollEvents(); this->pollEvents();
} }
void Game::render() { void Game::render() {
// EN: Render logic // Mechanizm renderowania
// PL: Mechanizm renderowania
this->window->clear(sf::Color(255, 0, 0, 255)); // Czyszczenie ekranu
this->window->clear(sf::Color::White);
// Rysowanie tła
this->window->draw(this->boardSprite);
// PL: Mechanizm wyświetlania // Mechanizm wyświetlania
// W celu wyświetlenia na ekranie wyniku renderowania // W celu wyświetlenia na ekranie wyniku renderowania
this->window->display(); this->window->display();
} }
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;
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;
#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] << "): ";
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();
//this->update();
//this->render();
}
// 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";
sf::sleep(sf::seconds(10.0f));
board.closeWindow();
}
}
}

View File

@@ -1,22 +1,59 @@
#pragma once #pragma once
#define LEADERBOARD_FILE "tablica_wynikow.csv"
#include "Board.hpp"
#include <thread>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp>
class Game { class Game {
private: private:
// Wskaźnik na okno.
sf::RenderWindow* window; sf::RenderWindow* window;
sf::VideoMode videoMode; sf::VideoMode videoMode;
sf::Event ev;
sf::Texture boardTexture;
sf::Sprite boardSprite;
std::vector<std::string> leaderboardName;
std::vector<unsigned int> leaderboardWins;
sf::Music soundBuffer1;
Board board;
// Metody
void initVariables(); void initVariables();
void initWindow(); void initWindow();
void initBoard();
void initView();
void playStartTune();
void handleResize(unsigned int newWidth, unsigned int newHeight);
public: public:
// Aby przekazać je do Engine
sf::Event ev;
std::string leaderboardLocation;
sf::View view;
// Konstruktor, destruktor
Game(); Game();
virtual ~Game(); ~Game();
// Akcesor
const bool running() const; const bool running() const;
void pollEvents();
// Metody klasy
void pollEvents();
void update(); void update();
void render(); void render();
void printGameWelcomeText() const;
void readLeaderboards(std::string leaderboardLocation);
void dumpLeaderboards(std::string leaderboardLocation = LEADERBOARD_FILE, bool verbose = true) const;
unsigned int updateLeaderboards(std::string winnerNickname);
void printLeaderboards() const;
void run();
}; };

View File

@@ -3,13 +3,14 @@
# EN: Warning: mingw-w64-x86_64-gcc-14.1.0-3 is the last version of gcc, which supports wildcards ("*.cpp"). # EN: Warning: mingw-w64-x86_64-gcc-14.1.0-3 is the last version of gcc, which supports wildcards ("*.cpp").
# PL: Uwaga: mingw-w64-x86_64-gcc-14.1.0-3 to ostatnia wspierana wersja gcc, w której można stosować wildcard'y ("*.cpp"). # PL: Uwaga: mingw-w64-x86_64-gcc-14.1.0-3 to ostatnia wspierana wersja gcc, w której można stosować wildcard'y ("*.cpp").
CC = "C:\\msys64\\mingw64\\bin\\g++.exe" CC = "C:\\msys64\\mingw64\\bin\\g++.exe"
DEPS = -lsfml-graphics-s -lsfml-window-s -lsfml-system-s -lopengl32 -lwinmm -lgdi32 -DSFML_STATIC DEPS = -lsfml-graphics-s -lsfml-window-s -lsfml-audio-s -lopenal32 -lflac -lvorbisenc -lvorbisfile -lvorbis -logg -lsfml-system-s -lopengl32 -lwinmm -lgdi32 -lpthread
#LINK = -L. LINK = -L. -Lres/SFML/lib-mingw/
FLAGS = -DSFML_STATIC # -DCHINCZYK188_IGNORE_USER_SEED
OUTPUT = output.exe OUTPUT = output.exe
CPPSTD = c++17 CPPSTD = c++17
default: default:
$(CC) -g "*.cpp" $(DEPS) $(LINK) -std=$(CPPSTD) -static -static-libgcc -fno-keep-inline-dllexport -o $(OUTPUT) $(CC) -g "*.cpp" $(LINK) $(DEPS) -std=$(CPPSTD) $(FLAGS) -static -static-libgcc -fno-keep-inline-dllexport -Os -s -Wl,--build-id=none -o $(OUTPUT)
run: default run: default
$(OUTPUT) $(OUTPUT)

59
Chinczyk188/Pawn.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include "Pawn.hpp"
// Konstruktor
Pawn::Pawn() {
this->position = -1;
this->grid_x = -1;
this->grid_y = -1;
}
// Gettery i settery
int Pawn::getRelativePosition() const {
return this->position;
}
void Pawn::setRelativePosition(int position) {
this->position = position;
}
int Pawn::move(int fields) {
switch (fields) {
case -1:
// Przenieś do bazy
// 0 = ok
return 0;
case 0:
// Wstaw na planszę
// 0 = ok
return 0;
default:
// Sprawdź:
// a) czy da się wejść na to miejsce
// b) czy są pionki do zbicia
// c) czy są inne pionki tego samego gracza
return 0;
}
}
bool Pawn::isAtBase() const {
return true;
}
void Pawn::sendToBase() {
}

22
Chinczyk188/Pawn.hpp Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
class Pawn {
public:
Pawn();
// Gettery i settery
int getRelativePosition() const;
void setRelativePosition(int position);
bool isAtBase() const;
bool isActive() const;
bool isAtHome() const;
void sendToBase();
int move(int fields);
private:
int position; // -1 oznacza pionek w bazie
int grid_x;
int grid_y;
};

62
Chinczyk188/Player.cpp Normal file
View File

@@ -0,0 +1,62 @@
#include "Player.hpp"
Player::Player(const std::string& name, unsigned int seed, short color):
name(name), seed(seed), color(color), pawnsFinished(0) {}
const std::string& Player::getName() const {
return name;
}
unsigned int Player::getSeed() const {
return seed;
}
void Player::initializePawns(int totalPawns) {
pawns.resize(totalPawns);
}
int Player::getRollCount() const {
return this->rollCount;
}
void Player::incrementRollCount() {
this->rollCount++;
}
short Player::getColor() const {
return this->color;
}
short Player::tryMovingPawn(short pawnID, short fields) {
// this->pawnsFinished = 4; // do testowania, wymuszania zakończenia gry
return 0;
}
void Player::movePawn(int pawnIndex, int steps) {
if (pawnIndex < 0 || pawnIndex >= pawns.size()) return;
// TODO: zaimplementować logikę ruchu pionków
Pawn& pawn = pawns[pawnIndex];
if (pawn.isAtBase()) {
if (steps == 6) {
pawn.setRelativePosition(1);
}
} else {
int newPosition = pawn.getRelativePosition() + steps;
// TODO: sprawdź pozycję pionków
pawn.setRelativePosition(newPosition);
if (newPosition >= 51/* "koniec", do przerobienia */) {
pawn.setRelativePosition(51/* "koniec" */);
pawnsFinished++;
}
}
}
std::vector<Pawn>& Player::getPawns() {
return pawns;
}
bool Player::hasWon() const {
return pawnsFinished == pawns.size();
}

38
Chinczyk188/Player.hpp Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <vector>
#include "Pawn.hpp"
class Player {
public:
Player(const std::string& name, unsigned int seed, short color);
// Akcesory
const std::string& getName() const;
unsigned int getSeed() const;
int getRollCount() const;
short getColor() const;
void incrementRollCount();
// Zarządzanie pionkami
void initializePawns(int totalPawns);
void movePawn(int pawnIndex, int steps);
short tryMovingPawn(short pawnID, short fields);
std::vector<Pawn>& getPawns();
short pawnsAtBase = 4;
short pawnsActive = 0;
short pawnsAtHome = 0;
bool hasWon() const;
private:
std::string name;
unsigned int seed;
std::vector<Pawn> pawns;
short color;
int rollCount = 0;
int pawnsFinished; // to nie to samo, co pawnsAtHome
};

View File

@@ -6,9 +6,7 @@ int main() {
while (game.running()) { while (game.running()) {
game.update(); game.run();
game.render();
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB