#include "gtest/gtest.h" // // Created by sebastian on 05.08.26. // #include "helpers/SequenceDiceSource.h" #include "ludo/LudoGameMode.h" TEST(LudoGameMode, SecondSixMovesOutOfBlockedEntry) { ludo::LudoGameMode mode({0}); mode.setDiceSource(std::make_unique(std::vector{6, 6})); mode.sendCommand(ludo::RollDiceCommand{0}); mode.pollEvents(); // Events des ersten Wurfs verwerfen, nicht Teil dieser Prüfung ASSERT_EQ(mode.getCurrentPlayer(), 0u); ASSERT_EQ(mode.getState().getPhase(), ludo::TurnPhase::AwaitingRoll); mode.sendCommand(ludo::RollDiceCommand{0}); mode.pollEvents(); // Events des zweiten Wurfs verwerfen ASSERT_EQ(mode.getState().getPhase(), ludo::TurnPhase::AwaitingPieceSelection); auto movable = mode.getMoveablePieceIndices(ludo::PlayerColor::Red, mode.getState().getDiceValue()); ASSERT_EQ(movable.size(), 1u); ASSERT_EQ(movable[0], 0); mode.sendCommand(ludo::MovePieceCommand{0, 0}); auto events = mode.pollEvents(); // jetzt NUR die Events dieses einen Commands ASSERT_FALSE(events.empty()); auto* moved = std::get_if(&events.front()); ASSERT_NE(moved, nullptr); EXPECT_EQ(moved->fromPosition, 0); EXPECT_EQ(moved->toPosition, 6); EXPECT_FALSE(moved->captured); 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); } } }