diff --git a/Chinczyk188/Engine.cpp b/Chinczyk188/Engine.cpp index aaeb01f..38b1a97 100644 --- a/Chinczyk188/Engine.cpp +++ b/Chinczyk188/Engine.cpp @@ -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); diff --git a/Chinczyk188/Player.cpp b/Chinczyk188/Player.cpp index 151eebf..f1dd68f 100644 --- a/Chinczyk188/Player.cpp +++ b/Chinczyk188/Player.cpp @@ -53,18 +53,40 @@ void Player::movePawn(int pawnIndex, int steps) { } } -std::vector& 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; } \ No newline at end of file