check return values (movePiece), offload some logic into Player class

This commit is contained in:
2025-01-28 04:48:45 +01:00
parent c4a74531db
commit 2469c6e504
2 changed files with 206 additions and 73 deletions

View File

@@ -78,81 +78,113 @@ void Engine::nextTurn() {
// Wybieranie pionka
bool pickAPlace = false;
bool isMovePossible = checkPossibleMoves(currentPlayer.getColor(), 6) > 0;
// Wylosowanie szóstki to specjalny przypadek
if (diceRoll == 6) {
std::string choice;
switch (currentPlayer.pawnsActive) {
isMovePossible |= currentPlayer.pawnsAtBase > 0;
// 0 pionków na planszy - musi wyjść pionkiem
case 0:
std::cout << currentPlayer.getName() << " wychodzi pierwszym pionkiem z bazy.\n";
this->spawnPiece(currentPlayer.getColor(), 0);
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
break;
if (isMovePossible) {
// 1, 2, 3 pionki na planszy - może wyjść z bazy albo ruszyć pionek
case 1: case 2: case 3:
std::string choice;
bool pawnMoved = false;
switch (currentPlayer.pawnsActive) {
std::cout << "Co chcesz zrobic?\n";
std::cout << "a) Wyjsc pionkiem z bazy\n"
<< "b) Wykonac ruch wyciagnietym wczesniej pionkiem\n"
<< "> ";
// 0 pionków na planszy - musi wyjść pionkiem
case 0:
std::cout << currentPlayer.getName() << " wychodzi pierwszym pionkiem z bazy.\n";
this->spawnPiece(currentPlayer.getColor(), 0);
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
break;
while (true) {
// 1, 2, 3 pionki na planszy - może wyjść z bazy albo ruszyć pionek
case 1: case 2: case 3:
// getline() nie pozostawia nic w buforze wejściowym, dlatego nie musimy
// aż tak na niego uważać, jak w przypadku cin'a.
std::cout << "Co chcesz zrobic?\n";
std::cout << "a) Wyjsc pionkiem z bazy\n"
<< "b) Wykonac ruch wyciagnietym wczesniej pionkiem\n"
<< "> ";
choice = this->board.asyncStrGetline();
choice[0] |= 32;
while (true) {
if (choice[0] == 'a') {
// getline() nie pozostawia nic w buforze wejściowym, dlatego nie musimy
// aż tak na niego uważać, jak w przypadku cin'a.
pickAPlace = false;
std::cout << currentPlayer.getName() << " wychodzi "
<< currentPlayer.pawnsActive + 1 << ". pionkiem z bazy.\n";
// Rusz pierwszy możliwy pionek w bazie:
this->spawnPiece(currentPlayer.getColor(), this->getFirstPawnAtBase(currentPlayer.getColor()));
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
break;
choice = this->board.asyncStrGetline();
choice[0] |= 32;
} else if (choice[0] == 'b') {
if (choice[0] == 'a') {
if (currentPlayer.pawnsActive > 1) {
pickAPlace = true;
} else {
std::cout << currentPlayer.getName() << " rusza jedyny pionek "
<< 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();
pickAPlace = false;
std::cout << currentPlayer.getName() << " wychodzi "
<< currentPlayer.pawnsActive + 1 << ". pionkiem z bazy.\n";
// Rusz pierwszy możliwy pionek w bazie:
this->spawnPiece(currentPlayer.getColor(), this->getFirstPawnAtBase(currentPlayer.getColor()));
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
break;
} else if (choice[0] == 'b') {
if (currentPlayer.pawnsActive > 1) {
pickAPlace = true;
break;
} else {
// Sprawdź, czy gracz może ruszyć jedyny pionek o tyle pól do przodu
for (int i = 0; i < 4; i++) {
short* relativePawns = currentPlayer.getRelativePawns();
if (relativePawns[i] >= 0) {
pawnMoved = this->movePiece(currentPlayer.getColor(), relPosToField(currentPlayer.getColor(), relativePawns[i]), diceRoll);
if (pawnMoved) break;
}
}
if (pawnMoved) {
std::cout << currentPlayer.getName() << " rusza jedyny pionek "
<< diceRoll << " pol do przodu.\n";
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
break;
} else {
std::cout << "Ups! Nie mozesz tego zrobic!\n";
std::cout << "Jedyna opcja to zagranie kolejnym pionkiem.\n";
this->invalidBuffer.play();
this->board.smartSleep(1000);
// Zróbmy to, co w a)
pickAPlace = false;
std::cout << currentPlayer.getName() << " wychodzi "
<< currentPlayer.pawnsActive + 1 << ". pionkiem z bazy.\n";
// Rusz pierwszy możliwy pionek w bazie:
this->spawnPiece(currentPlayer.getColor(), this->getFirstPawnAtBase(currentPlayer.getColor()));
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
break;
}
}
} else {
std::cout << "Podaj jedna z wymienionych odpowiedzi!\n> ";
this->invalidBuffer.play();
this->board.smartSleep(1000);
}
break;
} else {
std::cout << "Podaj jedna z wymienionych odpowiedzi!\n> ";
this->invalidBuffer.play();
this->board.smartSleep(1000);
}
}
break;
break;
// 4 pionki na planszy - musi ruszyć pionek, o ile może
case 4:
pickAPlace = true;
break;
// 4 pionki na planszy - musi ruszyć pionek, o ile może
case 4:
pickAPlace = true;
break;
}
}
} else {
// Jeśli nie wypadło 6, gracz musi ruszyć któryś z pionków
bool pawnMoved = false;
switch (currentPlayer.pawnsActive) {
// 0 pionków na planszy
@@ -167,15 +199,18 @@ void Engine::nextTurn() {
// dlatego ruszamy go za niego.
pickAPlace = false;
// TODO: znajdź ten pionek i ustaw selectedField na niego!!!
std::cout << currentPlayer.getName() << " rusza jedyny pionek "
<< 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);
pawnMoved = this->movePiece(currentPlayer.getColor(), relPosToField(currentPlayer.getColor(), relativePawns[i]), diceRoll);
}
if (pawnMoved) {
std::cout << currentPlayer.getName() << " rusza jedyny pionek "
<< diceRoll << " pol do przodu.\n";
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
}
this->pawnmoveBuffer.play();
this->board.smartSleep(2000);
break;
// 2, 3, 4 pionki na planszy
@@ -275,6 +310,22 @@ void Engine::nextTurn() {
return;
}
/**
* @brief Converts field to player's unique color.
*
* @param[in] field The field
*
* @return Player's color.
*/
short Engine::fieldToColor(short field) {
for (int i = 0; i < 4; i++) {
if (this->board.fields[field][i] != 0) {
return this->board.fields[field][i] - 4;
}
}
return -1;
}
/**
* @brief Turn a field into the first pawn inside it.
*
@@ -284,12 +335,38 @@ void Engine::nextTurn() {
* @return Player's pawn ID, between 0 and 3.
*/
short Engine::fieldToFirstPawnId(short color, short field) {
// TODO
return 0;
for (int i = 0; i < 4; i++) {
if (this->board.fields[field][i] == color + 4) return i;
}
return -1;
}
/**
* @brief Iterates through player's pawns to check if they can move.
* @brief Check if a pawn can move.
*
* @param[in] color Player's color
* @param[in] pawnId Player's unique pawn ID, ranges from 0-3
* @param[in] steps Amount of steps
*
* @return Returns either true or false, depending if the move is possible or not.
*/
bool Engine::isMoveLegal(short color, short pawnId, short steps) {
// kopia tego, co jest w Engine::movePawn()
short field = relPosToField(color, this->players[color].getRelativePawns()[pawnId]);
for (int i = 0; i < 4; i++) {
if (field == getPawnInitialPosition(color, i))
return false;
}
if (this->board.fields[field][pawnId] == color + 4) {
short relPos = fieldToRelPos(color, field);
if (relPos + steps < 44) return true;
else return false;
}
return false;
}
/**
* @brief Iterates through player's pawns on the board to check if they can move.
*
* @param[in] color Player's color
* @param[in] steps Amount of steps
@@ -297,8 +374,13 @@ short Engine::fieldToFirstPawnId(short color, short field) {
* @return Returns the number of pawns which can be moved given the step count.
*/
short Engine::checkPossibleMoves(short color, short steps) {
// TODO
return 0;
short possibleMoves = 0;
for (int i = 0; i < 4; i++) {
// Nie sprawdzaj pionków, które nie są na planszy
if (this->players[this->currentPlayerIndex].getRelativePawns()[i] == -1) continue;
if (this->isMoveLegal(color, i, steps)) possibleMoves++;
}
return possibleMoves;
}
/**
@@ -345,6 +427,15 @@ short Engine::getFirstPawnNotAtBase(short color) {
*/
short Engine::spawnPiece(short color, short pawnId) {
short * relativePieces = players[color].getRelativePawns();
short spawnField = relPosToField(color, 0);
short existingPieceColor = fieldToColor(spawnField);
// Zbij pionek, jeśli stoi na nim przeciwnik
if (existingPieceColor != color && existingPieceColor != -1) {
/*for (int i = 0; i < 4; i++) {
if (this->board.fields[spawnField][i] != 0) players[existingPieceColor].sendToBase(i);
}*/
this->takePawns(existingPieceColor, spawnField);
}
if (relativePieces[pawnId] == -1) {
players[color].sendToBoard(pawnId);
this->board.fields[relPosToField(color, relativePieces[pawnId])][pawnId] = color + 4;
@@ -381,8 +472,9 @@ short Engine::takePawns(short color, short field) {
short color = this->board.fields[field][i] - 4;
// Zaktualizuj liczbę pionków gracza
this->players[color].pawnsActive -= 1;
this->players[color].pawnsAtBase += 1;
/*this->players[color].pawnsActive -= 1;
this->players[color].pawnsAtBase += 1;*/
players[color].sendToBase(i);
// Ustaw pionek na odpowiednie miejsce w bazie
this->board.fields[getPawnInitialPosition(color, i)][i] = color + 4;
@@ -404,6 +496,13 @@ short Engine::takePawns(short color, short field) {
* @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
// Nie pozwól użytkownikowi wybrać (i w konsekwencji ruszyć) pionków w bazie
for (int i = 0; i < 4; i++) {
if (field == getPawnInitialPosition(color, i))
return false;
}
for (int i = 0; i < 4; i++) {
if (this->board.fields[field][i] == color + 4) {
// Znaleźliśmy pionek do przesunięcia, teraz pytanie:
@@ -423,7 +522,7 @@ bool Engine::movePiece(short color, short field, short steps) { // podajemy colo
}
}
// - jeśli ma, musimy go (/je) zbić
if (isNewFieldOccupied) takePawns(occupyingColor, newField);
if (isNewFieldOccupied && color != occupyingColor) takePawns(occupyingColor, newField);
// - w końcu, dopisujemy pionek bieżącego gracza
this->board.fields[newField][i] = color + 4;
this->players[this->currentPlayerIndex].unsafeMovePiece(i, steps);

View File

@@ -53,18 +53,40 @@ void Player::movePawn(int pawnIndex, int steps) {
}
}
std::vector<Pawn>& Player::getPawns() {
return pawns;
}
bool Player::hasWon() const {
return pawnsFinished == pawns.size();
short lastPosition = 43;
short totalPawnsAtHome = 0;
// szuka wartości pomiędzy 40-43
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (this->relativePawns[j] == lastPosition - i) totalPawnsAtHome++;
}
}
#ifdef DEBUG
std::cout << "(Player.cpp) totalPawnsAtHome: " << totalPawnsAtHome << "\n";
#endif
return totalPawnsAtHome == 4;
}
short* Player::getRelativePawns() {
return this->relativePawns;
}
void Player::recalculatePawnStates() {
short temp_atBase = 0;
short temp_Active = 0;
short temp_atHome = 0;
short temp_finished = 0;
short lastPosition = 44;
for (int i = 0; i < 4; i++) {
if (this->relativePawns[i] == -1) {
temp_atBase++;
continue;
}
// niedokończone...
}
}
void Player::sendToBase(short pawnNumber) {
this->relativePawns[pawnNumber] = -1;
this->pawnsActive--;
@@ -81,9 +103,21 @@ void Player::sendToBoard(short pawnNumber) {
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) {
if (this->relativePawns[pawnNumber] >= 39 && this->isPawnAtHome(pawnNumber)) {
this->pawnsActive--;
this->pawnsAtHome++;
}
this->relativePawns[pawnNumber] += steps;
}
/**
* @brief Determines if pawn is at home.
*
* @param[in] color Player's color
* @param[in] pawnId The pawn's unique identifier
*
* @return True if pawn at home, false otherwise.
*/
bool Player::isPawnAtHome(short pawnId) {
return this->relativePawns[pawnId] >= 44 - 4;
}