mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 09:35:42 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47481c2de6 | ||
|
|
4810e8e9a5 | ||
|
|
d46423bea8 | ||
|
|
3a2f9c0c8c | ||
|
|
c0accd9d6b | ||
|
|
c3c80e3c41 |
@@ -74,7 +74,7 @@ bazel run gazelle # Update Go build files
|
||||
|
||||
### Code Formatting
|
||||
```bash
|
||||
# ALWAYS run clang-format after making any C++ or C# code changes
|
||||
# MANDATORY: ALWAYS run clang-format after making any C++ or C# code changes
|
||||
clang-format -i <modified_files>
|
||||
|
||||
# Format all C++ files in a directory:
|
||||
@@ -84,6 +84,8 @@ find . -name "*.cpp" -o -name "*.hpp" | xargs clang-format -i
|
||||
find . -name "*.cs" | xargs clang-format -i
|
||||
```
|
||||
|
||||
**IMPORTANT FOR CLAUDE CODE**: After making any changes to C++ (.cpp, .hpp) or C# (.cs) files, you MUST immediately run clang-format on those files using the Bash tool. This is not optional - it's a mandatory step that must be done every time you modify code files. Use the exact command: `clang-format -i <file_path>` for each modified file.
|
||||
|
||||
## Language-Specific Patterns
|
||||
|
||||
**Scala (Strategic Layer):**
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "AIAttackerStrategySelector.hpp"
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
|
||||
|
||||
namespace shardok {
|
||||
@@ -15,7 +16,7 @@ constexpr double MAXIMUM_RATIO_FOR_ATTACKER_TO_FLEE = 0.50;
|
||||
|
||||
auto AIAttackerStrategySelector::BestAttackerStrategy(
|
||||
const PlayerId attackerPid,
|
||||
const net::eagle0::shardok::storage::fb::GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const CoordsSet& criticalTileCoords,
|
||||
const APDCache& apdCache,
|
||||
const ALCache& alCache,
|
||||
@@ -69,10 +70,7 @@ auto AIAttackerStrategySelector::BestAttackerStrategy(
|
||||
chosenStrategy = CrossRiversStrategy(startCrossingLocations);
|
||||
} else if (attackerUnitCount < criticalTileCoords.size()) {
|
||||
chosenStrategy = AttackUnitsStrategy(GenerateTargetPriorities(
|
||||
Occupants(
|
||||
*gameState->units(),
|
||||
gameState->hex_map()->row_count(),
|
||||
gameState->hex_map()->column_count()),
|
||||
gameState.GetOccupantsVector(),
|
||||
gameState->hex_map(),
|
||||
defenderPositions,
|
||||
attackerPid,
|
||||
@@ -86,10 +84,7 @@ auto AIAttackerStrategySelector::BestAttackerStrategy(
|
||||
// Otherwise, try to hold the castles.
|
||||
else if (defenderOccupiedCriticalTileCount > 0) {
|
||||
chosenStrategy = AttackCastlesStrategy(GenerateTargetPriorities(
|
||||
Occupants(
|
||||
*gameState->units(),
|
||||
gameState->hex_map()->row_count(),
|
||||
gameState->hex_map()->column_count()),
|
||||
gameState.GetOccupantsVector(),
|
||||
gameState->hex_map(),
|
||||
criticalTileCoords,
|
||||
attackerPid,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIStrategy.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIWaterCrossingCommandChooser.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
|
||||
#include "src/main/flatbuffer/net/eagle0/shardok/storage/game_state.hpp"
|
||||
|
||||
@@ -19,7 +20,7 @@ class AIAttackerStrategySelector {
|
||||
public:
|
||||
static auto BestAttackerStrategy(
|
||||
PlayerId attackerPid,
|
||||
const GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const CoordsSet& criticalTileCoords,
|
||||
const APDCache& apdCache,
|
||||
const ALCache& alCache,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIUnitScoreCalculator.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIVictoryConditionScoreCalculator.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIWaterCrossingCommandChooser.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokEngine.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/util/HexCubeUtils.hpp"
|
||||
@@ -253,7 +254,7 @@ auto AttackerMultiplierForTargetDistance(
|
||||
}
|
||||
|
||||
auto AttackerUnitsScore(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
int roundsRemaining,
|
||||
const SettingsGetter &settings,
|
||||
bool attackerWantsCastles,
|
||||
@@ -276,10 +277,7 @@ auto AttackerUnitsScore(
|
||||
double attackerUnitsValue = 0;
|
||||
double defenderUnitsValue = 0;
|
||||
|
||||
auto occupants = Occupants(
|
||||
*gameState->units(),
|
||||
gameState->hex_map()->row_count(),
|
||||
gameState->hex_map()->column_count());
|
||||
const auto &occupants = gameState.GetOccupantsVector();
|
||||
|
||||
for (const Unit *unit : *gameState->units()) {
|
||||
const auto *pi = PlayerInfoForPid(gameState, unit->player_id());
|
||||
@@ -370,11 +368,9 @@ auto AttackerUnitsScore(
|
||||
auto uv = UnitValue(
|
||||
unit,
|
||||
true,
|
||||
attackerUnits,
|
||||
gameState,
|
||||
attackerWantsCastles,
|
||||
/* includeCastleBonus=*/true,
|
||||
defenderUnits,
|
||||
gameState->hex_map(),
|
||||
roundsRemaining,
|
||||
attackLocationsForAttacker,
|
||||
locationsCausingDanger,
|
||||
@@ -398,11 +394,9 @@ auto AttackerUnitsScore(
|
||||
auto dv = UnitValue(
|
||||
unit,
|
||||
false,
|
||||
attackerUnits,
|
||||
gameState,
|
||||
attackerWantsCastles,
|
||||
/* includeCastleBonus=*/!defenderShouldScatter,
|
||||
defenderUnits,
|
||||
gameState->hex_map(),
|
||||
roundsRemaining,
|
||||
attackLocationsForDefender,
|
||||
locationsCausingDangerForAttacker,
|
||||
@@ -526,7 +520,7 @@ auto AIScoreCalculator::FleeStrategyScoreForState(
|
||||
}
|
||||
|
||||
auto AIScoreCalculator::DefenderScatterStrategyScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const int roundsRemaining,
|
||||
const SettingsGetter &settings,
|
||||
const ALCache &alCache,
|
||||
@@ -562,7 +556,7 @@ auto AIScoreCalculator::DefenderScatterStrategyScoreForState(
|
||||
}
|
||||
|
||||
auto AIScoreCalculator::DefenderHoldCastlesStrategyScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const CoordsSet &castleCoords,
|
||||
const int roundsRemaining,
|
||||
const SettingsGetter &settings,
|
||||
@@ -602,7 +596,7 @@ auto AIScoreCalculator::DefenderHoldCastlesStrategyScoreForState(
|
||||
}
|
||||
|
||||
auto AIScoreCalculator::DefenderScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const AIStrategy &defenderStrategy,
|
||||
const CoordsSet &castleCoords,
|
||||
const int roundsRemaining,
|
||||
@@ -658,7 +652,7 @@ auto AIScoreCalculator::DefenderScoreForState(
|
||||
}
|
||||
|
||||
auto AIScoreCalculator::AttackerScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const AIStrategy &attackerStrategy,
|
||||
const CoordsSet &castleCoords,
|
||||
const int roundsRemaining,
|
||||
@@ -741,7 +735,7 @@ auto AIScoreCalculator::AttackerScoreForState(
|
||||
|
||||
[[nodiscard]] auto AIScoreCalculator::GuessedStateScore(
|
||||
const bool isDefender,
|
||||
const GameState *state,
|
||||
const GameStateW &state,
|
||||
const AIStrategy &aiStrategy,
|
||||
const CoordsSet &allCastleCoords,
|
||||
const SettingsGetter &settingsGetter,
|
||||
@@ -843,7 +837,7 @@ auto AIScoreCalculator::CalcOne(
|
||||
|
||||
auto innerUtility = GuessedStateScore(
|
||||
isDefender,
|
||||
innerEngine->GetCurrentGameState(),
|
||||
innerEngine->GetCurrentGameStateW(),
|
||||
attackerStrategy,
|
||||
allCastleCoords,
|
||||
settingsGetter,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIStrategy.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCTypes.h"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokEngine.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
|
||||
@@ -39,14 +40,14 @@ public:
|
||||
|
||||
private:
|
||||
[[nodiscard]] static auto DefenderScatterStrategyScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
int roundsRemaining,
|
||||
const SettingsGetter &settings,
|
||||
const ALCache &alCache,
|
||||
const APDCache &apdCache) -> ScoreValue;
|
||||
|
||||
[[nodiscard]] static auto DefenderHoldCastlesStrategyScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const CoordsSet &castleCoords,
|
||||
int roundsRemaining,
|
||||
const SettingsGetter &settings,
|
||||
@@ -58,7 +59,7 @@ private:
|
||||
PlayerId playerId) -> ScoreValue;
|
||||
|
||||
[[nodiscard]] static auto DefenderScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const AIStrategy &defenderStrategy,
|
||||
const CoordsSet &castleCoords,
|
||||
int roundsRemaining,
|
||||
@@ -67,7 +68,7 @@ private:
|
||||
const APDCache &apdCache) -> ScoreValue;
|
||||
|
||||
[[nodiscard]] static auto AttackerScoreForState(
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const AIStrategy &attackerStrategy,
|
||||
const CoordsSet &castleCoords,
|
||||
int roundsRemaining,
|
||||
@@ -129,7 +130,7 @@ private:
|
||||
public:
|
||||
[[nodiscard]] static auto GuessedStateScore(
|
||||
bool isDefender,
|
||||
const GameState *state,
|
||||
const GameStateW &state,
|
||||
const AIStrategy &aiStrategy,
|
||||
const CoordsSet &allCastleCoords,
|
||||
const SettingsGetter &settingsGetter,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "AIAttackLocations.hpp"
|
||||
#include "AIScoreUtilities.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
|
||||
|
||||
namespace shardok {
|
||||
@@ -131,8 +133,8 @@ auto lightningValue(const Unit *unit) -> double {
|
||||
auto meteorDropRawValue(
|
||||
const Coords &targetCoords,
|
||||
const HexMap *map,
|
||||
const vector<const Unit *> &enemyOccupants,
|
||||
const vector<const Unit *> &friendlyOccupants) -> double {
|
||||
const vector<const Unit *> &occupants,
|
||||
const Unit *castingUnit) -> double {
|
||||
double rawMeteorDropValue = 0.0;
|
||||
if (targetCoords.row() >= 0) {
|
||||
const double castleMultiplier = GetTerrain(map, targetCoords)->modifier().castle().present()
|
||||
@@ -140,13 +142,16 @@ auto meteorDropRawValue(
|
||||
: 1.0;
|
||||
|
||||
const int index = targetCoords.row() * map->column_count() + targetCoords.column();
|
||||
if (const Unit *directOccupant = enemyOccupants[index]) {
|
||||
rawMeteorDropValue += directOccupant->battalion().size() * kMeteorDirectTargetingEnemy *
|
||||
castleMultiplier;
|
||||
}
|
||||
if (const Unit *directFriendly = friendlyOccupants[index]) {
|
||||
rawMeteorDropValue += directFriendly->battalion().size() *
|
||||
kMeteorDirectTargetingFriendly * castleMultiplier;
|
||||
if (const Unit *directOccupant = occupants[index]) {
|
||||
if (directOccupant->player_id() == castingUnit->player_id()) {
|
||||
// Friendly unit
|
||||
rawMeteorDropValue += directOccupant->battalion().size() *
|
||||
kMeteorDirectTargetingFriendly * castleMultiplier;
|
||||
} else {
|
||||
// Enemy unit
|
||||
rawMeteorDropValue += directOccupant->battalion().size() *
|
||||
kMeteorDirectTargetingEnemy * castleMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &splashCoords : HexMapUtils::GetAdjacentCoords(map, targetCoords)) {
|
||||
@@ -157,14 +162,16 @@ auto meteorDropRawValue(
|
||||
|
||||
const auto splashIndex =
|
||||
splashCoords.row() * map->column_count() + splashCoords.column();
|
||||
if (const Unit *splashOccupant = enemyOccupants[splashIndex]) {
|
||||
rawMeteorDropValue += splashOccupant->battalion().size() *
|
||||
kMeteorSplashTargetingEnemy * splashCastleMultiplier;
|
||||
}
|
||||
|
||||
if (const Unit *splashFriendly = friendlyOccupants[splashIndex]) {
|
||||
rawMeteorDropValue += splashFriendly->battalion().size() *
|
||||
kMeteorSplashTargetingFriendly * splashCastleMultiplier;
|
||||
if (const Unit *splashOccupant = occupants[splashIndex]) {
|
||||
if (splashOccupant->player_id() == castingUnit->player_id()) {
|
||||
// Friendly unit
|
||||
rawMeteorDropValue += splashOccupant->battalion().size() *
|
||||
kMeteorSplashTargetingFriendly * splashCastleMultiplier;
|
||||
} else {
|
||||
// Enemy unit
|
||||
rawMeteorDropValue += splashOccupant->battalion().size() *
|
||||
kMeteorSplashTargetingEnemy * splashCastleMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -177,14 +184,12 @@ auto meteorDropRawValue(
|
||||
auto bestTargetValue(
|
||||
const Unit *unit,
|
||||
const HexMap *map,
|
||||
const vector<const Unit *> &enemyOccupants,
|
||||
const vector<const Unit *> &friendlyOccupants,
|
||||
const vector<const Unit *> &occupants,
|
||||
const int meteorRange) -> double {
|
||||
double maxValue = 0.0;
|
||||
|
||||
for (const Coords &target : TilesWithinDistance(map, unit->location(), meteorRange)) {
|
||||
if (const double thisTargetValue =
|
||||
meteorDropRawValue(target, map, enemyOccupants, friendlyOccupants);
|
||||
if (const double thisTargetValue = meteorDropRawValue(target, map, occupants, unit);
|
||||
thisTargetValue > maxValue) {
|
||||
maxValue = thisTargetValue;
|
||||
}
|
||||
@@ -197,20 +202,15 @@ auto meteorValue(
|
||||
const Unit *unit,
|
||||
const int roundsRemaining,
|
||||
const HexMap *map,
|
||||
const vector<const Unit *> &enemyUnits,
|
||||
const vector<const Unit *> &friendlyUnits,
|
||||
const vector<const Unit *> &occupants,
|
||||
const int meteorRange,
|
||||
const double minVigorToCast) -> double {
|
||||
const auto enemyOccupants = Occupants(enemyUnits, map->row_count(), map->column_count());
|
||||
const auto friendlyOccupants = Occupants(friendlyUnits, map->row_count(), map->column_count());
|
||||
|
||||
switch (unit->attached_hero().profession_info().meteor_cast_state()) {
|
||||
case net::eagle0::shardok::storage::fb::MultiroundMagicState_NONE:
|
||||
case net::eagle0::shardok::storage::fb::MultiroundMagicState_START: {
|
||||
if ((roundsRemaining > 8 ||
|
||||
(unit->attached_hero().vigor() > 50 && roundsRemaining > 4))) {
|
||||
const double maxValue =
|
||||
bestTargetValue(unit, map, enemyOccupants, friendlyOccupants, meteorRange);
|
||||
const double maxValue = bestTargetValue(unit, map, occupants, meteorRange);
|
||||
|
||||
double multiplier = kMeteorStartRoundMultiplier;
|
||||
if (unit->attached_hero().profession_info().meteor_cast_state() ==
|
||||
@@ -233,18 +233,18 @@ auto meteorValue(
|
||||
return meteorDropRawValue(
|
||||
unit->attached_hero().profession_info().cast_target(),
|
||||
map,
|
||||
enemyOccupants,
|
||||
friendlyOccupants);
|
||||
occupants,
|
||||
unit);
|
||||
} else {
|
||||
return bestTargetValue(unit, map, enemyOccupants, friendlyOccupants, meteorRange);
|
||||
return bestTargetValue(unit, map, occupants, meteorRange);
|
||||
}
|
||||
case net::eagle0::shardok::storage::fb::MultiroundMagicState_CAST:
|
||||
return kMeteorCastRoundMultiplier *
|
||||
meteorDropRawValue(
|
||||
unit->attached_hero().profession_info().cast_target(),
|
||||
map,
|
||||
enemyOccupants,
|
||||
friendlyOccupants);
|
||||
occupants,
|
||||
unit);
|
||||
|
||||
default: break;
|
||||
}
|
||||
@@ -252,6 +252,7 @@ auto meteorValue(
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Original version that accepts unit vectors for backward compatibility
|
||||
auto GetRangedAttackBonus(
|
||||
const Unit *unit,
|
||||
const bool isAttacker,
|
||||
@@ -263,10 +264,35 @@ auto GetRangedAttackBonus(
|
||||
const vector<const Unit *> &defenderUnits,
|
||||
const int meteorRange,
|
||||
const double minVigorToCast) -> double {
|
||||
vector<double> rangedAttackValues{};
|
||||
// Create occupants vector for meteor calculations
|
||||
vector<const Unit *> allUnits;
|
||||
allUnits.insert(allUnits.end(), attackerUnits.begin(), attackerUnits.end());
|
||||
allUnits.insert(allUnits.end(), defenderUnits.begin(), defenderUnits.end());
|
||||
const auto occupants = Occupants(allUnits, map->row_count(), map->column_count());
|
||||
return GetRangedAttackBonus(
|
||||
unit,
|
||||
isAttacker,
|
||||
map,
|
||||
terrain,
|
||||
attackLocations,
|
||||
roundsRemaining,
|
||||
occupants,
|
||||
meteorRange,
|
||||
minVigorToCast);
|
||||
}
|
||||
|
||||
const auto &enemyUnits = isAttacker ? defenderUnits : attackerUnits;
|
||||
const auto &friendlyUnits = isAttacker ? attackerUnits : defenderUnits;
|
||||
// Optimized version that accepts occupants vector directly
|
||||
auto GetRangedAttackBonus(
|
||||
const Unit *unit,
|
||||
const bool isAttacker,
|
||||
const HexMap *map,
|
||||
const Terrain *terrain,
|
||||
const AttackLocations &attackLocations,
|
||||
const int roundsRemaining,
|
||||
const vector<const Unit *> &occupants,
|
||||
const int meteorRange,
|
||||
const double minVigorToCast) -> double {
|
||||
vector<double> rangedAttackValues{};
|
||||
|
||||
const auto &unitLocation = unit->location();
|
||||
|
||||
@@ -280,14 +306,8 @@ auto GetRangedAttackBonus(
|
||||
net::eagle0::shardok::storage::fb::MultiroundMagicState_NONE) {
|
||||
rangedAttackValues.push_back(lightningValue(unit));
|
||||
} else {
|
||||
const double mv = meteorValue(
|
||||
unit,
|
||||
roundsRemaining,
|
||||
map,
|
||||
enemyUnits,
|
||||
friendlyUnits,
|
||||
meteorRange,
|
||||
minVigorToCast);
|
||||
const double mv =
|
||||
meteorValue(unit, roundsRemaining, map, occupants, meteorRange, minVigorToCast);
|
||||
rangedAttackValues.push_back(mv);
|
||||
}
|
||||
}
|
||||
@@ -325,16 +345,29 @@ auto GetRangedAttackBonus(
|
||||
auto UnitValue(
|
||||
const Unit *unit,
|
||||
const bool isAttacker,
|
||||
const vector<const Unit *> &attackerUnits,
|
||||
const GameStateW &gameState,
|
||||
const bool attackerWantsCastles,
|
||||
const bool includeCastleBonus,
|
||||
const vector<const Unit *> &defenderUnits,
|
||||
const HexMap *map,
|
||||
const int roundsRemaining,
|
||||
const AttackLocations &locationsThisSideCanAttackFrom,
|
||||
const CoordsSet &locationsInDangerFromEnemy,
|
||||
const ActionPointDistances *distances,
|
||||
const SettingsGetter &settings) -> ScoreValue {
|
||||
const HexMap *map = gameState->hex_map();
|
||||
|
||||
// Get attacker and defender units from the game state
|
||||
vector<const Unit *> attackerUnits{};
|
||||
vector<const Unit *> defenderUnits{};
|
||||
for (const Unit *u : *gameState->units()) {
|
||||
if (u->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;
|
||||
const auto *pi = PlayerInfoForPid(gameState, u->player_id());
|
||||
if (pi == nullptr) continue;
|
||||
if (pi->is_defender()) {
|
||||
defenderUnits.push_back(u);
|
||||
} else {
|
||||
attackerUnits.push_back(u);
|
||||
}
|
||||
}
|
||||
const auto &location = unit->location();
|
||||
if (location.row() < 0) return 0; // unplaced unit
|
||||
|
||||
@@ -376,8 +409,7 @@ auto UnitValue(
|
||||
terrain,
|
||||
locationsThisSideCanAttackFrom,
|
||||
roundsRemaining,
|
||||
attackerUnits,
|
||||
defenderUnits,
|
||||
gameState.GetOccupantsVector(),
|
||||
settings.Backing().meteor_range(),
|
||||
settings.Backing().meteor_cast_vigor_cost());
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef EAGLE0_AIUNITSCORECALCULATOR_HPP
|
||||
#define EAGLE0_AIUNITSCORECALCULATOR_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistances.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/settings/GameSettings.hpp"
|
||||
@@ -34,14 +35,24 @@ auto GetRangedAttackBonus(
|
||||
int meteorRange,
|
||||
double minVigorToCast) -> double;
|
||||
|
||||
// Overload that accepts occupants vector for cached performance
|
||||
auto GetRangedAttackBonus(
|
||||
const Unit *unit,
|
||||
bool isAttacker,
|
||||
const HexMap *map,
|
||||
const Terrain *terrain,
|
||||
const AttackLocations &attackLocations,
|
||||
int roundsRemaining,
|
||||
const vector<const Unit *> &occupants,
|
||||
int meteorRange,
|
||||
double minVigorToCast) -> double;
|
||||
|
||||
auto UnitValue(
|
||||
const Unit *unit,
|
||||
bool isAttacker,
|
||||
const vector<const Unit *> &attackerUnits,
|
||||
const GameStateW &gameState,
|
||||
bool attackerWantsCastles,
|
||||
bool includeCastleBonus,
|
||||
const vector<const Unit *> &defenderUnits,
|
||||
const HexMap *map,
|
||||
int roundsRemaining,
|
||||
const AttackLocations &locationsThisSideCanAttackFrom,
|
||||
const CoordsSet &locationsInDangerFromEnemy,
|
||||
|
||||
@@ -122,7 +122,7 @@ auto AttackerDebufForDefenderOccupiedCriticalTile(
|
||||
}
|
||||
|
||||
auto DefenderHoldsCriticalTilesVictoryScore(
|
||||
const net::eagle0::shardok::storage::fb::GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const CoordsSet& criticalTileLocations,
|
||||
const PlayerInfo* player,
|
||||
const APDCache& apdCache,
|
||||
@@ -130,9 +130,8 @@ auto DefenderHoldsCriticalTilesVictoryScore(
|
||||
const SettingsGetter& settings) -> ScoreValue {
|
||||
ScoreValue total = 0.0;
|
||||
|
||||
const auto rc = gameState->hex_map()->row_count();
|
||||
const auto cc = gameState->hex_map()->column_count();
|
||||
const auto occupants = Occupants(*gameState->units(), rc, cc);
|
||||
const auto& occupants = gameState.GetOccupantsVector();
|
||||
|
||||
for (const Coords& criticalTileLocation : criticalTileLocations) {
|
||||
const auto index = criticalTileLocation.row() * cc + criticalTileLocation.column();
|
||||
@@ -152,7 +151,7 @@ auto DefenderHoldsCriticalTilesVictoryScore(
|
||||
}
|
||||
|
||||
auto AttackerHoldsCriticalTilesVictoryScore(
|
||||
const net::eagle0::shardok::storage::fb::GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const CoordsSet& criticalTileLocations,
|
||||
const PlayerInfo* player,
|
||||
const APDCache& apdCache,
|
||||
@@ -178,9 +177,8 @@ auto AttackerHoldsCriticalTilesVictoryScore(
|
||||
|
||||
ScoreValue total = 0.0;
|
||||
|
||||
const auto rc = gameState->hex_map()->row_count();
|
||||
const auto cc = gameState->hex_map()->column_count();
|
||||
const auto occupants = Occupants(*gameState->units(), rc, cc);
|
||||
const auto& occupants = gameState.GetOccupantsVector();
|
||||
|
||||
for (const Coords& criticalTileLocation : criticalTileLocations) {
|
||||
const auto index = criticalTileLocation.row() * cc + criticalTileLocation.column();
|
||||
@@ -246,7 +244,7 @@ auto AttackerHoldsCriticalTilesVictoryScore(
|
||||
}
|
||||
|
||||
auto LastPlayerStandingVictoryScore(
|
||||
const GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const PlayerInfo* player,
|
||||
const APDCache& apdCache,
|
||||
const ALCache& alCache,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackGroups.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/settings/GameSettings.hpp"
|
||||
@@ -23,7 +24,7 @@ using std::vector;
|
||||
using ScoreValue = double;
|
||||
|
||||
auto AttackerHoldsCriticalTilesVictoryScore(
|
||||
const net::eagle0::shardok::storage::fb::GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const CoordsSet& criticalTileLocations,
|
||||
const PlayerInfo* player,
|
||||
const APDCache& apdCache,
|
||||
@@ -31,7 +32,7 @@ auto AttackerHoldsCriticalTilesVictoryScore(
|
||||
const SettingsGetter& settings) -> ScoreValue;
|
||||
|
||||
auto DefenderHoldsCriticalTilesVictoryScore(
|
||||
const net::eagle0::shardok::storage::fb::GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const CoordsSet& criticalTileLocations,
|
||||
const PlayerInfo* player,
|
||||
const APDCache& apdCache,
|
||||
@@ -39,7 +40,7 @@ auto DefenderHoldsCriticalTilesVictoryScore(
|
||||
const SettingsGetter& settings) -> ScoreValue;
|
||||
|
||||
auto LastPlayerStandingVictoryScore(
|
||||
const GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const PlayerInfo* player,
|
||||
const APDCache& apdCache,
|
||||
const ALCache& alCache,
|
||||
|
||||
@@ -14,6 +14,7 @@ cc_library(
|
||||
":ai_score_utilities",
|
||||
":ai_strategy",
|
||||
":ai_water_crossing_command_chooser",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/map:coords_set",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/util:hex_map_utils",
|
||||
"//src/main/flatbuffer/net/eagle0/shardok/storage:game_state_cc_fbs",
|
||||
@@ -162,6 +163,7 @@ cc_library(
|
||||
":ai_victory_condition_score_calculator",
|
||||
"//src/main/cpp/net/eagle0/common:sequence_random_generator",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:engine",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/view_filters:game_state_guesser",
|
||||
],
|
||||
)
|
||||
@@ -191,6 +193,8 @@ cc_library(
|
||||
],
|
||||
deps = [
|
||||
":ai_attack_locations",
|
||||
":ai_score_utilities",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/action_point_distances",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/util:hex_map_utils",
|
||||
],
|
||||
@@ -210,6 +214,7 @@ cc_library(
|
||||
":ai_attack_locations",
|
||||
":ai_distance_debuf",
|
||||
":ai_score_utilities",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/action_point_distances",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/action_point_distances:action_point_distances_cache",
|
||||
"//src/main/flatbuffer/net/eagle0/shardok/storage:game_state_cc_fbs",
|
||||
|
||||
@@ -2,12 +2,16 @@ load("//tools:copts.bzl", "COPTS")
|
||||
|
||||
cc_library(
|
||||
name = "game_state_w",
|
||||
srcs = ["GameStateW.cpp"],
|
||||
hdrs = ["GameStateW.hpp"],
|
||||
copts = COPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":shardok_c_types",
|
||||
"//src/main/cpp/net/eagle0/common:container_utils",
|
||||
"//src/main/cpp/net/eagle0/shardok/library/fb_helpers:flatbuffer_wrapper",
|
||||
"//src/main/flatbuffer/net/eagle0/shardok/storage:game_state_cc_fbs",
|
||||
"//src/main/flatbuffer/net/eagle0/shardok/storage:unit_cc_fbs",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// Created by Dan Crosby on 2025-01-17.
|
||||
//
|
||||
|
||||
#include "GameStateW.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/ContainerUtils.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
/**
|
||||
* @class OccupantsCache
|
||||
* @brief Cache for efficient unit location lookups.
|
||||
*
|
||||
* This cache maintains a vector indexed by map position (row * columnCount + column)
|
||||
* containing pointers to units at each position. This converts O(n) unit lookups
|
||||
* into O(1) operations.
|
||||
*/
|
||||
class OccupantsCache {
|
||||
public:
|
||||
OccupantsCache(const GameStateW& gameState) { Regenerate(gameState); }
|
||||
|
||||
void Regenerate(const GameStateW& gameState) {
|
||||
const auto* state = gameState.Get();
|
||||
if (!state || !state->hex_map() || !state->units()) {
|
||||
occupants_.clear();
|
||||
rowCount_ = 0;
|
||||
columnCount_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
rowCount_ = state->hex_map()->row_count();
|
||||
columnCount_ = state->hex_map()->column_count();
|
||||
const size_t mapSize = rowCount_ * columnCount_;
|
||||
|
||||
// Clear and resize the vector
|
||||
occupants_.clear();
|
||||
occupants_.resize(mapSize, nullptr);
|
||||
|
||||
// Populate the cache
|
||||
for (const auto* unit : *state->units()) {
|
||||
if (unit &&
|
||||
unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) {
|
||||
const auto& location = unit->location();
|
||||
if (location.row() >= 0 && location.row() < rowCount_ && location.column() >= 0 &&
|
||||
location.column() < columnCount_) {
|
||||
const size_t index = location.row() * columnCount_ + location.column();
|
||||
occupants_[index] = unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto GetOccupant(const net::eagle0::shardok::storage::fb::Coords& coords) const
|
||||
-> const GameStateW::Unit* {
|
||||
if (coords.row() < 0 || coords.row() >= rowCount_ || coords.column() < 0 ||
|
||||
coords.column() >= columnCount_) {
|
||||
return nullptr;
|
||||
}
|
||||
const size_t index = coords.row() * columnCount_ + coords.column();
|
||||
return occupants_[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto GetOccupantsVector() const -> const std::vector<const GameStateW::Unit*>& {
|
||||
return occupants_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<const GameStateW::Unit*> occupants_;
|
||||
int16_t rowCount_ = 0;
|
||||
int16_t columnCount_ = 0;
|
||||
};
|
||||
|
||||
// GameStateW implementation
|
||||
|
||||
GameStateW::GameStateW() : BaseType() { InitializeCache(); }
|
||||
|
||||
GameStateW::GameStateW(const GameStateW& other)
|
||||
: BaseType(other),
|
||||
occupantsCache_(other.occupantsCache_) {
|
||||
// Share the cache - no need to rebuild!
|
||||
}
|
||||
|
||||
GameStateW::GameStateW(GameStateW&& other) noexcept
|
||||
: BaseType(std::move(other)),
|
||||
occupantsCache_(std::move(other.occupantsCache_)) {}
|
||||
|
||||
GameStateW& GameStateW::operator=(const GameStateW& other) {
|
||||
if (this != &other) {
|
||||
BaseType::operator=(other);
|
||||
occupantsCache_ = other.occupantsCache_; // Share the cache
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GameStateW& GameStateW::operator=(GameStateW&& other) noexcept {
|
||||
if (this != &other) {
|
||||
BaseType::operator=(std::move(other));
|
||||
occupantsCache_ = std::move(other.occupantsCache_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GameStateW::GameStateW(const BaseType& base) : BaseType(base) { InitializeCache(); }
|
||||
|
||||
GameStateW::GameStateW(BaseType&& base) : BaseType(std::move(base)) { InitializeCache(); }
|
||||
|
||||
GameStateW::GameStateW(flatbuffers::FlatBufferBuilder& fbb) : BaseType(fbb) { InitializeCache(); }
|
||||
|
||||
GameStateW::~GameStateW() = default;
|
||||
|
||||
auto GameStateW::GetOccupant(const net::eagle0::shardok::storage::fb::Coords& coords) const
|
||||
-> const Unit* {
|
||||
if (!occupantsCache_) {
|
||||
throw std::runtime_error(
|
||||
"GameStateW::GetOccupant() called with uninitialized occupants cache. "
|
||||
"This indicates a bug where the cache was invalidated without being rebuilt. "
|
||||
"Call RebuildOccupantsCache() or InitializeCache() after any mutations.");
|
||||
}
|
||||
|
||||
return occupantsCache_->GetOccupant(coords);
|
||||
}
|
||||
|
||||
auto GameStateW::GetOccupantsVector() const -> const std::vector<const Unit*>& {
|
||||
if (!occupantsCache_) {
|
||||
throw std::runtime_error(
|
||||
"GameStateW::GetOccupantsVector() called with uninitialized occupants cache. "
|
||||
"This indicates a bug where the cache was invalidated without being rebuilt. "
|
||||
"Call RebuildOccupantsCache() or InitializeCache() after any mutations.");
|
||||
}
|
||||
return occupantsCache_->GetOccupantsVector();
|
||||
}
|
||||
|
||||
auto GameStateW::GetKnownEnemyOccupant(
|
||||
PlayerId playerId,
|
||||
const std::vector<PlayerId>& allyPids,
|
||||
const net::eagle0::shardok::storage::fb::Coords& coords) const -> const Unit* {
|
||||
const auto* occupant = GetOccupant(coords);
|
||||
if (occupant) {
|
||||
if (!occupant->hidden() && occupant->player_id() != playerId &&
|
||||
!common::Contains(allyPids, occupant->player_id())) {
|
||||
return occupant;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GameStateW::RebuildOccupantsCache() {
|
||||
occupantsCache_.reset();
|
||||
occupantsCache_ = std::make_shared<OccupantsCache>(*this);
|
||||
}
|
||||
|
||||
void GameStateW::InvalidateOccupantsCache() { occupantsCache_.reset(); }
|
||||
|
||||
void GameStateW::InitializeCache() { occupantsCache_ = std::make_shared<OccupantsCache>(*this); }
|
||||
|
||||
} // namespace shardok
|
||||
@@ -5,11 +5,19 @@
|
||||
#ifndef EAGLE0_GAMESTATEW_HPP
|
||||
#define EAGLE0_GAMESTATEW_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCTypes.h"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/FlatbufferWrapper.hpp"
|
||||
#include "src/main/flatbuffer/net/eagle0/shardok/storage/game_state.hpp"
|
||||
#include "src/main/flatbuffer/net/eagle0/shardok/storage/unit.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
// Forward declaration for the cache
|
||||
class OccupantsCache;
|
||||
|
||||
/**
|
||||
* @class GameStateW
|
||||
* @brief A wrapper class for the FlatBuffer-generated GameState type.
|
||||
@@ -22,38 +30,116 @@ namespace shardok {
|
||||
* This class is part of the shardok namespace and is designed to simplify
|
||||
* interactions with the GameState FlatBuffer type while maintaining the
|
||||
* flexibility and functionality of the Wrapper base class.
|
||||
*
|
||||
* The class includes an occupants cache for efficient unit location lookups,
|
||||
* which significantly improves performance for operations that frequently
|
||||
* query which units occupy specific map coordinates.
|
||||
*/
|
||||
class GameStateW : public Wrapper<net::eagle0::shardok::storage::fb::GameState> {
|
||||
public:
|
||||
using BaseType = Wrapper<net::eagle0::shardok::storage::fb::GameState>;
|
||||
using Unit = net::eagle0::shardok::storage::fb::Unit;
|
||||
|
||||
// Inherit all constructors from Wrapper
|
||||
using BaseType::BaseType;
|
||||
|
||||
// Default constructor
|
||||
GameStateW() : BaseType() {}
|
||||
GameStateW();
|
||||
|
||||
// Copy constructor
|
||||
GameStateW(const GameStateW& other) : BaseType(other) {}
|
||||
GameStateW(const GameStateW& other);
|
||||
|
||||
// Move constructor
|
||||
GameStateW(GameStateW&& other) noexcept : BaseType(std::move(other)) {}
|
||||
GameStateW(GameStateW&& other) noexcept;
|
||||
|
||||
// Copy assignment
|
||||
GameStateW& operator=(const GameStateW& other) {
|
||||
BaseType::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
GameStateW& operator=(const GameStateW& other);
|
||||
|
||||
// Move assignment
|
||||
GameStateW& operator=(GameStateW&& other) noexcept {
|
||||
BaseType::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
GameStateW& operator=(GameStateW&& other) noexcept;
|
||||
|
||||
// Constructor from base type
|
||||
GameStateW(const BaseType& base) : BaseType(base) {}
|
||||
GameStateW(BaseType&& base) : BaseType(std::move(base)) {}
|
||||
GameStateW(const BaseType& base);
|
||||
GameStateW(BaseType&& base);
|
||||
|
||||
// Constructor from FlatBufferBuilder
|
||||
explicit GameStateW(flatbuffers::FlatBufferBuilder& fbb);
|
||||
|
||||
// Destructor
|
||||
~GameStateW();
|
||||
|
||||
/**
|
||||
* @brief Get the unit occupying the specified coordinates.
|
||||
* @param coords The coordinates to check.
|
||||
* @return Pointer to the unit at the coordinates, or nullptr if none.
|
||||
*
|
||||
* This is an O(1) operation using the internal occupants cache.
|
||||
*/
|
||||
[[nodiscard]] auto GetOccupant(const net::eagle0::shardok::storage::fb::Coords& coords) const
|
||||
-> const Unit*;
|
||||
|
||||
/**
|
||||
* @brief Get all occupants indexed by their position.
|
||||
* @return Vector where index = row * columnCount + column contains unit pointer or nullptr.
|
||||
*
|
||||
* This provides direct access to the occupants cache for bulk operations.
|
||||
*/
|
||||
[[nodiscard]] auto GetOccupantsVector() const -> const std::vector<const Unit*>&;
|
||||
|
||||
/**
|
||||
* @brief Get the known enemy unit occupying the specified coordinates.
|
||||
* @param playerId The player ID to check enemies for.
|
||||
* @param allyPids Vector of allied player IDs.
|
||||
* @param coords The coordinates to check.
|
||||
* @return Pointer to the enemy unit at the coordinates, or nullptr if none.
|
||||
*
|
||||
* This is an O(1) operation using the internal occupants cache.
|
||||
*/
|
||||
[[nodiscard]] auto GetKnownEnemyOccupant(
|
||||
PlayerId playerId,
|
||||
const std::vector<PlayerId>& allyPids,
|
||||
const net::eagle0::shardok::storage::fb::Coords& coords) const -> const Unit*;
|
||||
|
||||
/**
|
||||
* @brief Rebuild the occupants cache.
|
||||
*
|
||||
* This method invalidates the existing cache and immediately rebuilds it to reflect
|
||||
* the current state of units. Call this method after any mutations to the game state
|
||||
* that might affect unit positions.
|
||||
*
|
||||
* @warning This method is NOT thread-safe. It should only be called:
|
||||
* - After mutations when the GameStateW instance is owned by a single thread
|
||||
* - When transitioning ownership between threads (with proper synchronization)
|
||||
*
|
||||
* Concurrent calls to RebuildOccupantsCache() or calls concurrent with GetOccupant()
|
||||
* will result in undefined behavior due to race conditions.
|
||||
*/
|
||||
void RebuildOccupantsCache();
|
||||
|
||||
/**
|
||||
* @brief Initialize the occupants cache.
|
||||
* This rebuilds the cache from the current game state.
|
||||
*
|
||||
* @warning This method is NOT thread-safe. It should only be called:
|
||||
* - During GameStateW construction (automatically handled)
|
||||
* - After mutations when the GameStateW instance is owned by a single thread
|
||||
* - When transitioning ownership between threads (with proper synchronization)
|
||||
*
|
||||
* Concurrent calls to InitializeCache() or calls concurrent with GetOccupant()
|
||||
* will result in undefined behavior due to race conditions.
|
||||
*/
|
||||
void InitializeCache();
|
||||
|
||||
/**
|
||||
* @brief Force regeneration of the occupants cache.
|
||||
* @deprecated Use RebuildOccupantsCache() instead. This method is kept for
|
||||
* backward compatibility but should not be used in new code.
|
||||
*/
|
||||
void InvalidateOccupantsCache();
|
||||
|
||||
private:
|
||||
// The occupants cache implementation - shared across copies for efficiency
|
||||
std::shared_ptr<OccupantsCache> occupantsCache_;
|
||||
};
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
@@ -42,6 +42,10 @@ using TileModifierProto = net::eagle0::shardok::common::TileModifier;
|
||||
return gameState.Get();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto ShardokEngine::GetCurrentGameStateW() const -> const GameStateW & {
|
||||
return gameState;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto ShardokEngine::GetCurrentGameStateBytes() const -> byte_vector {
|
||||
return gameState.ToByteVector();
|
||||
}
|
||||
@@ -253,7 +257,7 @@ auto ShardokEngine::PostWhilePlayerHasOnlyOneOption(
|
||||
}
|
||||
|
||||
auto updateAction = UpdateGameStatusAction(
|
||||
GetCurrentGameState(),
|
||||
GetCurrentGameStateW(),
|
||||
criticalTileCoords,
|
||||
settingsGetter,
|
||||
false /* atEndOfRound */);
|
||||
@@ -267,7 +271,7 @@ void ShardokEngine::HandlePlayerTurnEnd(const std::shared_ptr<RandomGenerator> &
|
||||
if (static_cast<uint32_t>(GetCurrentGameState()->current_player()) >=
|
||||
GetCurrentGameState()->player_infos()->size()) {
|
||||
ApplyAndAddActionResults(UpdateGameStatusAction(
|
||||
GetCurrentGameState(),
|
||||
GetCurrentGameStateW(),
|
||||
criticalTileCoords,
|
||||
settingsGetter,
|
||||
true /* atEndOfRound */)
|
||||
@@ -291,7 +295,7 @@ void ShardokEngine::HandlePlayerTurnEnd(const std::shared_ptr<RandomGenerator> &
|
||||
|
||||
} else {
|
||||
const auto updateAction = UpdateGameStatusAction(
|
||||
GetCurrentGameState(),
|
||||
GetCurrentGameStateW(),
|
||||
criticalTileCoords,
|
||||
settingsGetter,
|
||||
false /* atEndOfRound */);
|
||||
@@ -356,7 +360,7 @@ void ShardokEngine::PostPlacementCommands(
|
||||
}
|
||||
|
||||
const auto updateAction = UpdateGameStatusAction(
|
||||
GetCurrentGameState(),
|
||||
GetCurrentGameStateW(),
|
||||
criticalTileCoords,
|
||||
settingsGetter,
|
||||
false /* atEndOfRound */);
|
||||
@@ -438,7 +442,7 @@ void ShardokEngine::PostActionUnchecked(
|
||||
}
|
||||
|
||||
const auto updateAction = UpdateGameStatusAction(
|
||||
GetCurrentGameState(),
|
||||
GetCurrentGameStateW(),
|
||||
criticalTileCoords,
|
||||
settingsGetter,
|
||||
false /* atEndOfRound */);
|
||||
|
||||
@@ -112,8 +112,8 @@ public:
|
||||
|
||||
[[nodiscard]] auto GetGameStateAtStartOfAction(ActionId startingActionId) const -> GameStateW;
|
||||
|
||||
[[nodiscard]] auto GetCurrentGameState() const
|
||||
-> net::eagle0::shardok::storage::fb::GameState const *;
|
||||
[[nodiscard]] auto GetCurrentGameState() const -> fb::GameState const *;
|
||||
[[nodiscard]] auto GetCurrentGameStateW() const -> const GameStateW &;
|
||||
|
||||
[[nodiscard]] auto GetCurrentGameStateBytes() const -> byte_vector;
|
||||
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ auto PlayerSetupCommandFactory::AddAvailablePlaceAndHideUnitCommandsForOneUnit(
|
||||
|
||||
CoordsSet unusedStartingPositions(gameState->hex_map());
|
||||
for (const Coords *possiblePosition : *thisUnitStartingPositions) {
|
||||
if (!Occupant(gameState->units(), *possiblePosition)) {
|
||||
if (!gameState.GetOccupant(*possiblePosition)) {
|
||||
unusedStartingPositions.Add(*possiblePosition);
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ auto PlayerSetupCommandFactory::AddAvailablePlaceAndHideUnitCommandsForOneUnit(
|
||||
CoordsSet unusedHidingPositions(gameState->hex_map());
|
||||
|
||||
for (const Coords &possibleHidingPosition : GetAllCoords(gameState->hex_map())) {
|
||||
if (!Occupant(gameState->units(), possibleHidingPosition)) {
|
||||
if (!gameState.GetOccupant(possibleHidingPosition)) {
|
||||
const Terrain *terrain = GetTerrain(gameState->hex_map(), possibleHidingPosition);
|
||||
if (AllowsHiding(terrain)) { unusedHidingPositions.Add(possibleHidingPosition); }
|
||||
}
|
||||
|
||||
@@ -392,6 +392,9 @@ void MutatingApplyResult(
|
||||
mutatingGameState->mutable_possible_chargee_ids()->Mutate(i, -1);
|
||||
}
|
||||
|
||||
// Rebuild the occupants cache since units may have moved or been destroyed
|
||||
mutatingGameState.RebuildOccupantsCache();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,14 +71,15 @@ auto ToVector(const Units &units) -> vector<const Unit *> {
|
||||
|
||||
auto FallIntoWaterAction::AdjacentsWithTerrain(
|
||||
const Coords &location,
|
||||
const GameState *gameState,
|
||||
const GameStateW &gameState,
|
||||
const PlayerId playerId) -> vector<AdjacentWithTerrain> {
|
||||
const CoordsSet adjacentCoords = HexMapUtils::GetAdjacentCoords(gameState->hex_map(), location);
|
||||
|
||||
vector<AdjacentWithTerrain> vec{};
|
||||
|
||||
for (const Coords &adjCoords : adjacentCoords) {
|
||||
const auto *possibleOccupant = Occupant(gameState->units(), adjCoords);
|
||||
// Note: This function takes GameState*, not GameStateW, so cannot use cache
|
||||
const auto *possibleOccupant = gameState.GetOccupant(adjCoords);
|
||||
if (possibleOccupant &&
|
||||
(possibleOccupant->player_id() == playerId || !possibleOccupant->hidden())) {
|
||||
continue;
|
||||
@@ -150,8 +151,7 @@ auto FallIntoWaterAction::InternalExecute(
|
||||
Terrain bestTerrain{};
|
||||
|
||||
for (const auto &adjWithTerrain : adjacentCoordsAndTerrain) {
|
||||
const auto *possibleOccupant =
|
||||
Occupant(currentState->units(), adjWithTerrain.adjacentCoords);
|
||||
const auto *possibleOccupant = currentState.GetOccupant(adjWithTerrain.adjacentCoords);
|
||||
if (possibleOccupant && (possibleOccupant->player_id() == fallerAfter.player_id() ||
|
||||
!possibleOccupant->hidden())) {
|
||||
continue;
|
||||
@@ -172,7 +172,7 @@ auto FallIntoWaterAction::InternalExecute(
|
||||
}
|
||||
}
|
||||
|
||||
if (found && !Occupant(currentState->units(), bestCoords)) {
|
||||
if (found && !currentState.GetOccupant(bestCoords)) {
|
||||
PercentileRollOdds odds = EscapeChance(
|
||||
baseEscapeOdds,
|
||||
bestTerrain,
|
||||
|
||||
@@ -35,7 +35,7 @@ private:
|
||||
|
||||
static auto AdjacentsWithTerrain(
|
||||
const Coords& location,
|
||||
const GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
PlayerId playerId) -> vector<AdjacentWithTerrain>;
|
||||
|
||||
[[nodiscard]] auto InternalExecute(
|
||||
|
||||
@@ -183,7 +183,7 @@ auto MeteorCastAction::PerformOneActorCast(
|
||||
results.push_back(mainResult);
|
||||
|
||||
const Coords target = actorBefore->attached_hero().profession_info().cast_target();
|
||||
const Unit *possibleOccupant = Occupant(runningGameState->units(), target);
|
||||
const Unit *possibleOccupant = runningGameState.GetOccupant(target);
|
||||
const Terrain *targetTerrain = GetTerrain(startingGameState->hex_map(), target);
|
||||
if (possibleOccupant) {
|
||||
// Direct damage action
|
||||
@@ -248,7 +248,7 @@ auto MeteorCastAction::PerformOneActorCast(
|
||||
for (const Coords &splashCoords : adjacentCoords) {
|
||||
const auto &splashTerrain = GetTerrain(runningGameState->hex_map(), splashCoords);
|
||||
|
||||
const Unit *splashOccupant = Occupant(runningGameState->units(), splashCoords);
|
||||
const Unit *splashOccupant = runningGameState.GetOccupant(splashCoords);
|
||||
if (splashOccupant) {
|
||||
MeteorUnitDamageAction splashUnitDamageAction(
|
||||
settings,
|
||||
@@ -301,7 +301,7 @@ auto MeteorCastAction::PerformOneActorCast(
|
||||
|
||||
// Check for fallen heroes
|
||||
for (const Coords &coords : destroyedBridgeOrIceTiles) {
|
||||
const auto *maybeOccupant = Occupant(runningGameState->units(), coords);
|
||||
const auto *maybeOccupant = runningGameState.GetOccupant(coords);
|
||||
if (maybeOccupant) {
|
||||
const BattalionTypeSPtr &battalionType =
|
||||
settings.GetBattalionType(maybeOccupant->battalion().type());
|
||||
|
||||
@@ -229,7 +229,7 @@ auto UpdateGameStatusAction::InternalExecute(
|
||||
// Check for attacker occupying castles & towns
|
||||
bool foundUncontrolledCriticalTile = false;
|
||||
for (const auto& criticalTile : criticalTileLocations) {
|
||||
const auto* possibleOccupant = Occupant(gameState->units(), criticalTile);
|
||||
const auto* possibleOccupant = gameState.GetOccupant(criticalTile);
|
||||
|
||||
if (!possibleOccupant) {
|
||||
foundUncontrolledCriticalTile = true;
|
||||
|
||||
@@ -23,7 +23,7 @@ private:
|
||||
const std::shared_ptr<RandomGenerator>& generator) const
|
||||
-> vector<ActionResult> override;
|
||||
|
||||
const GameState* gameState;
|
||||
const GameStateW& gameState;
|
||||
const CoordsSet criticalTileLocations;
|
||||
const SettingsGetter settingsGetter;
|
||||
const bool atEndOfRound;
|
||||
@@ -34,7 +34,7 @@ private:
|
||||
|
||||
public:
|
||||
UpdateGameStatusAction(
|
||||
const GameState* gameState,
|
||||
const GameStateW& gameState,
|
||||
const CoordsSet& criticalTileLocations,
|
||||
const SettingsGetter& settingsGetter,
|
||||
const bool atEndOfRound)
|
||||
|
||||
@@ -39,7 +39,7 @@ auto UpdateOpponentKnowledgeAction::InternalExecute(
|
||||
|
||||
for (const Coords &adjCoords :
|
||||
HexMapUtils::GetAdjacentCoords(currentState->hex_map(), unit->location())) {
|
||||
const auto &occupantOptional = Occupant(currentState->units(), adjCoords);
|
||||
const auto &occupantOptional = currentState.GetOccupant(adjCoords);
|
||||
if (occupantOptional && occupantOptional->player_id() != unitPid) {
|
||||
MutatingBumpOpponentKnowledge(
|
||||
&unitAfter,
|
||||
|
||||
@@ -44,6 +44,8 @@ auto ReduceCommandFactory::AddAvailableReduceCommands(
|
||||
const auto *const occupant = Occupant(units, reduceCoords);
|
||||
if (occupant && occupant->player_id() == playerId) continue;
|
||||
|
||||
// This function takes a Units*, not a GameStateW, so we can't use the cache method
|
||||
// yet
|
||||
const auto *const enemyOccupant =
|
||||
KnownEnemyOccupant(playerId, units, allyPids, reduceCoords);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ auto HideCommand::InternalExecute(
|
||||
hideResult.mutable_player()->set_value(GetPlayerId());
|
||||
hideResult.mutable_actor()->set_value(actorId);
|
||||
|
||||
const auto *occupant = Occupant(currentState->units(), target);
|
||||
const auto *occupant = currentState.GetOccupant(target);
|
||||
if (occupant) {
|
||||
auto occupantAfter = *occupant;
|
||||
occupantAfter.mutate_hidden(false);
|
||||
@@ -56,7 +56,7 @@ auto HideCommand::InternalExecute(
|
||||
vector<Unit> adjacentEnemyRangers{};
|
||||
for (const auto &adjCoords :
|
||||
HexMapUtils::GetAdjacentCoords(currentState->hex_map(), target)) {
|
||||
const auto *oneOverOccupant = Occupant(currentState->units(), adjCoords);
|
||||
const auto *oneOverOccupant = currentState.GetOccupant(adjCoords);
|
||||
if (!oneOverOccupant) continue;
|
||||
PlayerId occupantPid = oneOverOccupant->player_id();
|
||||
if (occupantPid == GetPlayerId()) continue;
|
||||
|
||||
@@ -230,7 +230,7 @@ auto HolyWaveCommand::InternalExecute(
|
||||
auto allyPids = AlliedPids(runningState, GetPlayerId());
|
||||
for (const auto &coords :
|
||||
HexMapUtils::GetAdjacentCoords(runningState->hex_map(), actorBefore->location())) {
|
||||
const auto *occupant = Occupant(runningState->units(), coords);
|
||||
const auto *occupant = runningState.GetOccupant(coords);
|
||||
if (occupant) {
|
||||
if (occupant->battalion().type() ==
|
||||
net::eagle0::shardok::storage::fb::BattalionTypeId_UNDEAD) {
|
||||
|
||||
@@ -50,7 +50,7 @@ auto RaiseDeadCommand::InternalExecute(
|
||||
|
||||
auto &mutableActingHero = actorAfter.mutable_attached_hero();
|
||||
|
||||
const auto *occupant = Occupant(currentState->units(), target);
|
||||
const auto *occupant = currentState.GetOccupant(target);
|
||||
if (!occupant && PercentileRollSucceeds(odds, roll)) {
|
||||
result.set_type(ActionType::RAISED_UNDEAD);
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ auto ReduceCommand::InternalExecute(
|
||||
damageGiven);
|
||||
*result.add_changed_tile_modifiers() = MakeTmc(target, modifierAfter);
|
||||
|
||||
const Unit* targetOccupant = Occupant(currentState->units(), target);
|
||||
const Unit* targetOccupant = currentState.GetOccupant(target);
|
||||
if (targetOccupant) {
|
||||
CombatDamage unitDamageGiven =
|
||||
CombatDamage::Builder()
|
||||
|
||||
@@ -60,7 +60,7 @@ auto ScoutCommand::InternalExecute(
|
||||
|
||||
AddChangedUnit(result, actorAfter);
|
||||
|
||||
const auto* directUnit = Occupant(currentState->units(), target);
|
||||
const auto* directUnit = currentState.GetOccupant(target);
|
||||
|
||||
if (directUnit) {
|
||||
auto possibleTarget =
|
||||
@@ -70,7 +70,7 @@ auto ScoutCommand::InternalExecute(
|
||||
|
||||
for (const auto& adjacentCoords :
|
||||
HexMapUtils::GetAdjacentCoords(currentState->hex_map(), target)) {
|
||||
const auto* adjacentUnit = Occupant(currentState->units(), adjacentCoords);
|
||||
const auto* adjacentUnit = currentState.GetOccupant(adjacentCoords);
|
||||
auto adjacentTarget =
|
||||
InternalScoutOneTile(actor, adjacentUnit, scoutAdjacentKnowledgeIncrement);
|
||||
if (adjacentTarget.has_value()) { AddChangedUnit(result, adjacentTarget.value()); }
|
||||
|
||||
@@ -298,6 +298,7 @@ auto GameStateGuesser::GuessedState(
|
||||
}
|
||||
}
|
||||
|
||||
// Cache is already initialized from construction, no need to rebuild
|
||||
return gsw;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -215,6 +215,7 @@ TEST_F(PlayerSetupCommandFactoryTest, unplacedDefendingRanger_cannotHideOnOccupi
|
||||
// Put an enemy at 2, 2
|
||||
attacker1->mutable_location() = Coords(2, 2);
|
||||
attacker1->mutate_status(net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT);
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
CommandList commands{};
|
||||
factory.AddAvailablePlayerSetupCommands(commands, 1, gameState);
|
||||
|
||||
@@ -66,7 +66,7 @@ TEST_F(UpdateGameStatusActionTests, onePlayerLeft_addsGameOverResult) {
|
||||
GetGameSettingsGetter(),
|
||||
false);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameState, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
EXPECT_EQ(net::eagle0::shardok::common::GAME_OVER, results[results.size() - 1].type());
|
||||
EXPECT_EQ(
|
||||
@@ -111,7 +111,7 @@ TEST_F(UpdateGameStatusActionTests, twoPlayersLeft_noGameOverResult) {
|
||||
GetGameSettingsGetter(),
|
||||
false);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameState, nullptr);
|
||||
EXPECT_EQ(results.size(), 0);
|
||||
}
|
||||
|
||||
@@ -146,16 +146,15 @@ TEST_F(UpdateGameStatusActionTests, onePlayerAllUnitsHidden_gameOverResult) {
|
||||
|
||||
auto gameState = Wrapper<GameState>(fbb);
|
||||
gameState->mutable_units()->GetMutableObject(1)->mutate_hidden(true);
|
||||
|
||||
gameState->mutable_units()->GetMutableObject(1)->mutate_hidden(true);
|
||||
GameStateW gameStateW(gameState);
|
||||
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameState,
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
false);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
EXPECT_EQ(net::eagle0::shardok::common::GAME_OVER, results[results.size() - 1].type());
|
||||
EXPECT_EQ(
|
||||
@@ -198,7 +197,7 @@ TEST_F(UpdateGameStatusActionTests, remainingPlayerDoesNotHaveLastPlayerStanding
|
||||
GetGameSettingsGetter(),
|
||||
false);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameState, nullptr);
|
||||
EXPECT_EQ(0, results.size());
|
||||
}
|
||||
|
||||
@@ -230,21 +229,32 @@ TEST_F(UpdateGameStatusActionTests, attackerHoldsAllCastles_gameOverResult) {
|
||||
statusBuilder.add_state(net::eagle0::shardok::storage::fb::GameStatus_::State_GAME_RUNNING);
|
||||
auto statusOffset = statusBuilder.Finish();
|
||||
|
||||
// Create a minimal hex map for the cache
|
||||
auto hexMapBuilder = net::eagle0::shardok::storage::fb::HexMapBuilder(fbb);
|
||||
hexMapBuilder.add_row_count(10);
|
||||
hexMapBuilder.add_column_count(10);
|
||||
auto hexMapOffset = hexMapBuilder.Finish();
|
||||
|
||||
net::eagle0::shardok::storage::fb::GameStateBuilder gsBuilder(fbb);
|
||||
gsBuilder.add_player_infos(playersOffset);
|
||||
gsBuilder.add_units(unitsOffset);
|
||||
gsBuilder.add_status(statusOffset);
|
||||
gsBuilder.add_hex_map(hexMapOffset);
|
||||
|
||||
fbb.Finish(gsBuilder.Finish());
|
||||
|
||||
auto gameState = Wrapper<GameState>(fbb);
|
||||
GameStateW gameStateW(gameState);
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
EXPECT_EQ(net::eagle0::shardok::common::GAME_OVER, results[results.size() - 1].type());
|
||||
EXPECT_EQ(
|
||||
@@ -287,18 +297,29 @@ TEST_F(UpdateGameStatusActionTests, attackerHoldsAllCastles_hiddenDefenderFlees)
|
||||
gsBuilder.add_units(unitsOffset);
|
||||
gsBuilder.add_status(statusOffset);
|
||||
|
||||
// Create a minimal hex map for the cache
|
||||
auto hexMapBuilder = net::eagle0::shardok::storage::fb::HexMapBuilder(fbb);
|
||||
hexMapBuilder.add_row_count(10);
|
||||
hexMapBuilder.add_column_count(10);
|
||||
auto hexMapOffset = hexMapBuilder.Finish();
|
||||
|
||||
gsBuilder.add_hex_map(hexMapOffset);
|
||||
fbb.Finish(gsBuilder.Finish());
|
||||
|
||||
auto gameState = Wrapper<GameState>(fbb);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_hidden(true);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_can_flee(true);
|
||||
GameStateW gameStateW(gameState);
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
|
||||
auto finalResult = results[0];
|
||||
@@ -351,13 +372,16 @@ TEST_F(UpdateGameStatusActionTests,
|
||||
auto gameState = Wrapper<GameState>(fbb);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_hidden(true);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_can_flee(false);
|
||||
GameStateW gameStateW(gameState);
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
|
||||
auto finalResult = results[0];
|
||||
@@ -405,15 +429,17 @@ TEST_F(UpdateGameStatusActionTests, attackerHoldsAllCastles_hiddenDefenderTooTir
|
||||
auto gameState = Wrapper<GameState>(fbb);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_hidden(true);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_can_flee(true);
|
||||
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutable_attached_hero().mutate_vigor(8);
|
||||
GameStateW gameStateW(gameState);
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
|
||||
auto finalResult = results[0];
|
||||
@@ -461,13 +487,16 @@ TEST_F(UpdateGameStatusActionTests, attackerHoldsAllCastles_notHiddenDefender_do
|
||||
auto gameState = Wrapper<GameState>(fbb);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_hidden(false);
|
||||
gameState->mutable_units()->GetMutableObject(2)->mutate_can_flee(true);
|
||||
GameStateW gameStateW(gameState);
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
|
||||
auto finalResult = results[0];
|
||||
@@ -520,7 +549,7 @@ TEST_F(UpdateGameStatusActionTests, attackerHoldsAllCastles_notEndOfRound_noGame
|
||||
GetGameSettingsGetter(),
|
||||
false);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameState, nullptr);
|
||||
EXPECT_EQ(0, results.size());
|
||||
}
|
||||
|
||||
@@ -564,10 +593,14 @@ TEST_F(UpdateGameStatusActionTests,
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
GameStateW gameStateW(gameState);
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_EQ(0, results.size());
|
||||
}
|
||||
|
||||
@@ -619,10 +652,14 @@ TEST_F(UpdateGameStatusActionTests, allianceVictory_alliesHaveConditionSet_retur
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
GameStateW gameStateW(gameState);
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_GE(results.size(), 1);
|
||||
EXPECT_EQ(net::eagle0::shardok::common::GAME_OVER, results[results.size() - 1].type());
|
||||
EXPECT_EQ(
|
||||
@@ -678,10 +715,14 @@ TEST_F(UpdateGameStatusActionTests, allianceVictory_notAllAlliesHaveConditionSet
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
GameStateW gameStateW(gameState);
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_EQ(0, results.size());
|
||||
}
|
||||
|
||||
@@ -733,9 +774,13 @@ TEST_F(UpdateGameStatusActionTests, allianceVictory_notAllAllied_returnsNoAction
|
||||
|
||||
criticalTileLocations.Add(Coords(0, 4));
|
||||
|
||||
auto action =
|
||||
UpdateGameStatusAction(gameState, criticalTileLocations, GetGameSettingsGetter(), true);
|
||||
GameStateW gameStateW(gameState);
|
||||
auto action = UpdateGameStatusAction(
|
||||
gameStateW,
|
||||
criticalTileLocations,
|
||||
GetGameSettingsGetter(),
|
||||
true);
|
||||
|
||||
vector<ActionResult> results = action.Execute(GameStateW(), nullptr);
|
||||
vector<ActionResult> results = action.Execute(gameStateW, nullptr);
|
||||
EXPECT_EQ(0, results.size());
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ TEST_F(UpdateOpponentKnowledgeActionTests, attackerGainsOneOnDefender) {
|
||||
|
||||
TEST_F(UpdateOpponentKnowledgeActionTests, defenderGainsMoreIfAdjacent) {
|
||||
defender->mutable_location() = adjacentPosition;
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
const auto action = UpdateOpponentKnowledgeAction(GetGameSettingsGetter());
|
||||
|
||||
@@ -113,6 +114,7 @@ TEST_F(UpdateOpponentKnowledgeActionTests, defenderGainsMoreIfAdjacent) {
|
||||
|
||||
TEST_F(UpdateOpponentKnowledgeActionTests, attackerGainsMoreIfAdjacent) {
|
||||
defender->mutable_location() = adjacentPosition;
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
const auto action = UpdateOpponentKnowledgeAction(GetGameSettingsGetter());
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ class HideCommandTests : public ::testing::Test {
|
||||
enemy->mutable_attached_hero().mutable_profession_info().mutate_profession(
|
||||
net::eagle0::shardok::storage::fb::Profession_RANGER);
|
||||
enemy->mutate_hidden(true);
|
||||
|
||||
// Rebuild cache after mutations
|
||||
gameState.RebuildOccupantsCache();
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -104,6 +107,7 @@ TEST_F(HideCommandTests, Execute_setsHidden) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteIntoOccupant_fails) {
|
||||
enemy->mutable_location() = target;
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
const ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
@@ -114,6 +118,7 @@ TEST_F(HideCommandTests, ExecuteIntoOccupant_fails) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteIntoOccupant_doesNotMove) {
|
||||
enemy->mutable_location() = target;
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
@@ -125,6 +130,7 @@ TEST_F(HideCommandTests, ExecuteIntoOccupant_doesNotMove) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteIntoOccupant_doesNotHide) {
|
||||
enemy->mutable_location() = target;
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
@@ -136,6 +142,7 @@ TEST_F(HideCommandTests, ExecuteIntoOccupant_doesNotHide) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteIntoOccupant_revealsOccupant) {
|
||||
enemy->mutable_location() = target;
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
@@ -147,6 +154,7 @@ TEST_F(HideCommandTests, ExecuteIntoOccupant_revealsOccupant) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteNextToRanger_fails) {
|
||||
enemy->mutable_location() = Coords(2, 4);
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
const ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
@@ -157,6 +165,7 @@ TEST_F(HideCommandTests, ExecuteNextToRanger_fails) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteNextToRanger_doesNotHide) {
|
||||
enemy->mutable_location() = Coords(2, 4);
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
@@ -168,6 +177,7 @@ TEST_F(HideCommandTests, ExecuteNextToRanger_doesNotHide) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteNextToRanger_stillMoves) {
|
||||
enemy->mutable_location() = Coords(2, 4);
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
@@ -179,6 +189,7 @@ TEST_F(HideCommandTests, ExecuteNextToRanger_stillMoves) {
|
||||
|
||||
TEST_F(HideCommandTests, ExecuteNextToRanger_revealsEnemy) {
|
||||
enemy->mutable_location() = Coords(2, 4);
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
ActionResult hideResult =
|
||||
HideCommand(cost, rangerPid, ranger->unit_id(), target, agiXp, minimumKnowledge)
|
||||
|
||||
@@ -189,6 +189,7 @@ TEST_F(RaiseDeadCommandTest, successRollOnHiddenDefender_fails) {
|
||||
|
||||
other->mutable_location() = target;
|
||||
other->mutate_hidden(true);
|
||||
gameState.RebuildOccupantsCache();
|
||||
|
||||
const auto results = RaiseDeadCommand(
|
||||
cost,
|
||||
|
||||
Reference in New Issue
Block a user