From 12c617587faedb644bd47ee179e7bfb3001e73ff Mon Sep 17 00:00:00 2001 From: sebastian Date: Wed, 5 Aug 2026 07:47:24 +0200 Subject: [PATCH] ADD: Tests for LudoGameMode::computePath --- game/src/ludo/LudoGameMode.h | 9 +++ tests/LoduGameMode.cpp | 104 +++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/game/src/ludo/LudoGameMode.h b/game/src/ludo/LudoGameMode.h index 9d75658..f3b020f 100644 --- a/game/src/ludo/LudoGameMode.h +++ b/game/src/ludo/LudoGameMode.h @@ -17,6 +17,15 @@ namespace ludo { struct PathNode { PieceState state; int position; + + friend bool operator==(const PathNode& lhs, const PathNode& rhs) { + return lhs.state == rhs.state && lhs.position == rhs.position; + } + + static inline void PrintTo(const PathNode& node, std::ostream* os) { + *os << "PathNode{state=" << static_cast(node.state) + << ", position=" << node.position << "}"; + } }; class LudoGameMode: public engine::TurnBasedGameMode { diff --git a/tests/LoduGameMode.cpp b/tests/LoduGameMode.cpp index c396a1b..5fb338c 100644 --- a/tests/LoduGameMode.cpp +++ b/tests/LoduGameMode.cpp @@ -35,4 +35,108 @@ TEST(LudoGameMode, SecondSixMovesOutOfBlockedEntry) { const auto& piece = mode.getState().getPieces()[0]; EXPECT_EQ(piece.state, ludo::PieceState::OnTrack); EXPECT_EQ(piece.position, 6); +} +using ColorPosParam = std::tuple; + +class MoveOutOfHomeTest : public ::testing::TestWithParam {}; + +void assertPathIsWellFormed(const std::vector& path) { + using ludo::PieceState; + for (size_t i = 1; i < path.size(); ++i) { + const auto& prev = path[i - 1]; + const auto& cur = path[i]; + + if (prev.state == PieceState::OnTrack && cur.state == PieceState::OnTrack) { + EXPECT_EQ(cur.position, (prev.position + 1) % ludo::kTrackLength) << "at i=" << i; + } else if (prev.state == PieceState::InHomeStretch && cur.state == PieceState::InHomeStretch) { + EXPECT_EQ(cur.position, prev.position + 1) << "at i=" << i; + } else if (prev.state == PieceState::OnTrack && cur.state == PieceState::InHomeStretch) { + EXPECT_EQ(cur.position, 0) << "at i=" << i; + } else if (prev.state == PieceState::InHomeStretch && cur.state == PieceState::Finished) { + EXPECT_EQ(prev.position, ludo::kHomeStretchLength - 1) << "at i=" << i; + } else if (prev.state == PieceState::AtHome && cur.state == PieceState::OnTrack) { + // ok, Position ist der Entry-Offset - separat geprüft + } else { + ADD_FAILURE() << "unerwarteter Zustandswechsel bei i=" << i; + } + } +} + +TEST_P(MoveOutOfHomeTest, EntersTrackAtEntryOffset) { + auto [color, startPos] = GetParam(); + const int entry = ludo::LudoGameMode::getEntryOffset(color); + + auto path = ludo::LudoGameMode::computePath( + color, ludo::PieceState::AtHome, startPos, + ludo::PieceState::OnTrack, entry); + + ASSERT_EQ(path.size(), 2u); + EXPECT_EQ(path[0].state, ludo::PieceState::AtHome); + EXPECT_EQ(path[0].position, startPos); + EXPECT_EQ(path[1].state, ludo::PieceState::OnTrack); + EXPECT_EQ(path[1].position, entry); + assertPathIsWellFormed(path); +} + +INSTANTIATE_TEST_SUITE_P( + AllColorsAllHomeSlots, + MoveOutOfHomeTest, + ::testing::Combine( + ::testing::Values(ludo::PlayerColor::Red, ludo::PlayerColor::Blue, + ludo::PlayerColor::Green, ludo::PlayerColor::Yellow), + ::testing::Range(0, 4))); + +int expectedStepCount(ludo::PlayerColor color, ludo::PieceState fromState, int fromPos, ludo::PieceState toState, int toPos) { + using ludo::PieceState; + const int entry = ludo::LudoGameMode::getEntryOffset(color); + int steps = 0; + PieceState state = fromState; + int pos = fromPos; + + if (state == PieceState::AtHome) { + steps += 1; + state = PieceState::OnTrack; + pos = entry; + } + if (state == PieceState::OnTrack) { + if (toState == PieceState::OnTrack) { + return steps + (toPos - pos + ludo::kTrackLength) % ludo::kTrackLength; + } + steps += (entry - pos + ludo::kTrackLength) % ludo::kTrackLength; // bis zum Entry + steps += 1; // Abbiegen in Home Stretch (Pos 0) + state = PieceState::InHomeStretch; + pos = 0; + } + // state == InHomeStretch + if (toState == PieceState::InHomeStretch) { + return steps + (toPos - pos); + } + // toState == Finished + return steps + (ludo::kHomeStretchLength - 1 - pos) + 1; +} + + + +TEST(LudoGameMode, PathLengthMatchesIndependentFormula) { + using namespace ludo; + const std::vector colors = { + PlayerColor::Red, PlayerColor::Blue, PlayerColor::Green, PlayerColor::Yellow}; + + for (auto color : colors) { + for (int start = 0; start < kTrackLength; ++start) { + auto path = LudoGameMode::computePath( + color, PieceState::OnTrack, start, + PieceState::InHomeStretch, 0); + + EXPECT_EQ(path.size() - 1, + expectedStepCount(color, PieceState::OnTrack, start, + PieceState::InHomeStretch, 0)) + << "color=" << static_cast(color) << " start=" << start; + + EXPECT_EQ(path.front(), (PathNode{PieceState::OnTrack, start})); + EXPECT_EQ(path.back(), (PathNode{PieceState::InHomeStretch, 0})); + + assertPathIsWellFormed(path); + } + } } \ No newline at end of file