170 lines
6.5 KiB
C++
170 lines
6.5 KiB
C++
#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<SequenceDiceSource>(std::vector<int>{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<ludo::PieceMovedEvent>(&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<ludo::PlayerColor, int>;
|
|
|
|
class MoveOutOfHomeTest : public ::testing::TestWithParam<ColorPosParam> {};
|
|
|
|
void assertPathIsWellFormed(const std::vector<ludo::PathNode>& 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<PlayerColor> 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<int>(color) << " start=" << start;
|
|
|
|
EXPECT_EQ(path.front(), (PathNode{PieceState::OnTrack, start}));
|
|
EXPECT_EQ(path.back(), (PathNode{PieceState::InHomeStretch, 0}));
|
|
|
|
assertPathIsWellFormed(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(ComputePath, CaptureFromOnTrackReturnsAtHome) {
|
|
auto path = ludo::LudoGameMode::computePath(
|
|
ludo::PlayerColor::Red, ludo::PieceState::OnTrack, 17,
|
|
ludo::PieceState::AtHome, 2);
|
|
|
|
ASSERT_EQ(path.size(), 2u);
|
|
EXPECT_EQ(path[0].state, ludo::PieceState::OnTrack);
|
|
EXPECT_EQ(path[0].position, 17);
|
|
EXPECT_EQ(path[1].state, ludo::PieceState::AtHome);
|
|
EXPECT_EQ(path[1].position, 2);
|
|
}
|
|
|
|
TEST(ComputePath, ThrowsOnCaptureFromInHomeStretch) {
|
|
EXPECT_THROW(
|
|
ludo::LudoGameMode::computePath(
|
|
ludo::PlayerColor::Red, ludo::PieceState::InHomeStretch, 1,
|
|
ludo::PieceState::AtHome, 0),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
TEST(ComputePath, ThrowsOnCaptureFromAtHome) {
|
|
EXPECT_THROW(
|
|
ludo::LudoGameMode::computePath(
|
|
ludo::PlayerColor::Red, ludo::PieceState::AtHome, 0,
|
|
ludo::PieceState::AtHome, 1), // targetPosition != startPosition, also kein Early-Return
|
|
std::invalid_argument);
|
|
} |