UPD: Move computePath to PiecePathBuilder
This commit is contained in:
parent
bb75a3400a
commit
a3bb54f041
@ -287,6 +287,11 @@ add_library(GameLib STATIC
|
||||
game/src/gameLayer/GameInput.cpp
|
||||
game/src/gameLayer/GameInput.h
|
||||
game/src/gameLayer/gameController/LudoGameControllerContext.h
|
||||
game/src/ludo/LudoRules.cpp
|
||||
game/src/ludo/LudoRules.h
|
||||
game/src/ludo/gameMode/PiecePathBuilder.cpp
|
||||
game/src/ludo/gameMode/PiecePathBuilder.h
|
||||
game/src/ludo/gameMode/PathNode.h
|
||||
)
|
||||
target_include_directories(GameLib PUBLIC game/src)
|
||||
target_link_libraries(GameLib PUBLIC Engine)
|
||||
|
||||
@ -235,217 +235,4 @@ bool ludo::LudoGameMode::isBlockedByOwnPiece(PlayerColor owner, PieceState state
|
||||
void ludo::LudoGameMode::publishEvent(LudoEvent event) {
|
||||
m_pendingEvents.push_back(event);
|
||||
m_eventBus.publish(event);
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stateOrder(ludo::PieceState state) {
|
||||
switch (state) {
|
||||
case ludo::PieceState::AtHome: return 0;
|
||||
case ludo::PieceState::OnTrack: return 1;
|
||||
case ludo::PieceState::InHomeStretch: return 2;
|
||||
case ludo::PieceState::Finished: return 3;
|
||||
}
|
||||
throw std::invalid_argument("computePath: unbekannter PieceState");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::vector<ludo::PathNode> 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 {};
|
||||
}
|
||||
|
||||
// --- Sonderfall: Figur wird geschlagen und springt direkt zurück ---
|
||||
if (targetState == PieceState::AtHome) {
|
||||
if (startingState != PieceState::OnTrack) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetState==AtHome ist nur von OnTrack aus erlaubt (Capture). {}",
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
PathNode{startingState, startPosition},
|
||||
PathNode{targetState, targetPosition}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// --- Nur Vorwärtsbewegungen sind erlaubt ---
|
||||
if (stateOrder(targetState) < stateOrder(startingState)) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetState liegt vor startingState (Rueckwaerts nicht unterstuetzt). "
|
||||
"stateOrder(start)={}, stateOrder(target)={}. {}",
|
||||
stateOrder(startingState),
|
||||
stateOrder(targetState),
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (startingState == PieceState::Finished) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: startingState ist bereits Finished, aber Ziel weicht ab. {}",
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (startingState == PieceState::InHomeStretch &&
|
||||
targetState == PieceState::InHomeStretch &&
|
||||
targetPosition < startPosition) {
|
||||
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetPosition liegt im HomeStretch vor startPosition "
|
||||
"(Rueckwaertsbewegung). {}",
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// --- Positionsbereiche ---
|
||||
if (targetState == PieceState::OnTrack &&
|
||||
(targetPosition < 0 || targetPosition >= kTrackLength)) {
|
||||
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetPosition ausserhalb OnTrack-Bereich. "
|
||||
"Erwartet [0,{}), erhalten {}. {}",
|
||||
kTrackLength,
|
||||
targetPosition,
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
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 (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<PathNode> path;
|
||||
|
||||
if (startPosition == targetPosition && startingState == targetState) return path;
|
||||
|
||||
PieceState state = startingState;
|
||||
int position = startPosition;
|
||||
|
||||
path.push_back({state, position});
|
||||
|
||||
// Defense in depth: falls die Validierung oben eine Kombination übersieht
|
||||
// (z.B. nach zukünftigen Erweiterungen), lieber laut abstürzen als RAM fressen.
|
||||
const int maxSteps = kTrackLength + kHomeStretchLength + 4;
|
||||
int steps = 0;
|
||||
|
||||
while (state != targetState || position != targetPosition) {
|
||||
if (++steps > maxSteps) {
|
||||
throw std::logic_error(
|
||||
"computePath: Iterationslimit ueberschritten - unreachable Ziel trotz Validierung");
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case PieceState::AtHome:
|
||||
state = PieceState::OnTrack;
|
||||
position = getEntryOffset(color);
|
||||
break;
|
||||
case PieceState::OnTrack:
|
||||
if (position == getEntryOffset(color) && (targetState == PieceState::InHomeStretch || targetState == PieceState::Finished)) {
|
||||
state = PieceState::InHomeStretch;
|
||||
position = 0;
|
||||
} else {
|
||||
position = (position + 1) % kTrackLength;
|
||||
}
|
||||
break;
|
||||
case PieceState::InHomeStretch:
|
||||
if (targetState == PieceState::Finished && position == kHomeStretchLength - 1) {
|
||||
state = PieceState::Finished;
|
||||
} else {
|
||||
++position;
|
||||
}
|
||||
break;
|
||||
case PieceState::Finished:
|
||||
break;
|
||||
}
|
||||
path.push_back({state, position});
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@ -43,7 +43,7 @@ namespace ludo {
|
||||
std::vector<int> getMoveablePieceIndices(PlayerColor color, int diceValue) const;
|
||||
std::optional<int> wouldCapture(int pieceIndex, int diceValue) const;
|
||||
[[nodiscard]] static int getEntryOffset(PlayerColor color);
|
||||
static std::vector<PathNode> computePath(PlayerColor color, PieceState startingState, int startPosition, PieceState targetState, int targetPosition);
|
||||
|
||||
|
||||
void setDiceSource(std::unique_ptr<IDiceSource> diceSource) { m_diceSource = std::move(diceSource); }
|
||||
private:
|
||||
|
||||
9
game/src/ludo/LudoRules.cpp
Normal file
9
game/src/ludo/LudoRules.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
//
|
||||
// Created by sebastian on 05.08.26.
|
||||
//
|
||||
|
||||
#include "LudoRules.h"
|
||||
|
||||
int ludo::LudoRules::getEntryOffset(PlayerColor color) {
|
||||
return static_cast<int>(color) * 10;
|
||||
}
|
||||
31
game/src/ludo/LudoRules.h
Normal file
31
game/src/ludo/LudoRules.h
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Created by sebastian on 05.08.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_LUDORULES_H
|
||||
#define COLORRACE_LUDORULES_H
|
||||
#include "LudoTypes.h"
|
||||
|
||||
|
||||
// LudoRules.h
|
||||
namespace ludo {
|
||||
|
||||
class LudoRules {
|
||||
public:
|
||||
// Alles hier: reine Funktionen, keine Member, kein Zustand.
|
||||
// Nimmt GameState als Parameter, verändert nichts, wirft keine Events.
|
||||
|
||||
static int getEntryOffset(PlayerColor color);
|
||||
|
||||
|
||||
};
|
||||
|
||||
struct Destination {
|
||||
PieceState state;
|
||||
int position;
|
||||
};
|
||||
|
||||
} // namespace ludo
|
||||
|
||||
|
||||
#endif //COLORRACE_LUDORULES_H
|
||||
@ -11,6 +11,7 @@
|
||||
#include "ecs/standardComponents/MeshComponent.h"
|
||||
#include "ecs/standardComponents/PickableComponent.h"
|
||||
#include "ecs/standardComponents/TransformComponent.h"
|
||||
#include "gameMode/PiecePathBuilder.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
|
||||
@ -41,7 +42,7 @@ void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, cons
|
||||
auto entity = m_pieceEntities[event.pieceIndex];
|
||||
|
||||
engine::animation::TransformAnimationComponent animationComponent;
|
||||
std::vector<PathNode> path = LudoGameMode::computePath(piece.color, event.startState, event.fromPosition, event.targetState, event.toPosition);
|
||||
std::vector<ludo::PathNode> path = PiecePathBuilder::computePath(piece.color, event.startState, event.fromPosition, event.targetState, event.toPosition);
|
||||
std::vector<glm::vec3> pathPositions;
|
||||
for (const auto& node : path) {
|
||||
pathPositions.push_back(m_layout.getWorldPosition(piece.color, node.state, node.position));
|
||||
|
||||
17
game/src/ludo/gameMode/PathNode.h
Normal file
17
game/src/ludo/gameMode/PathNode.h
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by sebastian on 05.08.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_PATHNODE_H
|
||||
#define COLORRACE_PATHNODE_H
|
||||
#include "ludo/LudoTypes.h"
|
||||
|
||||
struct PathNode {
|
||||
ludo::PieceState state;
|
||||
int position;
|
||||
|
||||
friend bool operator==(const PathNode& lhs, const PathNode& rhs) {
|
||||
return lhs.state == rhs.state && lhs.position == rhs.position;
|
||||
}
|
||||
};
|
||||
#endif //COLORRACE_PATHNODE_H
|
||||
221
game/src/ludo/gameMode/PiecePathBuilder.cpp
Normal file
221
game/src/ludo/gameMode/PiecePathBuilder.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
//
|
||||
// Created by sebastian on 05.08.26.
|
||||
//
|
||||
|
||||
#include "PiecePathBuilder.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
#include "ludo/LudoGameMode.h"
|
||||
#include "ludo/LudoRules.h"
|
||||
|
||||
namespace {
|
||||
int stateOrder(ludo::PieceState state) {
|
||||
switch (state) {
|
||||
case ludo::PieceState::AtHome: return 0;
|
||||
case ludo::PieceState::OnTrack: return 1;
|
||||
case ludo::PieceState::InHomeStretch: return 2;
|
||||
case ludo::PieceState::Finished: return 3;
|
||||
}
|
||||
throw std::invalid_argument("computePath: unbekannter PieceState");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ludo::PathNode> ludo::PiecePathBuilder::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 {};
|
||||
}
|
||||
|
||||
// --- Sonderfall: Figur wird geschlagen und springt direkt zurück ---
|
||||
if (targetState == PieceState::AtHome) {
|
||||
if (startingState != PieceState::OnTrack) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetState==AtHome ist nur von OnTrack aus erlaubt (Capture). {}",
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
PathNode{startingState, startPosition},
|
||||
PathNode{targetState, targetPosition}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// --- Nur Vorwärtsbewegungen sind erlaubt ---
|
||||
if (stateOrder(targetState) < stateOrder(startingState)) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetState liegt vor startingState (Rueckwaerts nicht unterstuetzt). "
|
||||
"stateOrder(start)={}, stateOrder(target)={}. {}",
|
||||
stateOrder(startingState),
|
||||
stateOrder(targetState),
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (startingState == PieceState::Finished) {
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: startingState ist bereits Finished, aber Ziel weicht ab. {}",
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (startingState == PieceState::InHomeStretch &&
|
||||
targetState == PieceState::InHomeStretch &&
|
||||
targetPosition < startPosition) {
|
||||
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetPosition liegt im HomeStretch vor startPosition "
|
||||
"(Rueckwaertsbewegung). {}",
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// --- Positionsbereiche ---
|
||||
if (targetState == PieceState::OnTrack &&
|
||||
(targetPosition < 0 || targetPosition >= kTrackLength)) {
|
||||
|
||||
throw std::invalid_argument(
|
||||
std::format(
|
||||
"computePath: targetPosition ausserhalb OnTrack-Bereich. "
|
||||
"Erwartet [0,{}), erhalten {}. {}",
|
||||
kTrackLength,
|
||||
targetPosition,
|
||||
errorContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
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 (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<PathNode> path;
|
||||
|
||||
if (startPosition == targetPosition && startingState == targetState) return path;
|
||||
|
||||
PieceState state = startingState;
|
||||
int position = startPosition;
|
||||
|
||||
path.push_back({state, position});
|
||||
|
||||
// Defense in depth: falls die Validierung oben eine Kombination übersieht
|
||||
// (z.B. nach zukünftigen Erweiterungen), lieber laut abstürzen als RAM fressen.
|
||||
const int maxSteps = kTrackLength + kHomeStretchLength + 4;
|
||||
int steps = 0;
|
||||
|
||||
while (state != targetState || position != targetPosition) {
|
||||
if (++steps > maxSteps) {
|
||||
throw std::logic_error(
|
||||
"computePath: Iterationslimit ueberschritten - unreachable Ziel trotz Validierung");
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case PieceState::AtHome:
|
||||
state = PieceState::OnTrack;
|
||||
position = LudoRules::getEntryOffset(color);
|
||||
break;
|
||||
case PieceState::OnTrack:
|
||||
if (position == LudoRules::getEntryOffset(color) && (targetState == PieceState::InHomeStretch || targetState == PieceState::Finished)) {
|
||||
state = PieceState::InHomeStretch;
|
||||
position = 0;
|
||||
} else {
|
||||
position = (position + 1) % kTrackLength;
|
||||
}
|
||||
break;
|
||||
case PieceState::InHomeStretch:
|
||||
if (targetState == PieceState::Finished && position == kHomeStretchLength - 1) {
|
||||
state = PieceState::Finished;
|
||||
} else {
|
||||
++position;
|
||||
}
|
||||
break;
|
||||
case PieceState::Finished:
|
||||
break;
|
||||
}
|
||||
path.push_back({state, position});
|
||||
}
|
||||
return path;
|
||||
}
|
||||
23
game/src/ludo/gameMode/PiecePathBuilder.h
Normal file
23
game/src/ludo/gameMode/PiecePathBuilder.h
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by sebastian on 05.08.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_PIECEPATHBUILDER_H
|
||||
#define COLORRACE_PIECEPATHBUILDER_H
|
||||
#include <vector>
|
||||
|
||||
#include "PathNode.h"
|
||||
#include "ludo/LudoGameMode.h"
|
||||
#include "ludo/LudoTypes.h"
|
||||
|
||||
|
||||
namespace ludo {
|
||||
|
||||
class PiecePathBuilder {
|
||||
public:
|
||||
static std::vector<ludo::PathNode> computePath(PlayerColor color, PieceState startingState, int startPosition, PieceState targetState, int targetPosition);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_PIECEPATHBUILDER_H
|
||||
Loading…
Reference in New Issue
Block a user