diff --git a/game/src/ludo/LudoGameMode.cpp b/game/src/ludo/LudoGameMode.cpp index 27a314b..43d34c5 100644 --- a/game/src/ludo/LudoGameMode.cpp +++ b/game/src/ludo/LudoGameMode.cpp @@ -147,10 +147,19 @@ void ludo::LudoGameMode::handle(const MovePieceCommand &command) { spdlog::info("MovePieceCommand akzeptiert: pieceIndex={}", command.pieceIndex); auto result = movePieceInternal(command.pieceIndex, m_gameState.m_diceValue); - publishEvent(PieceMovedEvent{command.pieceIndex, result.fromPosition, result.toPosition, result.captured, result.targetState, result.startState}); + publishEvent(PieceMovedEvent::create(command.pieceIndex, result.fromPosition, result.toPosition, result.captured, result.startState, result.targetState)); if (result.captured) { spdlog::info("MovePieceCommand: gegnerische Figur auf {} geworfen", result.toPosition); - publishEvent(PieceMovedEvent{result.capturedPieceIndex, result.toPosition, -1, false}); + auto freeHomePositions = m_gameState.getFreeHomePositions(m_gameState.getColorOfPieceIndex(result.capturedPieceIndex)); + if (!freeHomePositions.empty()) { + Piece capturedPiece = m_gameState.m_pieces[result.capturedPieceIndex]; + PieceMovedEvent event = PieceMovedEvent::create(result.capturedPieceIndex, capturedPiece.position, freeHomePositions.front(), true, capturedPiece.state, PieceState::AtHome); + publishEvent(event); + } else { + throw std::runtime_error( + std::format("No free home positions for captured piece {}",std::string(ludo::toString(m_gameState.getColorOfPieceIndex(result.capturedPieceIndex)))) + ); + } } finishTurn(); } @@ -238,9 +247,26 @@ namespace { } throw std::invalid_argument("computePath: unbekannter PieceState"); } + + } + std::vector ludo::LudoGameMode::computePath(PlayerColor color, PieceState startingState, int startPosition, PieceState targetState, int targetPosition) { + auto errorContext = [&]() { + return std::format( + "color={}, startState={}, startPosition={}, targetState={}, targetPosition={}, " + "kTrackLength={}, kHomeStretchLength={}", + toString(color), + toString(startingState), + startPosition, + toString(targetState), + targetPosition, + kTrackLength, + ludo::kHomeStretchLength + ); + }; + if (startingState == targetState && startPosition == targetPosition) { return {}; } @@ -249,55 +275,131 @@ std::vector ludo::LudoGameMode::computePath(PlayerColor color, P if (targetState == PieceState::AtHome) { if (startingState != PieceState::OnTrack) { throw std::invalid_argument( - "computePath: targetState==AtHome ist nur von OnTrack aus erlaubt (Capture)"); + std::format( + "computePath: targetState==AtHome ist nur von OnTrack aus erlaubt (Capture). {}", + errorContext() + ) + ); } - return { PathNode{startingState, startPosition}, PathNode{targetState, targetPosition} }; + + return { + PathNode{startingState, startPosition}, + PathNode{targetState, targetPosition} + }; } + // --- Nur Vorwärtsbewegungen sind erlaubt --- if (stateOrder(targetState) < stateOrder(startingState)) { throw std::invalid_argument( - "computePath: targetState liegt vor startingState (Rueckwaerts nicht unterstuetzt)"); + std::format( + "computePath: targetState liegt vor startingState (Rueckwaerts nicht unterstuetzt). " + "stateOrder(start)={}, stateOrder(target)={}. {}", + stateOrder(startingState), + stateOrder(targetState), + errorContext() + ) + ); } - if (targetState == PieceState::AtHome) { - throw std::invalid_argument( - "computePath: AtHome kann kein Zielzustand sein (ausser Start==Ziel)"); - } if (startingState == PieceState::Finished) { - // hier nur erreichbar, wenn targetState/targetPosition abweicht (early return greift sonst) throw std::invalid_argument( - "computePath: startingState ist bereits Finished, aber Ziel weicht ab"); + std::format( + "computePath: startingState ist bereits Finished, aber Ziel weicht ab. {}", + errorContext() + ) + ); } - if (startingState == PieceState::InHomeStretch && targetState == PieceState::InHomeStretch - && targetPosition < startPosition) { + + if (startingState == PieceState::InHomeStretch && + targetState == PieceState::InHomeStretch && + targetPosition < startPosition) { + throw std::invalid_argument( - "computePath: targetPosition liegt in InHomeStretch vor startPosition"); + std::format( + "computePath: targetPosition liegt im HomeStretch vor startPosition " + "(Rueckwaertsbewegung). {}", + errorContext() + ) + ); } + // --- Positionsbereiche --- - if (targetState == PieceState::OnTrack - && (targetPosition < 0 || targetPosition >= kTrackLength)) { - throw std::invalid_argument("computePath: targetPosition ausserhalb OnTrack-Bereich"); - } - if (targetState == PieceState::InHomeStretch - && (targetPosition < 0 || targetPosition >= kHomeStretchLength)) { - throw std::invalid_argument("computePath: targetPosition ausserhalb InHomeStretch-Bereich"); - } - if (targetState == PieceState::Finished && targetPosition != kHomeStretchLength - 1) { - // <-- vermutlich dein aktueller Bug + if (targetState == PieceState::OnTrack && + (targetPosition < 0 || targetPosition >= kTrackLength)) { + throw std::invalid_argument( - "computePath: targetPosition fuer Finished muss kHomeStretchLength - 1 sein"); + std::format( + "computePath: targetPosition ausserhalb OnTrack-Bereich. " + "Erwartet [0,{}), erhalten {}. {}", + kTrackLength, + targetPosition, + errorContext() + ) + ); } - if (startingState == PieceState::OnTrack - && (startPosition < 0 || startPosition >= kTrackLength)) { - throw std::invalid_argument("computePath: startPosition ausserhalb OnTrack-Bereich"); + + + if (targetState == PieceState::InHomeStretch && + (targetPosition < 0 || targetPosition >= kHomeStretchLength)) { + + throw std::invalid_argument( + std::format( + "computePath: targetPosition ausserhalb InHomeStretch-Bereich. " + "Erwartet [0,{}), erhalten {}. {}", + kHomeStretchLength, + targetPosition, + errorContext() + ) + ); } - if (startingState == PieceState::InHomeStretch - && (startPosition < 0 || startPosition >= kHomeStretchLength)) { - throw std::invalid_argument("computePath: startPosition ausserhalb InHomeStretch-Bereich"); + + + if (targetState == PieceState::Finished && + targetPosition != kHomeStretchLength - 1) { + + throw std::invalid_argument( + std::format( + "computePath: targetPosition fuer Finished ungueltig. " + "Erwartet {}, erhalten {}. {}", + kHomeStretchLength - 1, + targetPosition, + errorContext() + ) + ); + } + + + if (startingState == PieceState::OnTrack && + (startPosition < 0 || startPosition >= kTrackLength)) { + + throw std::invalid_argument( + std::format( + "computePath: startPosition ausserhalb OnTrack-Bereich. " + "Erwartet [0,{}), erhalten {}. {}", + kTrackLength, + startPosition, + errorContext() + ) + ); + } + + + if (startingState == PieceState::InHomeStretch && + (startPosition < 0 || startPosition >= kHomeStretchLength)) { + + throw std::invalid_argument( + std::format( + "computePath: startPosition ausserhalb InHomeStretch-Bereich. " + "Erwartet [0,{}), erhalten {}. {}", + kHomeStretchLength, + startPosition, + errorContext() + ) + ); } std::vector path; diff --git a/game/src/ludo/LudoTypes.h b/game/src/ludo/LudoTypes.h index 5176727..b10f856 100644 --- a/game/src/ludo/LudoTypes.h +++ b/game/src/ludo/LudoTypes.h @@ -42,6 +42,7 @@ namespace ludo { return "Unknown"; } + constexpr std::array(PlayerColor::Count)> PlayerColors = { PlayerColor::Red, PlayerColor::Blue, @@ -55,6 +56,22 @@ namespace ludo { } enum class PieceState : uint8_t { AtHome, OnTrack, InHomeStretch, Finished }; + constexpr std::string_view toString(PieceState state) + { + switch(state) + { + case PieceState::AtHome: + return "AtHome"; + case PieceState::OnTrack: + return "OnTrack"; + case PieceState::InHomeStretch: + return "InHomeStretch"; + case PieceState::Finished: + return "Finished"; + } + + return "Unknown"; + } enum class TurnPhase : uint8_t { AwaitingRoll, AwaitingPieceSelection }; struct Piece { @@ -83,6 +100,21 @@ namespace ludo { } private: + PieceMovedEvent( + int pieceIndex, + int fromPosition, + int toPosition, + bool captured, + PieceState targetState, + PieceState startState) + : + pieceIndex(pieceIndex), + fromPosition(fromPosition), + toPosition(toPosition), + captured(captured), + targetState(targetState), + startState(startState) + {} static void validate(PieceState startState, int fromPosition, PieceState targetState, int toPosition, bool captured) { if (captured && targetState != PieceState::AtHome) {