new suite of helper functions, draw pawns to the screen (not fully working yet)

This commit is contained in:
2025-01-27 02:57:50 +01:00
parent c0a4e3bdb2
commit c378e34e62
10 changed files with 403 additions and 15 deletions

View File

@@ -1,4 +1,5 @@
#include "Board.hpp" #include "Board.hpp"
#include "Utils.hpp"
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <thread> #include <thread>
@@ -23,6 +24,20 @@ void Board::initVariables() {
this->window->close(); this->window->close();
} }
// Podobnie, wczytaj czcionkę Ubuntu.
if (!this->Ubuntu.loadFromFile("res/fonts/Ubuntu-Regular.ttf")) {
// Jeśli nie uda nam się wczytać czcionki:
std::cerr << "Uwaga: Nie udalo sie wczytac "
"wymaganej czcionki Ubuntu z \"res/fonts/Ubuntu.ttf\"!\n"
"Upewnij sie, ze plik .exe jest we wlasciwym katalogu, "
"a gra zostala w pelni wypakowana wraz z folderem \"res\".\n";
this->smartSleep(10000);
this->window->close();
}
} }
void Board::initWindow() { void Board::initWindow() {
@@ -141,7 +156,13 @@ void Board::render() {
// Rysowanie tła // Rysowanie tła
this->window->draw(this->boardSprite); this->window->draw(this->boardSprite);
for (sf::CircleShape pawn: pieces) {
this->window->draw(pawn);
}
for (sf::Text pawnText: piecesText) {
this->window->draw(pawnText);
}
// Mechanizm wyświetlania // Mechanizm wyświetlania
// W celu wyświetlenia na ekranie wyniku renderowania // W celu wyświetlenia na ekranie wyniku renderowania
@@ -230,7 +251,7 @@ void Board::selectAField(short &pickStatus, short &field) {
mapped = this->window->mapPixelToCoords(mousePosition, this->view); mapped = this->window->mapPixelToCoords(mousePosition, this->view);
if (mapped.x >= 0 && mapped.x <= 1100 && mapped.y >= 0 && mapped.y <= 1100) { if (mapped.x >= 0 && mapped.x <= 1100 && mapped.y >= 0 && mapped.y <= 1100) {
// Wyznacz ponownie field, na wypadek jeśli użytkownik zmienił zdanie // Wyznacz ponownie field, na wypadek jeśli użytkownik zmienił zdanie
field = static_cast<short>(mapped.y / 100) * 11 + static_cast<short>(mapped.x / 100); field = XYToField(mapped.x, mapped.y);
} }
pickStatus = 8; pickStatus = 8;
} }
@@ -246,6 +267,56 @@ void Board::selectAField(short &pickStatus, short &field) {
} }
/**
* @brief Draws a pawn (or, optionally, multiple).
*
* @param[in] color Pawn color
* @param[in] field Field in fields matrix to draw in
* @param[in] howMany (Optional, default: 1) Piece count overlay
*/
void Board::drawPawn(short color, short field, short howMany) {
sf::CircleShape circle(40.f);
cords3 outlineColor = color2RGBOutline(color);
cords3 fillerColor = color2RGBFiller(color);
cords2 XY = fieldToXY(field);
circle.setOutlineColor(sf::Color(outlineColor.x - 20, outlineColor.y - 20, outlineColor.z - 20));
circle.setFillColor( sf::Color( fillerColor.x - 20, fillerColor.y - 20, fillerColor.z - 20));
circle.setPosition(XY.x - 50 + 10, XY.y - 50 + 10);
circle.setOutlineThickness(5);
this->pieces.push_back(circle);
if (howMany > 1) {
sf::Text number(std::to_string(howMany), Ubuntu);
number.setCharacterSize(30);
number.setFillColor(sf::Color::Black);
number.setPosition(XY.x, XY.y);
this->piecesText.push_back(number);
}
return;
}
void Board::drawPawns() {
/*this->pieces.clear();
this->piecesText.clear();*/
for (int i = 0; i < 121; i++) {
// Wyznacz kolor i zlicz pionki
short color = -1;
short pawnsFound = 0;
for (int j = 0; j < 4; j++) {
if (fields[i][j] != 0) {
color = fields[i][j] - 4;
pawnsFound++;
}
}
if (pawnsFound != 0) {
this->drawPawn(color, i, pawnsFound);
}
}
}
void Board::closeWindow() { void Board::closeWindow() {
this->window->close(); this->window->close();

View File

@@ -15,10 +15,15 @@ class Board {
sf::VideoMode videoMode; sf::VideoMode videoMode;
sf::Texture boardTexture; sf::Texture boardTexture;
sf::Sprite boardSprite; sf::Sprite boardSprite;
sf::Font Ubuntu;
// void _thread_wait_for_str_cin(bool &done, std::string &text); // Rysowanie obiektów na ekranie
// void _thread_wait_for_str_getline(bool& done, std::string& text); void drawPawn(short color, short field, short howMany = 1);
// Obiekty przechowujące pionki i tekst w celu renderowania
std::vector<sf::CircleShape> pieces{};
std::vector<sf::Text> piecesText{};
public: public:
@@ -26,6 +31,8 @@ class Board {
sf::Event ev; sf::Event ev;
sf::View view; sf::View view;
short fields[121][4] = {{}};
// Konstruktor, destruktor // Konstruktor, destruktor
void closeWindow(); void closeWindow();
@@ -39,6 +46,7 @@ class Board {
void updateAndRender(); void updateAndRender();
void run(); void run();
bool manualUpdate(); bool manualUpdate();
void drawPawns();
sf::Event lastEvent; sf::Event lastEvent;
// Sterowanie oknem // Sterowanie oknem

View File

@@ -1,4 +1,5 @@
#include "Engine.hpp" #include "Engine.hpp"
#include "Utils.hpp"
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System/Sleep.hpp> #include <SFML/System/Sleep.hpp>
#include <SFML/System/Time.hpp> #include <SFML/System/Time.hpp>
@@ -33,12 +34,19 @@ void Engine::initializeGame() {
int totalPawnsPerPlayer = 4; // 4 pionki na gracza int totalPawnsPerPlayer = 4; // 4 pionki na gracza
for (auto& player: players) { for (auto& player: players) {
player.initializePawns(totalPawnsPerPlayer); player.initializePawns(totalPawnsPerPlayer);
short playerColor = player.getColor();
for (int i = 0; i < 4; i++) {
// 3. najmniej znaczący bit informuje o zajęciu pola
// Ponadto przyjmujemy, że jeśli tylko 0 miejsce jest
// zajęte, to możemy wyświetlić
this->board.fields[getPawnInitialPosition(playerColor, i)][0] = playerColor + 4;
}
} }
} }
void Engine::startGame(Board board) { void Engine::startGame(Board board) {
initializeGame();
this->board = board; this->board = board;
initializeGame();
} }
void Engine::nextTurn() { void Engine::nextTurn() {
@@ -54,6 +62,7 @@ void Engine::nextTurn() {
// Rysuj pionki // Rysuj pionki
// ... // ...
this->board.drawPawns();
// Rzut kostką // Rzut kostką
if (dicerollLoaded) this->dicerollBuffer.play(); if (dicerollLoaded) this->dicerollBuffer.play();
@@ -72,9 +81,7 @@ void Engine::nextTurn() {
// 0 pionków na planszy - musi wyjść pionkiem // 0 pionków na planszy - musi wyjść pionkiem
case 0: case 0:
std::cout << currentPlayer.getName() << " wychodzi pierwszym pionkiem z bazy.\n"; std::cout << currentPlayer.getName() << " wychodzi pierwszym pionkiem z bazy.\n";
currentPlayer.hasLeftBase = true; this->spawnPiece(currentPlayer.getColor(), 0);
currentPlayer.pawnsAtBase--;
currentPlayer.pawnsActive++;
this->pawnmoveBuffer.play(); this->pawnmoveBuffer.play();
this->board.smartSleep(2000); this->board.smartSleep(2000);
break; break;
@@ -100,8 +107,7 @@ void Engine::nextTurn() {
pickAPlace = false; pickAPlace = false;
std::cout << currentPlayer.getName() << " wychodzi " std::cout << currentPlayer.getName() << " wychodzi "
<< currentPlayer.pawnsActive + 1 << ". pionkiem z bazy.\n"; << currentPlayer.pawnsActive + 1 << ". pionkiem z bazy.\n";
currentPlayer.pawnsAtBase--; this->spawnPiece(currentPlayer.getColor(), currentPlayer.pawnsActive + 1);
currentPlayer.pawnsActive++;
this->pawnmoveBuffer.play(); this->pawnmoveBuffer.play();
this->board.smartSleep(2000); this->board.smartSleep(2000);
break; break;
@@ -113,6 +119,11 @@ void Engine::nextTurn() {
} else { } else {
std::cout << currentPlayer.getName() << " rusza jedyny pionek " std::cout << currentPlayer.getName() << " rusza jedyny pionek "
<< diceRoll << " pol do przodu.\n"; << diceRoll << " pol do przodu.\n";
for (int i = 0; i < 4; i++) {
short* relativePawns = currentPlayer.getRelativePawns();
if (relativePawns[i] >= 0)
this->movePiece(currentPlayer.getColor(), relPosToField(currentPlayer.getColor(), relativePawns[i]), diceRoll);
}
this->pawnmoveBuffer.play(); this->pawnmoveBuffer.play();
this->board.smartSleep(2000); this->board.smartSleep(2000);
} }
@@ -168,12 +179,25 @@ void Engine::nextTurn() {
} }
// Użytkownik wybiera pionek na planszy, którym chce się ruszyć // Użytkownik wybiera pionek na planszy, którym chce się ruszyć
bool moveResult = false;
short pickStatus = 0; short pickStatus = 0;
short selectedField = -1; short selectedField = -1;
if (!pickAPlace) pickStatus = 8; // pomiń pętlę if (!pickAPlace) pickStatus = 8; // pomiń pętlę
while (pickStatus != 8) { while (!moveResult) {
this->board.selectAField(pickStatus, selectedField); while (pickStatus != 8) {
this->board.selectAField(pickStatus, selectedField);
}
if (pickAPlace) {
moveResult = this->movePiece(currentPlayer.getColor(), selectedField, diceRoll);
if (!moveResult) std::cout << "Nie mozna wykonac tego ruchu.\n";
} else {
// Nadpisz moveResult jeżeli gracz nie wybiera pionka
moveResult = true;
}
} }
@@ -192,6 +216,8 @@ void Engine::nextTurn() {
// std::cout << "Brak ruchu.\n"; // std::cout << "Brak ruchu.\n";
// } // }
if (currentPlayer.hasWon()) { if (currentPlayer.hasWon()) {
// Zostanie wykorzystane do zwiększenia liczby wygranych // Zostanie wykorzystane do zwiększenia liczby wygranych
@@ -219,6 +245,110 @@ void Engine::nextTurn() {
return; return;
} }
/**
* @brief Put the piece onto the board
*
* @param[in] color Player's color
* @param[in] pawnId Unique pawn number
*
* @return Returns true on success, false on failure
*/
short Engine::spawnPiece(short color, short pawnId) {
short * relativePieces = players[color].getRelativePawns();
if (relativePieces[pawnId] == -1) {
players[color].sendToBoard(pawnId);
this->board.fields[relPosToField(color, relativePieces[pawnId])][pawnId] = color + 4;
return true;
}
return false;
}
/**
* @brief Take pawns down
*
* @param[in] field Field inside field matrix
*
* @return Pawns taken down
*/
short Engine::takePawns(short color, short field) {
// Zbij wszystkie możliwe pionki
// usuwając je z pola $field, a następnie
// przenosząc je do bazy.
short counter = 0;
// Nie można zbić pionka w polu startowym.
if (field == relPosToField(color, 0)) {
return -1;
}
for (int i = 0; i < 4; i++) {
if (this->board.fields[field][i] > 0) {
counter++;
// Zidentyfikuj kolor na podstawie wartości w $fields
short color = this->board.fields[field][i] - 4;
// Zaktualizuj liczbę pionków gracza
this->players[color].pawnsActive -= 1;
this->players[color].pawnsAtBase += 1;
// Ustaw pionek na odpowiednie miejsce w bazie
this->board.fields[getPawnInitialPosition(color, i)][i] = color + 4;
// i usuń go z planszy
this->board.fields[field][i] = 0;
}
}
return counter;
}
/**
* @brief Try moving a piece
*
* @param[in] color Player color
* @param[in] field Selected field
* @param[in] steps How many steps to move
*
* @return Returns true on success, false on failure.
*/
bool Engine::movePiece(short color, short field, short steps) { // podajemy color dla sanity-checku, czy użytkownik nie chce ruszyć nie swojego pionka
for (int i = 0; i < 4; i++) {
if (this->board.fields[field][i] == color + 4) {
// Znaleźliśmy pionek do przesunięcia, teraz pytanie:
// - czy możemy się ruszyć do przodu o $steps kroków
short relPos = fieldToRelPos(color, field);
if (relPos + steps < 44) {
short newField = relPosToField(color, relPos + steps);
bool isNewFieldOccupied = false;
short occupyingColor = -1;
// - czy tam, gdzie chcemy przenieść pionek gracza
// inny gracz nie ma swojego
for (int j = 0; j < 4; j++) {
if (this->board.fields[newField][j] != 0) {
isNewFieldOccupied = true;
occupyingColor = this->board.fields[newField][j] - 4;
break;
}
}
// - jeśli ma, musimy go (/je) zbić
if (isNewFieldOccupied) takePawns(occupyingColor, newField);
// - w końcu, dopisujemy pionek bieżącego gracza
this->board.fields[field][i] = color + 4;
this->players[this->currentPlayerIndex].unsafeMovePiece(i, steps);
// - po przesunięciu jednego z pionków kończymy pętlę, ponieważ
// nie chcemy przesuwać kolejnych pionków
return true;
}
// jeżeli nie jesteśmy w stanie przesunąć jednego pionka o $steps pozycji,
// to nie będziemy mogli przesunąć żadnego innego pionka
else return false;
}
}
return false;
}
// Deterministycznie (lub nie) generuj kolejne rzuty kostką // Deterministycznie (lub nie) generuj kolejne rzuty kostką
int Engine::rollDice(unsigned int seed, unsigned int rollNumber) { int Engine::rollDice(unsigned int seed, unsigned int rollNumber) {
// Podane ziarno gracza oraz numer losowania zostaną użyte // Podane ziarno gracza oraz numer losowania zostaną użyte

View File

@@ -24,6 +24,9 @@ class Engine {
// Logika gry // Logika gry
void nextTurn(); void nextTurn();
int rollDice(unsigned int seed, unsigned int moveNumber); int rollDice(unsigned int seed, unsigned int moveNumber);
short spawnPiece(short color, short pawnId);
short takePawns(short color, short field);
bool movePiece(short color, short field, short steps);
// Audio // Audio
bool dicerollLoaded = true; bool dicerollLoaded = true;

View File

@@ -3,7 +3,7 @@
# 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-audio-s -lopenal32 -lflac -lvorbisenc -lvorbisfile -lvorbis -logg -lsfml-system-s -lopengl32 -lwinmm -lgdi32 -lpthread DEPS = -lsfml-graphics-s -lsfml-window-s -lsfml-audio-s -lopenal32 -lfreetype -lflac -lvorbisenc -lvorbisfile -lvorbis -logg -lsfml-system-s -lopengl32 -lwinmm -lgdi32 -lpthread
LINK = -L. -Lres/SFML/lib-mingw/ LINK = -L. -Lres/SFML/lib-mingw/
FLAGS = -DSFML_STATIC # -DCHINCZYK188_IGNORE_USER_SEED FLAGS = -DSFML_STATIC # -DCHINCZYK188_IGNORE_USER_SEED
OUTPUT = output.exe OUTPUT = output.exe

View File

@@ -60,3 +60,30 @@ std::vector<Pawn>& Player::getPawns() {
bool Player::hasWon() const { bool Player::hasWon() const {
return pawnsFinished == pawns.size(); return pawnsFinished == pawns.size();
} }
short* Player::getRelativePawns() {
return this->relativePawns;
}
void Player::sendToBase(short pawnNumber) {
this->relativePawns[pawnNumber] = -1;
this->pawnsActive--;
this->pawnsAtBase++;
}
void Player::sendToBoard(short pawnNumber) {
this->hasLeftBase = true;
this->relativePawns[pawnNumber] = 0;
this->pawnsActive++;
this->pawnsAtBase--;
}
void Player::unsafeMovePiece(short pawnNumber, short steps) {
// to dopełnia obecną w Engine.cpp movePiece()
// metoda jest "unsafe" ponieważ nie sprawdza danych, tylko ufa movePiece()
this->relativePawns[pawnNumber] += steps;
if (this->relativePawns[pawnNumber] >= 40) {
this->pawnsActive--;
this->pawnsAtHome++;
}
}

View File

@@ -12,6 +12,7 @@ class Player {
unsigned int getSeed() const; unsigned int getSeed() const;
int getRollCount() const; int getRollCount() const;
short getColor() const; short getColor() const;
short* getRelativePawns();
void incrementRollCount(); void incrementRollCount();
@@ -19,6 +20,9 @@ class Player {
void initializePawns(int totalPawns); void initializePawns(int totalPawns);
void movePawn(int pawnIndex, int steps); void movePawn(int pawnIndex, int steps);
short tryMovingPawn(short pawnID, short fields); short tryMovingPawn(short pawnID, short fields);
void sendToBase(short pawnNumber);
void sendToBoard(short pawnNumber);
void unsafeMovePiece(short pawnNumber, short steps);
std::vector<Pawn>& getPawns(); std::vector<Pawn>& getPawns();
short pawnsAtBase = 4; short pawnsAtBase = 4;
short pawnsActive = 0; short pawnsActive = 0;
@@ -32,6 +36,7 @@ class Player {
unsigned int seed; unsigned int seed;
std::vector<Pawn> pawns; std::vector<Pawn> pawns;
short color; short color;
short relativePawns[4] = {-1};
int rollCount = 0; int rollCount = 0;

View File

@@ -196,3 +196,125 @@ short green_path[44] = {
51, 52, 53, 54, 51, 52, 53, 54,
65, 64, 63, 62, 61 65, 64, 63, 62, 61
}; };
short pawnInitialPositions[4][4] = {
{
99, 100, 110, 111
},
{
9, 10, 20, 21
},
{
0, 1, 11, 12
},
{
108, 109, 119, 120
}
};
short colors_RGB[4][2][3] = {
// czerwony
{
// obwódka
{
192, 67, 67
},
// wypełnienie
{
255, 166, 166
}
},
// niebieski
{
// obwódka
{
102, 175, 187
},
// wypełnienie
{
187, 233, 255
}
},
// żółty
{
// obwódka
{
211, 211, 30
},
// wypełnienie
{
255, 251, 141
}
},
// zielony
{
// obwódka
{
102, 187, 103
},
// wypełnienie
{
187, 255, 200
}
}
};
/**
* @brief Convert field in fields matrix to relative position in paths matrix
*
* @param[in] color Player's color
* @param[in] field Field in the matrix
*
* @return Position inside paths relative to player's color
*/
short fieldToRelPos(short color, short field) {
for (int i = 0; i < 44; i++) {
if (paths[color][i] == field) {
return i;
}
} return -1;
}
/**
* @brief Convert relative position in paths matrix to a field in fields matrix
*
* @param[in] color Player's color
* @param[in] field Field in the matrix
*
* @return Field in fields matrix relative to player's color
*/
short relPosToField(short color, short relPos) {
if (relPos >= 0 && relPos < 44) return paths[color][relPos];
else return -1;
}
/**
* @brief Gets the pawn initial position. Wrapper for pawnInitialPositions[][].
*
* @param[in] color Player's color
* @param[in] pawnNumber Unique pawn number inside fields matrix
*
* @return The pawn initial position.
*/
short getPawnInitialPosition(short color, short pawnNumber) {
return pawnInitialPositions[color][pawnNumber];
}
cords3 color2RGBOutline(short color) {
return cords3(colors_RGB[color][0][0], colors_RGB[color][0][1], colors_RGB[color][0][2]);
}
cords3 color2RGBFiller(short color) {
return cords3(colors_RGB[color][1][0], colors_RGB[color][1][1], colors_RGB[color][1][2]);
}
cords2 fieldToXY(short field) {
cords2 XY;
XY.x = (field % 11) * 100 + 50;
XY.y = static_cast<short>(field / 11) * 100 + 50;
return XY;
}
short XYToField(short x, short y) {
return static_cast<short>(y / 100) * 11 + static_cast<short>(x / 100);
}

View File

@@ -2,7 +2,29 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
struct cords2 {
cords2(): x(0), y(0) {};
cords2(short x, short y): x(x), y(y) {};
short x;
short y;
};
struct cords3 {
cords3(): x(0), y(0), z(0) {};
cords3(short x, short y, short z): x(x), y(y), z(z) {};
short x;
short y;
short z;
};
std::string removeSpaces(std::string str); std::string removeSpaces(std::string str);
std::string removeWhitespace(std::string str); std::string removeWhitespace(std::string str);
void _thread_wait_for_str_cin(bool &done, std::string &text); void _thread_wait_for_str_cin(bool &done, std::string &text);
void _thread_wait_for_str_getline(bool& done, std::string& text); void _thread_wait_for_str_getline(bool& done, std::string& text);
short fieldToRelPos(short color, short field);
short relPosToField(short color, short relPos);
short getPawnInitialPosition(short color, short pawnNumber);
cords3 color2RGBOutline(short color);
cords3 color2RGBFiller(short color);
cords2 fieldToXY(short field);
short XYToField(short x, short y);

Binary file not shown.