Compare commits

..
Author SHA1 Message Date
admin c541085956 Preserve Unity 6000.4 generated iOS outputs 2026-06-22 09:10:40 -07:00
admin c97c141155 Revert "Upgrade Unity client to 6000.5.0f1 (#7325)"
This reverts commit 6a98cd8d6d.
2026-06-22 08:34:01 -07:00
387 changed files with 3231 additions and 18208 deletions
+15 -1
View File
@@ -255,7 +255,7 @@ jobs:
echo "=== All images pushed successfully ==="
deploy:
runs-on: ubuntu-latest
runs-on: [self-hosted, bazel]
needs: [build-all]
if: needs.build-all.outputs.skip_build != 'true' && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true'))
environment: production
@@ -334,11 +334,25 @@ jobs:
ssh-keyscan -H "$DO_DROPLET_IP" >> ~/.ssh/known_hosts 2>/dev/null || true
- name: Download warmup binary
id: download-warmup
continue-on-error: true
uses: actions/download-artifact@v8
with:
name: warmup-binary
path: scripts/bin/
- name: Build warmup binary fallback
if: steps.download-warmup.outcome != 'success'
run: |
echo "::warning::warmup-binary artifact was unavailable; rebuilding warmup binary before deploy"
./ci/github_actions/ensure_bazel_installed.sh
bazel build \
--platforms=//:linux_x86_64 \
--extra_toolchains=@llvm_toolchain_linux//:cc-toolchain-x86_64-linux \
//ci:warmup_tar
mkdir -p scripts/bin
tar -xf bazel-bin/ci/warmup_tar.tar -C scripts/bin --strip-components=1
- name: Copy config files to droplet
run: |
# Create directory structure on remote
+2 -24
View File
@@ -50,11 +50,9 @@ on:
- "scripts/notarize_wait.sh"
- "ci/github_actions/build_mac.sh"
- "ci/github_actions/build_unity_mac.sh"
- "ci/github_actions/ensure_unity_installed.sh"
- "ci/github_actions/ensure_bazel_installed.sh"
- ".github/actions/setup-bazel/**"
- "ci/github_actions/detect_addressables_changes.sh"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectSettings.asset"
- ".bazelrc"
- "ci/mac/**"
@@ -125,25 +123,11 @@ jobs:
- name: Clean stale files
run: |
git clean -ffd
LIBRARY_DIR="src/main/csharp/net/eagle0/clients/unity/eagle0/Library"
VERSION_CACHE="$LIBRARY_DIR/.last_unity_version"
PROJECT_VERSION_FILE="src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
CURRENT_VERSION=$(grep "m_EditorVersion:" "$PROJECT_VERSION_FILE" | head -1 | sed 's/m_EditorVersion: //')
# Nuke Library/ when Unity version changes to avoid import loops
if [ -f "$VERSION_CACHE" ]; then
CACHED_VERSION=$(cat "$VERSION_CACHE")
if [ "$CACHED_VERSION" != "$CURRENT_VERSION" ]; then
echo "Unity version changed ($CACHED_VERSION -> $CURRENT_VERSION) -- clearing Library/"
rm -rf "$LIBRARY_DIR"
fi
fi
# Only clear Bee/ when C# files were added/deleted/renamed (structural
# changes that stale the DAG). Content-only modifications are handled by
# Bee's incremental compilation. See persist_library.sh for background.
BEE_DIR="$LIBRARY_DIR/Bee"
SHA_FILE="$LIBRARY_DIR/.last_built_sha"
BEE_DIR="src/main/csharp/net/eagle0/clients/unity/eagle0/Library/Bee"
SHA_FILE="src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_built_sha"
if [ -f "$SHA_FILE" ] && [ -d "$BEE_DIR" ]; then
LAST_SHA=$(cat "$SHA_FILE")
if git diff --diff-filter=ADR --name-only "$LAST_SHA" HEAD -- '*.cs' '*.csproj' '*.asmdef' 2>/dev/null | grep -q .; then
@@ -200,12 +184,6 @@ jobs:
mkdir -p src/main/csharp/net/eagle0/clients/unity/eagle0/Library
git rev-parse HEAD > src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_built_sha
- name: Save Unity version for Library/ cache invalidation
if: success()
run: |
mkdir -p src/main/csharp/net/eagle0/clients/unity/eagle0/Library
grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //' > src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_unity_version
- name: Check if should deploy
id: check-deploy
run: |
+1 -3
View File
@@ -196,9 +196,7 @@ jobs:
echo "Also tagged as: registry.digitalocean.com/eagle0/shardok-server:arm64-latest"
deploy-hetzner:
# Keep this on a self-hosted Mac runner for IPv6 reachability to Hetzner,
# but do not require a Bazel runner slot for artifact transport/deploy work.
runs-on: [self-hosted, macOS, ARM64]
runs-on: [self-hosted, bazel]
needs: [build-shardok-arm64]
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
environment: production
+1 -20
View File
@@ -29,16 +29,6 @@ If you catch yourself about to run `git push origin main` or `git push origin <b
If you catch yourself about to run `gh pr merge`, STOP. Only the user merges PRs.
## Commit History During Review
Prefer new follow-up commits over amending existing commits once a PR is open and the user is actively reviewing or
testing it. This preserves working interim states so the user can inspect, compare, or return to a known-good point
while improvements continue.
Use `git commit --amend` and force-push only when the user explicitly asks for history cleanup, when fixing metadata
before review has started, or when repairing a local commit that has not been pushed. If you believe squashing is the
right choice despite an open review, explain why and ask first.
## Worktree Usage
Prefer using the current worktree for small requested fixes. Create a separate worktree only when the user asks for one,
@@ -68,15 +58,6 @@ For PRs expected to trigger substantial Bazel work in CI, run the relevant Bazel
workstation is fast, and local Bazel runs can help hydrate the remote cache for the CI builders while still giving
earlier signal on failures.
## Cleanup PR Batching
When making mechanical cleanup changes, group 3-5 files per PR when the files are receiving the same kind of change
and can be reviewed as one pattern. Do not open one PR per file for nearly identical few-line cleanups, such as replacing
the same unsafe accessor pattern with the same contextual failure pattern across multiple Scala files.
Keep separate PRs for changes that involve different semantics, materially different risk, unrelated subsystems, or
files whose tests/validation strategy makes them better reviewed independently.
## GitHub CLI PR Bodies
When creating or editing PR descriptions with multiline bodies, write the body to a temporary markdown file and pass it
@@ -313,7 +294,7 @@ to be used for different players or game situations within the same server proce
**C# (Unity Client):**
- Located in `/src/main/csharp/net/eagle0/clients/unity/eagle0/`
- Uses Unity 6.5 (6000.5.0f1) with comprehensive protobuf integration (100+ .proto files)
- Uses Unity 6 (6000.4.10f1) with comprehensive protobuf integration (100+ .proto files)
- Key components: `EagleConnection.cs` (gRPC client), `EagleGameController.cs` (main game logic)
- Real-time bidirectional streaming with server via `PersistentClientConnection.cs`
- Strategic map UI in `Assets/Eagle/`, tactical battle UI in `Assets/Shardok/`
+13
View File
@@ -40,6 +40,19 @@ if [ -f "$INFO_PLIST" ]; then
/usr/libexec/PlistBuddy -c "Set :ITSAppUsesNonExemptEncryption false" "$INFO_PLIST" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :ITSAppUsesNonExemptEncryption bool false" "$INFO_PLIST"
echo "Set ITSAppUsesNonExemptEncryption=false in Info.plist"
# Remove UIApplicationSceneManifest if present.
# Unity 6 generates this key, which opts the app into the iOS scene-based
# lifecycle. This breaks Application.deepLinkActivated because iOS routes
# deep link URLs through UISceneDelegate instead of UIApplicationDelegate,
# and Unity doesn't implement the scene delegate path.
# See: https://issuetracker.unity3d.com/issues/in-135632
if /usr/libexec/PlistBuddy -c "Print :UIApplicationSceneManifest" "$INFO_PLIST" 2>/dev/null; then
echo "Found UIApplicationSceneManifest in Info.plist — removing to fix deep link handling"
/usr/libexec/PlistBuddy -c "Delete :UIApplicationSceneManifest" "$INFO_PLIST"
else
echo "No UIApplicationSceneManifest in Info.plist (deep links should work)"
fi
fi
# Unity always generates "Unity-iPhone" as the main app scheme
+2 -10
View File
@@ -49,7 +49,7 @@ check_modules_installed() {
fi
;;
mac)
if ! has_mac_il2cpp_support "$unity_path" "$unity_app_contents_path"; then
if ! has_il2cpp_variation "${unity_app_contents_path}/PlaybackEngines/MacStandaloneSupport/Variations"; then
echo "✗ Mac IL2CPP module not installed for Unity ${UNITY_VERSION}"
return 1
fi
@@ -67,7 +67,7 @@ check_modules_installed() {
echo "✗ iOS module missing"
missing=1
fi
if ! has_mac_il2cpp_support "$unity_path" "$unity_app_contents_path"; then
if ! has_il2cpp_variation "${unity_app_contents_path}/PlaybackEngines/MacStandaloneSupport/Variations"; then
echo "✗ Mac IL2CPP module missing"
missing=1
fi
@@ -84,14 +84,6 @@ check_modules_installed() {
return 0
}
has_mac_il2cpp_support() {
local unity_path="$1"
local unity_app_contents_path="$2"
has_il2cpp_variation "${unity_app_contents_path}/PlaybackEngines/MacStandaloneSupport/Variations" ||
has_il2cpp_variation "${unity_path}/PlaybackEngines/MacStandaloneSupport/Variations"
}
has_il2cpp_variation() {
local variations_path="$1"
local variation
Binary file not shown.
@@ -39,15 +39,14 @@ auto CalculateMap(
for (std::size_t i = 0; i < hexMap->attacker_starting_positions()->size(); i++) {
const auto* positionList = hexMap->attacker_starting_positions()->Get(i);
const auto* positions = positionList->positions();
if (positions->empty()) continue;
if (positions->size() != 10) {
if (positionList->positions()->empty()) continue;
if (positionList->positions()->size() != 10) {
std::cerr << "Should have 10 starting positions!" << '\n';
std::abort();
}
int positionsRequiringCrossing = 0;
for (const auto* startingPosition : *positions) {
for (const auto* startingPosition : *positionList->positions()) {
for (const shardok::Coords& castlePosition : criticalTileLocations) {
if (!shardok::CanReach(
*startingPosition,
@@ -58,7 +57,7 @@ auto CalculateMap(
battalionType)) {
++positionsRequiringCrossing;
break;
}
};
}
}
info.positionsRequiringCrossing[static_cast<int>(i)] = positionsRequiringCrossing;
@@ -32,10 +32,8 @@ auto main(const int argc, char** argv) -> int {
const shardok::BattalionTypeSPtr& battalionType = gameSettings->GetGetter().GetBattalionType(
net::eagle0::shardok::storage::fb::BattalionTypeId_LIGHT_INFANTRY);
const std::vector<std::string> mapNames = GetMapNames();
std::vector<OneMapInfo> mapInfos{};
mapInfos.reserve(mapNames.size());
for (const std::string& mapName : mapNames) {
for (const std::string& mapName : GetMapNames()) {
std::shared_ptr<shardok::ActionPointDistancesCache> apdCache =
std::make_shared<shardok::ActionPointDistancesCache>();
@@ -3,7 +3,6 @@
//
#include <algorithm>
#include <array>
#include <climits>
#include <iostream>
#include <memory>
@@ -11,8 +10,6 @@
#include <random>
#include <stdexcept>
#include <thread>
#include <utility>
#include <vector>
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
#include "src/main/cpp/net/eagle0/common/UnitConversions.hpp"
@@ -47,27 +44,25 @@ void runBattle(const std::vector<shardok::Unit>& units) {
if (fileNames.empty()) { throw std::runtime_error("No map files found"); }
if (units.size() < 2) { throw std::runtime_error("Need at least two units to run a battle"); }
const auto mapName = fileNames[randInt(0, static_cast<int>(fileNames.size()) - 1)];
const auto month = randInt(1, 12);
auto mapName = fileNames[randInt(0, (int)fileNames.size() - 1)];
auto month = randInt(1, 12);
const auto maxP1UnitCount = std::min<size_t>(20, units.size() - 1);
const auto p1UnitCount = randInt(1, static_cast<int>(maxP1UnitCount));
auto p1UnitCount = randInt(1, static_cast<int>(maxP1UnitCount));
std::vector<shardok::Unit> p1Units{};
p1Units.reserve(p1UnitCount);
for (int i = 0; i < p1UnitCount; i++) {
auto unit = units[i];
unit.mutate_player_id(0);
p1Units.push_back(std::move(unit));
p1Units.push_back(unit);
}
const auto maxP2UnitCount = std::min<size_t>(20, units.size() - p1UnitCount);
const auto p2UnitCount = randInt(1, static_cast<int>(maxP2UnitCount));
auto p2UnitCount = randInt(1, static_cast<int>(maxP2UnitCount));
std::vector<shardok::Unit> p2Units{};
p2Units.reserve(p2UnitCount);
for (int i = p1UnitCount; i < p1UnitCount + p2UnitCount; i++) {
auto unit = units[i];
unit.mutate_player_id(1);
p2Units.push_back(std::move(unit));
p2Units.push_back(unit);
}
std::cout << "map name is " << mapName << ", " << p1UnitCount << " attacking " << p2UnitCount
@@ -80,34 +75,32 @@ void runBattle(const std::vector<shardok::Unit>& units) {
VictoryConditionProto::VICTORY_CONDITION_LAST_PLAYER_STANDING,
VictoryConditionProto::VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS};
std::vector<shardok::PlayerInfoWithUnits> playerInfos;
playerInfos.reserve(2);
playerInfos.emplace_back(
auto p1 = shardok::PlayerInfoWithUnits(
0,
true,
5,
false,
10000,
attVictoryConditions,
std::vector<shardok::PlayerId>{},
{},
p1Units,
false);
playerInfos.emplace_back(
auto p2 = shardok::PlayerInfoWithUnits(
1,
true,
7,
true,
10000,
defVictoryConditions,
std::vector<shardok::PlayerId>{},
{},
p2Units,
false);
const std::string gameId = std::to_string(randInt(INT_MIN, INT_MAX));
std::string gameId = std::to_string(randInt(INT_MIN, INT_MAX));
manager->UnlockedCreateSpecifiedGame(
gameId,
playerInfos,
{p1, p2},
mapName,
"",
month,
@@ -142,7 +135,10 @@ auto LoadUnits() -> std::vector<shardok::Unit> {
auto main(int /*argc*/, char** argv) -> int {
#if FIXED_SEED
std::srand(4564564);
g.seed(378473);
#else
std::srand(std::time(nullptr));
#endif
FilesystemUtils::SetExecPath(argv[0]);
@@ -156,7 +152,7 @@ auto main(int /*argc*/, char** argv) -> int {
manager = std::make_unique<shardok::ShardokGamesManager>(std::vector<std::string>());
std::array<std::thread, THREAD_COUNT> threads;
std::thread threads[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = std::thread(runBattlesThread, randomUnits);
@@ -153,7 +153,6 @@ auto GenerateTargetPriorities(
vector<TargetAndAttackLocations>{});
vector<TargetAndDistance> targetsWithDistance;
targetsWithDistance.reserve(targets.size());
// Get APDs directly from cache (now with built-in thread-local optimization)
const auto& battType = battalionTypeGetter(unit->battalion().type());
@@ -33,7 +33,6 @@ auto AIAttackerStrategySelector::BestAttackerStrategy(
bool canFlee = false;
vector<const Unit*> attackerUnits{};
attackerUnits.reserve(gameState->units()->size());
CoordsSet defenderPositions(gameState->hex_map());
for (const Unit* unit : *gameState->units()) {
if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT &&
@@ -132,4 +131,4 @@ auto AIAttackerStrategySelector::BestAttackerStrategy(
return chosenStrategy;
}
} // namespace shardok
} // namespace shardok
@@ -327,9 +327,9 @@ auto AICommandEvaluator::FindBestCommand(
// Structure to hold all command evaluation data
struct CommandEvaluation {
size_t index{};
CommandType type{};
ScoreValue immediateScore{};
size_t index;
CommandType type;
ScoreValue immediateScore;
bool immediateCompleted = true;
std::vector<std::future<EvaluationResult>> lookaheadFutures;
};
@@ -422,7 +422,6 @@ auto AICommandEvaluator::FindBestCommand(
} else {
ScoreValue sum = 0.0;
const int sampleCount = std::max(1, maxRepeatCount);
commandEvaluations[index].lookaheadFutures.reserve(sampleCount);
for (int repeatIteration = 0; repeatIteration < sampleCount; repeatIteration++) {
// In each iteration, use a double from [0, 1] as the random roll
auto sequence =
@@ -433,7 +432,7 @@ auto AICommandEvaluator::FindBestCommand(
originalIndex,
remainingLookahead,
maxRepeatCount,
std::make_shared<SequenceRandomGenerator>(std::move(sequence)),
std::make_shared<SequenceRandomGenerator>(sequence),
guessedEngine,
attackerStrategy,
allCastleCoords,
@@ -512,15 +511,13 @@ auto AICommandEvaluator::EvaluateCommand(
}
const auto& guessedDescriptor = guessedDescriptors->at(commandIndex);
const auto guessedCommandType = guessedDescriptor->GetCommandType();
if (guessedCommandType == net::eagle0::shardok::common::END_TURN_COMMAND) {
if (const auto guessedCommandType = guessedDescriptor->GetCommandType();
guessedCommandType == net::eagle0::shardok::common::END_TURN_COMMAND) {
std::promise<EvaluationResult> p;
p.set_value(EvaluationResult{.score = currentUtility, .completed = true});
return p.get_future();
}
if (IsDeterministic(guessedCommandType)) {
} else if (IsDeterministic(guessedCommandType)) {
auto evaluation = EvaluateWithRandomness(
pid,
isDefender,
@@ -533,9 +530,7 @@ auto AICommandEvaluator::EvaluateCommand(
allCastleCoords,
deadline);
return std::move(evaluation.lookaheadScore);
}
if (guessedDescriptor->HasOdds()) {
} else if (guessedDescriptor->HasOdds()) {
const auto successChancePercentile = guessedDescriptor->GetOddsPercentile();
const double successChance = static_cast<double>(successChancePercentile) / 100.0;
@@ -580,47 +575,47 @@ auto AICommandEvaluator::EvaluateCommand(
.score = std::lerp(failure.score, success.score, successChance),
.completed = true};
});
} else {
// For non-deterministic commands without odds, use multiple attempts
std::vector<std::future<EvaluationResult>> lookaheadFutures;
const int sampleCount = std::max(1, maxRepeatCount);
lookaheadFutures.reserve(sampleCount);
for (int repeatIteration = 0; repeatIteration < sampleCount; repeatIteration++) {
auto sequence = std::vector{RandomnessSampleForRepeat(repeatIteration, sampleCount)};
auto evaluation = EvaluateWithRandomness(
pid,
isDefender,
commandIndex,
remainingLookahead,
maxRepeatCount,
std::make_shared<SequenceRandomGenerator>(sequence),
guessedEngine,
attackerStrategy,
allCastleCoords,
deadline);
lookaheadFutures.push_back(std::move(evaluation.lookaheadScore));
}
// Return a future that computes the average when needed
return std::async(
std::launch::deferred,
[lookaheadFutures = std::move(lookaheadFutures),
sampleCount]() mutable -> EvaluationResult {
ScoreValue total = 0.0;
bool completed = true;
for (auto& future : lookaheadFutures) {
const auto result = future.get();
if (!result.completed) { completed = false; }
total += result.score;
}
if (!completed) { return EvaluationResult{.score = 0.0, .completed = false}; }
return EvaluationResult{
.score = total / static_cast<ScoreValue>(sampleCount),
.completed = true};
});
}
// For non-deterministic commands without odds, use multiple attempts
std::vector<std::future<EvaluationResult>> lookaheadFutures;
const int sampleCount = std::max(1, maxRepeatCount);
lookaheadFutures.reserve(sampleCount);
for (int repeatIteration = 0; repeatIteration < sampleCount; repeatIteration++) {
auto sequence = std::vector{RandomnessSampleForRepeat(repeatIteration, sampleCount)};
auto evaluation = EvaluateWithRandomness(
pid,
isDefender,
commandIndex,
remainingLookahead,
maxRepeatCount,
std::make_shared<SequenceRandomGenerator>(std::move(sequence)),
guessedEngine,
attackerStrategy,
allCastleCoords,
deadline);
lookaheadFutures.push_back(std::move(evaluation.lookaheadScore));
}
// Return a future that computes the average when needed
return std::async(
std::launch::deferred,
[lookaheadFutures = std::move(lookaheadFutures),
sampleCount]() mutable -> EvaluationResult {
ScoreValue total = 0.0;
bool completed = true;
for (auto& future : lookaheadFutures) {
const auto result = future.get();
if (!result.completed) { completed = false; }
total += result.score;
}
if (!completed) { return EvaluationResult{.score = 0.0, .completed = false}; }
return EvaluationResult{
.score = total / static_cast<ScoreValue>(sampleCount),
.completed = true};
});
}
} // namespace shardok
@@ -57,11 +57,11 @@ public:
/// Find the best command among all available commands at the given depth.
struct IndexAndScore {
size_t index{};
CommandType type{};
ScoreValue lookaheadScore{};
ScoreValue immediateScore{};
bool completed{};
size_t index;
CommandType type;
ScoreValue lookaheadScore;
ScoreValue immediateScore;
bool completed;
};
[[nodiscard]] auto FindBestCommand(
@@ -81,8 +81,8 @@ private:
BattalionTypeGetter battalionTypeGetter_; // Store by value
struct ImmediateAndLookaheadScore {
ScoreValue immediateScore{};
bool completed{};
ScoreValue immediateScore;
bool completed;
std::future<EvaluationResult> lookaheadScore;
};
@@ -32,7 +32,6 @@ auto AIDefenderStrategySelector::BestDefenderStrategy(
bool canFlee = false;
vector<UnitId> attackerUnitIdsRequiringWaterCrossing{};
attackerUnitIdsRequiringWaterCrossing.reserve(gameState->units()->size());
for (const auto& player : *gameState->player_infos()) {
if (!player->is_defender()) {
const auto& unitIdsRequiringWaterCrossing = UnitIdsRequiringWaterCrossing(
@@ -5,27 +5,15 @@
#ifndef EAGLE0_SHARDOK_AI_AI_EXPERIMENT_CONFIG_HPP
#define EAGLE0_SHARDOK_AI_AI_EXPERIMENT_CONFIG_HPP
#include <charconv>
#include <cstdlib>
#include <string_view>
#include <system_error>
#include <string>
namespace shardok {
inline auto CurrentAIExperimentId() -> int {
const char* rawValue = std::getenv("SHARDOK_SCORING_EXPERIMENT_ID");
if (rawValue == nullptr) { return 0; }
const std::string_view value(rawValue);
if (value.empty()) { return 0; }
int experimentId = 0;
const auto parseResult =
std::from_chars(value.data(), value.data() + value.size(), experimentId);
if (parseResult.ec != std::errc{} || parseResult.ptr != value.data() + value.size()) {
return 0;
}
return experimentId;
if (rawValue == nullptr || std::string(rawValue).empty()) { return 0; }
return std::atoi(rawValue);
}
inline auto IsAIExperiment(const int id) -> bool { return CurrentAIExperimentId() == id; }
@@ -25,11 +25,6 @@ class AIEvaluationCounter {
public:
AIEvaluationCounter();
~AIEvaluationCounter();
AIEvaluationCounter(const AIEvaluationCounter&) = delete;
auto operator=(const AIEvaluationCounter&) -> AIEvaluationCounter& = delete;
AIEvaluationCounter(AIEvaluationCounter&&) = delete;
auto operator=(AIEvaluationCounter&&) -> AIEvaluationCounter& = delete;
static int GetCurrentCount();
};
@@ -46,8 +41,8 @@ struct AITimeBudget {
// If isAllAiBattle is true, uses allAiBattleTimeBudgetMaximum instead of the normal maximum.
auto CalculateTimeBudget(
PlayerId playerId,
const GameSettingsSPtr& settings,
const GameStateW& state,
const GameSettingsSPtr &settings,
const GameStateW &state,
size_t numCommands,
bool isAllAiBattle) -> AITimeBudget;
@@ -11,6 +11,10 @@
namespace shardok {
using std::begin;
using std::end;
using std::shared_ptr;
constexpr double kProfessionValue = 200;
constexpr double kVigorScoreMultiplier = 5.0;
constexpr double kCastleMultiplierBonus = 1.0;
@@ -276,7 +280,7 @@ auto GetRangedAttackBonus(
const int meteorRange,
const double minVigorToCast,
const ActionPoints archeryActionPointCost) -> double {
double bestRangedAttackValue = 0.0;
vector<double> rangedAttackValues{};
const auto &enemyUnits = isAttacker ? defenderUnits : attackerUnits;
const auto &friendlyUnits = isAttacker ? attackerUnits : defenderUnits;
@@ -292,7 +296,7 @@ auto GetRangedAttackBonus(
net::eagle0::shardok::storage::fb::MultiroundMagicState_NONE)) {
if (unit->attached_hero().profession_info().meteor_cast_state() ==
net::eagle0::shardok::storage::fb::MultiroundMagicState_NONE) {
bestRangedAttackValue = std::max(bestRangedAttackValue, lightningValue(unit));
rangedAttackValues.push_back(lightningValue(unit));
} else {
const double mv = meteorValue(
unit,
@@ -302,14 +306,14 @@ auto GetRangedAttackBonus(
friendlyUnits,
meteorRange,
minVigorToCast);
bestRangedAttackValue = std::max(bestRangedAttackValue, mv);
rangedAttackValues.push_back(mv);
}
}
if (attackLocations.EngineerLocations().Contains(unitLocation) && unit->has_attached_hero() &&
unit->attached_hero().profession_info().profession() ==
net::eagle0::shardok::storage::fb::Profession_ENGINEER) {
bestRangedAttackValue = std::max(bestRangedAttackValue, reduceValue(unit, terrain));
rangedAttackValues.push_back(reduceValue(unit, terrain));
}
if (attackLocations.NecromancerLocations().Contains(unitLocation) &&
@@ -317,24 +321,23 @@ auto GetRangedAttackBonus(
unit->attached_hero().profession_info().profession() ==
net::eagle0::shardok::storage::fb::Profession_NECROMANCER &&
unit->attached_hero().control_info().controlled_unit_id() != -1) {
bestRangedAttackValue = std::max(bestRangedAttackValue, fearValue(enemyUnits));
rangedAttackValues.push_back(fearValue(enemyUnits));
}
if (attackLocations.ArcheryLocations().Contains(unitLocation) &&
unit->volleys_remaining() > 0) {
bestRangedAttackValue =
std::max(bestRangedAttackValue, archeryValue(enemyUnits, canShootNow));
rangedAttackValues.push_back(archeryValue(enemyUnits, canShootNow));
}
// Longbowmen in castle can archer adjacent
if (attackLocations.AdjacentLocations().Contains(unitLocation) &&
unit->volleys_remaining() > 0 && terrain->modifier().castle().present() &&
unit->battalion().type() == net::eagle0::shardok::storage::fb::BattalionTypeId_LONGBOWMEN) {
bestRangedAttackValue =
std::max(bestRangedAttackValue, archeryValue(enemyUnits, canShootNow));
rangedAttackValues.push_back(archeryValue(enemyUnits, canShootNow));
}
return bestRangedAttackValue;
if (rangedAttackValues.empty()) return 0.0;
return *std::max_element(begin(rangedAttackValues), end(rangedAttackValues));
}
auto UnitValue(
@@ -18,7 +18,7 @@ TranspositionTable::TranspositionTable() : table(TABLE_SIZE) {
clear();
}
auto TranspositionTable::hashGameState(const GameStateW& state) const -> uint64_t {
uint64_t TranspositionTable::hashGameState(const GameStateW& state) const {
// The FlatBuffer is contiguous in memory and units are sorted by ID,
// so we can just hash the raw bytes for order-independent hashing
// Use ComputeFNV1aHash to avoid creating a string copy
@@ -28,8 +28,8 @@ public:
std::atomic<uint64_t> stores{0};
std::atomic<uint64_t> collisions{0};
[[nodiscard]] auto hitRate() const -> double {
const uint64_t p = probes.load();
double hitRate() const {
uint64_t p = probes.load();
return p > 0 ? (100.0 * hits.load() / p) : 0.0;
}
@@ -60,7 +60,7 @@ private:
std::atomic<uint16_t> current_age{0};
// Hash function for FlatBuffer game state
[[nodiscard]] auto hashGameState(const GameStateW& state) const -> uint64_t;
uint64_t hashGameState(const GameStateW& state) const;
public:
TranspositionTable();
@@ -78,7 +78,7 @@ public:
void incrementAge() { current_age++; }
// Get statistics
[[nodiscard]] auto getStats() const -> const Stats& { return stats; }
const Stats& getStats() const { return stats; }
// Print statistics to stdout
void printStats() const;
@@ -37,23 +37,6 @@ auto LegalActionsCacheKey(const uint64_t stateHash, const bool rootIsDefender) -
return rootIsDefender ? stateHash ^ kDefenderRoleHash : stateHash;
}
auto SortActionsByWeight(
std::vector<std::unique_ptr<MCTSAction>> actions,
const std::vector<double>& weights) -> std::vector<std::unique_ptr<MCTSAction>> {
std::vector<size_t> sortedIndices(actions.size());
std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
std::sort(sortedIndices.begin(), sortedIndices.end(), [&weights](size_t a, size_t b) {
return weights[a] > weights[b];
});
std::vector<std::unique_ptr<MCTSAction>> sortedActions;
sortedActions.reserve(actions.size());
for (size_t idx : sortedIndices) { sortedActions.push_back(std::move(actions[idx])); }
return sortedActions;
}
} // namespace
// Shared cache for legal actions (uses lock-free parallel hash map for thread safety)
@@ -299,7 +282,19 @@ std::vector<std::unique_ptr<MCTSAction>> ShardokGameEngine::getLegalActions(
// Sort actions by weight (descending) to ensure MCTS explores high-value actions first
const std::vector<double> weights = getActionWeights(actions, state);
return SortActionsByWeight(std::move(actions), weights);
std::vector<size_t> sortedIndices(actions.size());
std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
std::sort(sortedIndices.begin(), sortedIndices.end(), [&weights](size_t a, size_t b) {
return weights[a] > weights[b];
});
std::vector<std::unique_ptr<MCTSAction>> sortedActions;
sortedActions.reserve(actions.size());
for (size_t idx : sortedIndices) { sortedActions.push_back(std::move(actions[idx])); }
return sortedActions;
}
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
@@ -360,7 +355,20 @@ std::vector<std::unique_ptr<MCTSAction>> ShardokGameEngine::getLegalActions(
// get explored deeply. Original indices are preserved in ShardokAction::getIndex()
const std::vector<double> weights = getActionWeights(actions, state);
actions = SortActionsByWeight(std::move(actions), weights);
// Create index vector for sorting
std::vector<size_t> sortedIndices(actions.size());
std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
// Sort indices by weight (descending)
std::sort(sortedIndices.begin(), sortedIndices.end(), [&weights](size_t a, size_t b) {
return weights[a] > weights[b];
});
// Reorder actions according to sorted indices
std::vector<std::unique_ptr<MCTSAction>> sortedActions;
sortedActions.reserve(actions.size());
for (size_t idx : sortedIndices) { sortedActions.push_back(std::move(actions[idx])); }
actions = std::move(sortedActions);
const auto actionsEnd = std::chrono::high_resolution_clock::now();
timeInLegalActionsComputation_.fetch_add(
@@ -160,8 +160,6 @@ auto AttackerHoldsCriticalTilesVictoryScore(
ActionPoints braveWaterCost) -> ScoreValue {
vector<const Unit*> playerUnits{};
vector<const Unit*> claimablePlayerUnits{};
playerUnits.reserve(gameState->units()->size());
claimablePlayerUnits.reserve(gameState->units()->size());
for (const Unit* unit : *gameState->units()) {
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT &&
unit->player_id() == player->player_id()) {
@@ -262,8 +260,6 @@ auto LastPlayerStandingVictoryScore(
vector<const Unit*> playerUnits{};
vector<const Unit*> otherUnits{};
playerUnits.reserve(gameState->units()->size());
otherUnits.reserve(gameState->units()->size());
for (const Unit* unit : *gameState->units()) {
if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;
@@ -144,10 +144,6 @@ private:
public:
AttackerScoreTimer() : startTime(std::chrono::high_resolution_clock::now()) {}
AttackerScoreTimer(const AttackerScoreTimer &) = delete;
auto operator=(const AttackerScoreTimer &) -> AttackerScoreTimer & = delete;
AttackerScoreTimer(AttackerScoreTimer &&) = delete;
auto operator=(AttackerScoreTimer &&) -> AttackerScoreTimer & = delete;
~AttackerScoreTimer() {
auto endTime = std::chrono::high_resolution_clock::now();
@@ -47,19 +47,19 @@ auto AbstractAIScoreCalculator::CalculateUnitsScoreComponents(
// Memoization cache for EffectiveDistance calls
EffectiveDistanceCache distanceCache;
// Early return for empty game states
const size_t estimatedUnitCount = cachedUnits->size();
if (estimatedUnitCount == 0) { return UnitsScoreComponents{0.0, 0.0}; }
std::vector<const Unit *> attackerUnits{};
std::vector<const Unit *> defenderUnits{};
// Pre-allocate vectors based on estimated unit ratios to avoid reallocations
attackerUnits.reserve(estimatedUnitCount);
defenderUnits.reserve(estimatedUnitCount);
const size_t estimatedUnitCount = cachedUnits->size();
attackerUnits.reserve(estimatedUnitCount - 1);
defenderUnits.reserve(estimatedUnitCount - 1);
double attackerUnitsValue = 0;
double defenderUnitsValue = 0;
// Early return for empty game states
if (cachedUnits->size() == 0) { return UnitsScoreComponents{0.0, 0.0}; }
auto occupants = Occupants(*cachedUnits, cachedRowCount, cachedColumnCount);
for (const Unit *unit : *cachedUnits) {
@@ -8,7 +8,6 @@
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <utility>
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
@@ -333,7 +332,6 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
// Create player info protos
std::vector<net::eagle0::shardok::common::PlayerInfo> playerInfoProtos;
playerInfoProtos.reserve(2);
// Attacker info
net::eagle0::shardok::common::PlayerInfo attackerInfo;
@@ -344,7 +342,7 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
net::eagle0::shardok::common::VICTORY_CONDITION_LAST_PLAYER_STANDING);
attackerInfo.add_victory_conditions(
net::eagle0::shardok::common::VICTORY_CONDITION_HOLDS_CRITICAL_TILES);
playerInfoProtos.push_back(std::move(attackerInfo));
playerInfoProtos.push_back(attackerInfo);
// Defender info
net::eagle0::shardok::common::PlayerInfo defenderInfo;
@@ -355,11 +353,10 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
net::eagle0::shardok::common::VICTORY_CONDITION_LAST_PLAYER_STANDING);
defenderInfo.add_victory_conditions(
net::eagle0::shardok::common::VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS);
playerInfoProtos.push_back(std::move(defenderInfo));
playerInfoProtos.push_back(defenderInfo);
// Create units from config
std::vector<net::eagle0::shardok::storage::fb::Unit> units;
units.reserve(config_.attacker().units_size() + config_.defender().units_size());
// Attacker units
const int attackerUnitCount = config_.attacker().units_size();
@@ -453,7 +450,7 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
unit.mutable_opponent_knowledge()->Mutate(0, 0);
unit.mutable_opponent_knowledge()->Mutate(1, 0);
units.push_back(std::move(unit));
units.push_back(unit);
}
// Defender units
@@ -549,7 +546,7 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
unit.mutable_opponent_knowledge()->Mutate(0, 0);
unit.mutable_opponent_knowledge()->Mutate(1, 0);
units.push_back(std::move(unit));
units.push_back(unit);
}
// Create game state
@@ -917,7 +914,6 @@ BattleResult AiBattleSimulator::CreateResultFromGameState(
// Collect surviving units (for winner, or all units if draw)
const auto* units = state->units();
if (units) {
result.survivingUnits.reserve(units->size());
for (const auto* unit : *units) {
if (!unit) { continue; }
@@ -88,7 +88,8 @@ void GenerateConfigFile(const std::string& outputPath) {
// Output to file
std::ofstream outFile(outputPath);
if (!outFile.is_open()) {
throw std::runtime_error("Failed to open output file: " + outputPath);
std::cerr << "Error: Failed to open output file: " << outputPath << "\n";
std::exit(1);
}
outFile << jsonString << "\n";
std::cout << "Sample config written to: " << outputPath << "\n";
@@ -294,7 +295,8 @@ BattleResult RunBattle(
if (!config) {
restoreOutput();
throw std::runtime_error("Failed to load config file");
std::cerr << "Error: Failed to load config file\n";
std::exit(1);
}
// Create simulator
@@ -326,8 +328,6 @@ int main(int argc, char* argv[]) {
std::vector<std::string> configPaths;
std::vector<std::string> statePaths;
configPaths.reserve(argc - 1);
statePaths.reserve(argc - 1);
std::string outputPath;
std::string tracePath;
std::string runLabel;
@@ -2,6 +2,7 @@
// Converts saved Eagle ShardokBattle/GameState protobuf blobs into AI battle configs.
//
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <optional>
@@ -145,9 +146,9 @@ int main(int argc, char *argv[]) {
} else if (arg.starts_with("--output=")) {
outputPath = arg.substr(9);
} else if (arg.starts_with("--seed=")) {
seed = std::stoi(arg.substr(7));
seed = std::atoi(arg.substr(7).c_str());
} else if (arg.starts_with("--max-rounds=")) {
maxRounds = std::stoi(arg.substr(13));
maxRounds = std::atoi(arg.substr(13).c_str());
} else if (arg.starts_with("--attacker-scoring=")) {
const auto scoring = ParseScoring(arg.substr(19));
if (!scoring.has_value()) { throw std::runtime_error("Unknown attacker scoring"); }
@@ -115,7 +115,6 @@ int main(int argc, char *argv[]) {
std::string resultPath;
std::string stateFbPath;
std::vector<std::string> actionViewPaths;
actionViewPaths.reserve(argc - 1);
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
@@ -146,9 +145,6 @@ int main(int argc, char *argv[]) {
std::vector<int> attackerFactions;
std::vector<int> defenderFactions;
std::vector<std::string> playerSides;
attackerFactions.reserve(battle.players_size());
defenderFactions.reserve(battle.players_size());
playerSides.reserve(battle.players_size());
bool aiVsAi = true;
for (const auto &player : battle.players()) {
if (player.eagle_fid() == 3 || player.eagle_fid() == 4) { aiVsAi = false; }
@@ -4,10 +4,9 @@
#include "AIPerformanceRunner.hpp"
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <optional>
#include <stdexcept>
#include <string>
#include "PerformanceTestGameStateBuilder.hpp"
@@ -48,7 +47,7 @@ auto CompletionReasonToString(EvaluationCompletionReason reason) -> std::string
/**
* Parse command line arguments into a configuration struct.
*/
auto ParseCommandLineArgs(int argc, char* argv[]) -> std::optional<PerformanceTestConfig> {
auto ParseCommandLineArgs(int argc, char* argv[]) -> PerformanceTestConfig {
PerformanceTestConfig config;
for (int i = 1; i < argc; ++i) {
@@ -64,7 +63,7 @@ auto ParseCommandLineArgs(int argc, char* argv[]) -> std::optional<PerformanceTe
<< " --defender=BOOL AI is defender (default: false)\n"
<< " --verbose Enable verbose output\n"
<< " --help, -h Show this help message\n";
return std::nullopt;
std::exit(0);
} else if (arg.starts_with("--map=")) {
config.mapName = arg.substr(6);
} else if (arg.starts_with("--turns=")) {
@@ -75,8 +74,9 @@ auto ParseCommandLineArgs(int argc, char* argv[]) -> std::optional<PerformanceTe
} else if (arg == "--verbose") {
config.verbose = true;
} else {
throw std::invalid_argument(
"Unknown argument: " + arg + "\nUse --help for usage information.");
std::cerr << "Unknown argument: " << arg << "\n";
std::cerr << "Use --help for usage information.\n";
std::exit(1);
}
}
@@ -96,9 +96,7 @@ int main(int argc, char* argv[]) {
std::cout << "==============================\n";
// Parse command line arguments
const auto parsedConfig = ParseCommandLineArgs(argc, argv);
if (!parsedConfig.has_value()) { return 0; }
const auto& config = parsedConfig.value();
auto config = ParseCommandLineArgs(argc, argv);
if (config.verbose) {
std::cout << "Configuration:\n";
@@ -165,7 +163,6 @@ int main(int argc, char* argv[]) {
std::cout << "Running AI performance test for " << config.numTurns << " turns...\n";
std::vector<AIPerformanceMetrics> metrics;
if (config.numTurns > 0) { metrics.reserve(config.numTurns); }
for (int turn = 0; turn < config.numTurns; ++turn) {
// Check if AI can make a move
@@ -4,9 +4,7 @@
#include "PerformanceTestGameStateBuilder.hpp"
#include <algorithm>
#include <filesystem>
#include <utility>
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai_testing_common/GameSettingsFactory.hpp"
@@ -55,7 +53,6 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
// Create player info protos
std::vector<net::eagle0::shardok::common::PlayerInfo> playerInfoProtos;
playerInfoProtos.reserve(2);
// AI player
net::eagle0::shardok::common::PlayerInfo aiPlayerInfo;
@@ -71,7 +68,7 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
aiPlayerInfo.add_victory_conditions(
net::eagle0::shardok::common::VICTORY_CONDITION_HOLDS_CRITICAL_TILES);
}
playerInfoProtos.push_back(std::move(aiPlayerInfo));
playerInfoProtos.push_back(aiPlayerInfo);
// Human player
net::eagle0::shardok::common::PlayerInfo humanPlayerInfo;
@@ -87,16 +84,13 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
humanPlayerInfo.add_victory_conditions(
net::eagle0::shardok::common::VICTORY_CONDITION_HOLDS_CRITICAL_TILES);
}
playerInfoProtos.push_back(std::move(humanPlayerInfo));
playerInfoProtos.push_back(humanPlayerInfo);
// Create units
const int clampedAiUnitCount = std::clamp(aiUnitCount, 0, 6);
const int clampedHumanUnitCount = std::clamp(humanUnitCount, 0, 6);
std::vector<net::eagle0::shardok::storage::fb::Unit> units;
units.reserve(clampedAiUnitCount + clampedHumanUnitCount);
// Create AI units in reserve (location -1, -1)
for (int i = 0; i < clampedAiUnitCount; ++i) {
for (int i = 0; i < aiUnitCount && i < 6; ++i) {
units.push_back(AddGenericUnit(
AI_PLAYER_ID,
i, // Unit ID
@@ -107,10 +101,10 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
}
// Create human units in reserve (location -1, -1)
for (int i = 0; i < clampedHumanUnitCount; ++i) {
for (int i = 0; i < humanUnitCount && i < 6; ++i) {
units.push_back(AddGenericUnit(
HUMAN_PLAYER_ID,
clampedAiUnitCount + i, // Unit ID starting after generated AI units
aiUnitCount + i, // Unit ID starting aiUnitCount
net::eagle0::shardok::storage::fb::Coords(-1, -1), // Reserve location
NO_PROFESSION,
HEAVY_INFANTRY_BATTALION_TYPE,
@@ -241,4 +235,4 @@ auto PerformanceTestGameStateBuilder::AddGenericUnit(
return unit;
}
} // namespace shardok
} // namespace shardok
@@ -24,6 +24,11 @@
namespace shardok {
using net::eagle0::common::ScopedShardokLatencyTrace;
using std::condition_variable;
using std::mutex;
using std::scoped_lock;
using std::unique_lock;
using std::weak_ptr;
using net::eagle0::shardok::common::GameStatus;
@@ -61,9 +66,9 @@ public:
[[nodiscard]] auto what() const noexcept -> const char * override { return message.c_str(); }
};
std::vector<std::shared_ptr<ShardokAIClient>> ShardokGameController::MakeAIClients(
const std::unique_ptr<ShardokEngine> &e) {
std::vector<std::shared_ptr<ShardokAIClient>> aic;
vector<shared_ptr<ShardokAIClient>> ShardokGameController::MakeAIClients(
const unique_ptr<ShardokEngine> &e) {
vector<shared_ptr<ShardokAIClient>> aic;
mcts::MCTSConfig mctsConfig;
mctsConfig.backpropagationPolicy = mcts::MCTSBackpropagationPolicy::MINIMAX;
@@ -73,7 +78,6 @@ std::vector<std::shared_ptr<ShardokAIClient>> ShardokGameController::MakeAIClien
// Check if all players are AI - if so, use faster time budgets
const auto &playerInfos = e->GetPlayerInfos();
aic.reserve(playerInfos.size());
const bool isAllAiBattle =
std::ranges::all_of(playerInfos, [](const auto &pi) { return pi.is_ai(); });
@@ -103,7 +107,7 @@ std::vector<std::shared_ptr<ShardokAIClient>> ShardokGameController::MakeAIClien
void ShardokGameController::LockedNotifyClients() const { updateCondition.notify_all(); }
auto ShardokGameController::LockedAIClientForPid(PlayerId pid) const
-> std::shared_ptr<ShardokAIClient> {
-> shared_ptr<ShardokAIClient> {
const auto it = std::ranges::find_if(aiClients, [pid](const auto &client) {
return client->GetPlayerId() == pid;
});
@@ -116,7 +120,7 @@ void ShardokGameController::DoAIThread() {
while (aiThreadKeepGoing) {
// Phase 1: Gather data for AI decision (brief lock)
std::shared_ptr<ShardokAIClient> aiClient;
shared_ptr<ShardokAIClient> aiClient;
PlayerId playerId;
GameSettingsSPtr settings;
net::eagle0::shardok::api::GameStateView gsv;
@@ -124,7 +128,7 @@ void ShardokGameController::DoAIThread() {
size_t expectedHistoryCount;
{
std::unique_lock lk(masterLock);
unique_lock lk(masterLock);
if (engine->GameIsOver()) {
aiThreadKeepGoing = false;
@@ -168,7 +172,7 @@ void ShardokGameController::DoAIThread() {
// Phase 3: Post the command (brief lock)
{
std::unique_lock lk(masterLock);
unique_lock lk(masterLock);
// Verify state hasn't changed while we were thinking
if (engine->GetUnfilteredHistoryCount() != expectedHistoryCount) {
@@ -224,7 +228,7 @@ void ShardokGameController::DoAIThread() {
ShardokGameController::~ShardokGameController() {
{
std::unique_lock lk(masterLock);
unique_lock lk(masterLock);
aiThreadKeepGoing = false;
aiCondition.notify_one();
}
@@ -232,7 +236,7 @@ ShardokGameController::~ShardokGameController() {
}
void CheckFactionId(
const std::unique_ptr<ShardokEngine> &engine,
const unique_ptr<ShardokEngine> &engine,
const PlayerId shardokPlayerId,
const int eagleFactionId) {
bool found = false;
@@ -252,7 +256,7 @@ void ShardokGameController::PostCommand(
const int eagleFactionId,
const int64_t token,
const int64_t commandIndex,
const std::optional<int32_t> roll) {
const optional<int32_t> roll) {
ScopedShardokLatencyTrace totalTrace(
"controller_post_command_total",
cachedGameId,
@@ -260,7 +264,7 @@ void ShardokGameController::PostCommand(
{"eagle_faction_id", std::to_string(eagleFactionId)},
{"token", std::to_string(token)},
{"command_index", std::to_string(commandIndex)}});
std::scoped_lock<std::mutex> guard(masterLock);
scoped_lock<mutex> guard(masterLock);
CheckFactionId(engine, shardokPlayerId, eagleFactionId);
@@ -299,7 +303,7 @@ void ShardokGameController::PostPlacementCommands(
const PlayerId shardokPlayerId,
const int eagleFactionId,
const int64_t token,
const std::vector<UnitPlacementInfo> &infos) {
const vector<UnitPlacementInfo> &infos) {
ScopedShardokLatencyTrace totalTrace(
"controller_post_placement_commands_total",
cachedGameId,
@@ -308,7 +312,7 @@ void ShardokGameController::PostPlacementCommands(
{"token", std::to_string(token)},
{"placement_count", std::to_string(infos.size())}});
{
std::scoped_lock<std::mutex> guard(masterLock);
scoped_lock<mutex> guard(masterLock);
CheckFactionId(engine, shardokPlayerId, eagleFactionId);
@@ -352,7 +356,7 @@ void ShardokGameController::PostPlacementCommands(
}
auto ShardokGameController::GetCurrentGameStateBytes() -> byte_vector {
std::scoped_lock<std::mutex> guard(masterLock);
scoped_lock<mutex> guard(masterLock);
return engine->GetCurrentGameStateBytes();
}
@@ -364,9 +368,9 @@ auto ShardokGameController::GetUpdates(const int64_t startingActionId) -> AllUpd
AllUpdates updates{};
incomingRegistrations++;
std::vector<net::eagle0::shardok::storage::ShardokActionWithResultingState> awrs;
vector<net::eagle0::shardok::storage::ShardokActionWithResultingState> awrs;
{
std::unique_lock<std::mutex> guard(masterLock);
unique_lock<mutex> guard(masterLock);
updates.newUnfilteredCount = engine->GetUnfilteredHistoryCount();
@@ -396,7 +400,7 @@ auto ShardokGameController::GetUpdates(const int64_t startingActionId) -> AllUpd
auto fid = pi.eagle_faction_id();
std::vector<net::eagle0::shardok::api::ActionResultView> actionResultViews;
vector<net::eagle0::shardok::api::ActionResultView> actionResultViews;
{
ScopedShardokLatencyTrace trace(
"engine_filter_new_results",
@@ -407,7 +411,7 @@ auto ShardokGameController::GetUpdates(const int64_t startingActionId) -> AllUpd
actionResultViews = engine->FilterNewResults(pi.player_id(), startingActionId);
}
std::shared_ptr<AvailableCommands> acs = nullptr;
shared_ptr<AvailableCommands> acs = nullptr;
const auto availableCommands = [&] {
ScopedShardokLatencyTrace trace(
@@ -426,11 +430,9 @@ auto ShardokGameController::GetUpdates(const int64_t startingActionId) -> AllUpd
if (!availableCommands.empty() || !previewCommands.empty()) {
acs = std::make_shared<AvailableCommands>();
acs->mutable_current_command()->Reserve(availableCommands.size());
*acs->mutable_current_command() = {
std::begin(availableCommands),
std::end(availableCommands)};
acs->mutable_preview_command()->Reserve(previewCommands.size());
*acs->mutable_preview_command() = {
std::begin(previewCommands),
std::end(previewCommands)};
@@ -481,7 +483,6 @@ auto ShardokGameController::GetUpdates(const int64_t startingActionId) -> AllUpd
// Extract battle progress data from the FlatBuffer game state
const auto *gs = engine->GetCurrentGameState().Get();
updates.currentRound = gs->current_round();
updates.playerTroopCounts.reserve(gs->player_infos()->size());
for (const auto *player : *gs->player_infos()) {
int32_t troopCount = 0;
for (const auto *unit : *gs->units()) {
@@ -503,37 +504,36 @@ auto ShardokGameController::GetUpdates(const int64_t startingActionId) -> AllUpd
}
auto ShardokGameController::GameIsOver() -> bool {
std::scoped_lock<std::mutex> guard(masterLock);
scoped_lock<mutex> guard(masterLock);
const auto &gameStatus = engine->GetGameStatus();
return ShardokEngine::GameIsOver(gameStatus);
}
auto ShardokGameController::GameOverStatus() -> net::eagle0::shardok::common::GameStatus {
std::scoped_lock<std::mutex> guard(masterLock);
scoped_lock<mutex> guard(masterLock);
return fb::ToProto(engine->GetGameStatus());
}
auto ShardokGameController::EndGameUnits()
-> std::vector<net::eagle0::shardok::storage::ResolvedUnit> {
std::scoped_lock<std::mutex> guard(masterLock);
auto ShardokGameController::EndGameUnits() -> vector<net::eagle0::shardok::storage::ResolvedUnit> {
scoped_lock<mutex> guard(masterLock);
return engine->EndGameUnits();
}
auto ShardokGameController::ResolvedPlayerInfos()
-> std::vector<net::eagle0::shardok::common::PlayerInfo> {
std::scoped_lock<std::mutex> guard(masterLock);
-> vector<net::eagle0::shardok::common::PlayerInfo> {
scoped_lock<mutex> guard(masterLock);
return engine->GetPlayerInfos();
}
void ShardokGameController::RegisterSubscriber(std::shared_ptr<StreamSubscriber> subscriber) {
std::scoped_lock<std::mutex> guard(subscriberLock);
scoped_lock<mutex> guard(subscriberLock);
subscribers.push_back(subscriber);
}
void ShardokGameController::UnregisterSubscriber(const StreamSubscriber *subscriber) {
std::scoped_lock<std::mutex> guard(subscriberLock);
scoped_lock<mutex> guard(subscriberLock);
subscribers.erase(
std::remove_if(
subscribers.begin(),
@@ -556,7 +556,7 @@ auto ShardokGameController::WaitForUpdatesAndPush(
GameOverInfo gameOverInfo{};
{
std::unique_lock<std::mutex> guard(masterLock);
unique_lock<mutex> guard(masterLock);
// Wait for updates, game over, or AI thread failure
updateCondition.wait(guard, [this, lastPushedActionId] {
@@ -10,14 +10,9 @@
#define EAGLE0_SHARDOK_CONTROLLER_SHARDOK_GAME_CONTROLLER_HPP
#include <atomic>
#include <condition_variable>
#include <ctime>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <utility>
#include <vector>
@@ -31,27 +26,31 @@
namespace shardok {
using net::eagle0::shardok::api::ActionResultView;
using net::eagle0::shardok::api::AvailableCommands;
using std::optional;
using std::shared_ptr;
using std::unique_ptr;
using std::weak_ptr;
// Forward declaration
class ShardokGameController;
/// Info about a game that has ended, for notifying subscribers
struct GameOverInfo {
std::vector<net::eagle0::shardok::common::PlayerInfo> playerInfos;
std::vector<net::eagle0::shardok::storage::ResolvedUnit> endGameUnits;
vector<net::eagle0::shardok::common::PlayerInfo> playerInfos;
vector<net::eagle0::shardok::storage::ResolvedUnit> endGameUnits;
net::eagle0::shardok::common::GameStatus gameStatus;
};
/// Updates for a single player (includes faction ID for client routing)
struct OnePlayerUpdates {
int32_t eagleFactionId;
std::vector<ActionResultView> resultViews;
std::shared_ptr<AvailableCommands> availableCommands;
vector<ActionResultView> resultViews;
shared_ptr<AvailableCommands> availableCommands;
OnePlayerUpdates(
const int32_t fid,
const std::vector<ActionResultView>& arvs,
const std::shared_ptr<AvailableCommands>& acs)
const vector<ActionResultView>& arvs,
const shared_ptr<AvailableCommands>& acs)
: eagleFactionId(fid),
resultViews(arvs),
availableCommands(acs) {}
@@ -71,12 +70,12 @@ public:
/// Called when new game updates are available
virtual void OnUpdate(
const std::vector<ActionResult>& mainResults,
const std::vector<OnePlayerUpdates>& filteredResults,
const vector<ActionResult>& mainResults,
const vector<OnePlayerUpdates>& filteredResults,
int32_t newUnfilteredCount,
const byte_vector& currentGameState,
int32_t currentRound,
const std::vector<std::pair<int32_t, int32_t>>& playerTroopCounts) = 0;
const vector<std::pair<int32_t, int32_t>>& playerTroopCounts) = 0;
/// Called when the game ends
virtual void OnGameOver(const GameOverInfo& info) = 0;
@@ -102,52 +101,51 @@ private:
mutable std::mutex subscriberLock{};
std::vector<std::weak_ptr<StreamSubscriber>> subscribers{};
std::string serializedRequest;
string serializedRequest;
std::unique_ptr<ShardokEngine> engine;
unique_ptr<ShardokEngine> engine;
// Cached immutable data - safe to access without lock since it never changes after construction
const GameId cachedGameId;
std::atomic_int incomingRegistrations = 0;
const std::string mapName;
const std::string logFilePath;
const string mapName;
const string logFilePath;
// AI thread error state - written once by AI thread before setting atomic flag
std::atomic<bool> aiThreadFailed{false};
std::string aiThreadErrorMessage;
std::vector<std::shared_ptr<ShardokAIClient>> aiClients;
vector<shared_ptr<ShardokAIClient>> aiClients;
std::thread aiThread;
// Each entry maps an Eagle faction id of a non-participant watcher to the
// Shardok pids of participants whose allies list named this watcher. Used
// when generating per-watcher filtered views in GetUpdates.
const std::vector<std::pair<int32_t, std::vector<PlayerId>>> watcherAllies;
const vector<std::pair<int32_t, vector<PlayerId>>> watcherAllies;
static auto MakeLogFilePath() -> std::string {
const std::time_t timer = std::time(nullptr);
static auto MakeLogFilePath() -> string {
const time_t timer = time(nullptr);
char buf[255];
std::strftime(buf, 255, "/tmp/playthrough_%Y_%m_%d_%H%M%S.log", std::localtime(&timer));
return std::string(buf);
strftime(buf, 255, "/tmp/playthrough_%Y_%m_%d_%H%M%S.log", localtime(&timer));
return string(buf);
}
void LockedNotifyClients() const;
auto LockedAIClientForPid(PlayerId pid) const -> std::shared_ptr<ShardokAIClient>;
auto LockedAIClientForPid(PlayerId pid) const -> shared_ptr<ShardokAIClient>;
static std::vector<std::shared_ptr<ShardokAIClient>> MakeAIClients(
const std::unique_ptr<ShardokEngine>& e);
static vector<shared_ptr<ShardokAIClient>> MakeAIClients(const unique_ptr<ShardokEngine>& e);
void DoAIThread();
public:
ShardokGameController(
std::unique_ptr<ShardokEngine> e,
std::string mapName,
std::string serializedRequest = "",
std::vector<std::pair<int32_t, std::vector<PlayerId>>> watcherAllies = {})
unique_ptr<ShardokEngine> e,
string mapName,
string serializedRequest = "",
vector<std::pair<int32_t, vector<PlayerId>>> watcherAllies = {})
: serializedRequest(std::move(serializedRequest)),
engine(std::move(e)),
cachedGameId(engine->GetGameId()),
@@ -162,40 +160,40 @@ public:
~ShardokGameController();
auto SerializedRequest() const -> std::string { return serializedRequest; }
auto SerializedRequest() const -> string { return serializedRequest; }
void PostCommand(
PlayerId shardokPlayerId,
int eagleFactionId,
int64_t token,
int64_t commandIndex,
std::optional<int32_t> roll);
optional<int32_t> roll);
void PostPlacementCommands(
PlayerId shardokPlayerId,
int eagleFactionId,
int64_t token,
const std::vector<UnitPlacementInfo>& infos);
const vector<UnitPlacementInfo>& infos);
struct AllUpdates {
std::vector<ActionResult> mainResults;
std::vector<OnePlayerUpdates> filteredResults;
vector<ActionResult> mainResults;
vector<OnePlayerUpdates> filteredResults;
int32_t newUnfilteredCount;
byte_vector currentGameState;
int32_t currentRound = 0;
std::vector<std::pair<int32_t, int32_t>> playerTroopCounts; // (playerId, troopCount)
vector<std::pair<int32_t, int32_t>> playerTroopCounts; // (playerId, troopCount)
};
auto GetUpdates(int64_t startingActionId) -> AllUpdates;
auto GetCurrentGameStateBytes() -> byte_vector;
auto GameIsOver() -> bool;
auto GameOverStatus() -> net::eagle0::shardok::common::GameStatus;
auto ResolvedPlayerInfos() -> std::vector<net::eagle0::shardok::common::PlayerInfo>;
auto EndGameUnits() -> std::vector<net::eagle0::shardok::storage::ResolvedUnit>;
auto ResolvedPlayerInfos() -> vector<net::eagle0::shardok::common::PlayerInfo>;
auto EndGameUnits() -> vector<net::eagle0::shardok::storage::ResolvedUnit>;
[[nodiscard]] auto GetGameId() const -> GameId { return cachedGameId; }
[[nodiscard]] auto GetLogFilePath() const -> std::string { return logFilePath; }
[[nodiscard]] auto GetLogFilePath() const -> string { return logFilePath; }
[[nodiscard]] auto HasAIThreadError() const -> bool { return aiThreadFailed.load(); }
[[nodiscard]] auto GetAIThreadError() const -> std::string { return aiThreadErrorMessage; }
@@ -9,7 +9,6 @@
#include "AvailableCommandsFactory.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <ranges>
@@ -33,22 +32,22 @@ private:
if (pid == UNCONTROLLED_PLAYER_ID) return false;
const auto &player = std::find_if(
std::begin(*gameState->player_infos()),
std::end(*gameState->player_infos()),
begin(*gameState->player_infos()),
end(*gameState->player_infos()),
[pid](const PlayerInfoFb *pi) { return pi->player_id() == pid; });
return (player != std::end(*gameState->player_infos()) && player->cannot_become_outlaw());
return (player != end(*gameState->player_infos()) && player->cannot_become_outlaw());
}
static auto IsAttacker(const GameStateW &gameState, PlayerId pid) -> bool {
if (pid == UNCONTROLLED_PLAYER_ID) return false;
const auto &player = std::find_if(
std::begin(*gameState->player_infos()),
std::end(*gameState->player_infos()),
begin(*gameState->player_infos()),
end(*gameState->player_infos()),
[pid](const PlayerInfoFb *pi) { return pi->player_id() == pid; });
return (player == std::end(*gameState->player_infos()) || !player->is_defender());
return (player == end(*gameState->player_infos()) || !player->is_defender());
}
const std::vector<std::shared_ptr<const CommandFactory>> commandFactories;
@@ -117,7 +116,6 @@ void AvailableCommandsFactoryImpl::AddAvailableCommandsForOneUnit(
const bool isEligibleCharger = unit->unit_id() == gameState->eligible_charger_id();
std::vector<UnitId> possibleChargees{};
possibleChargees.reserve(gameState->possible_chargee_ids()->size());
for (const UnitId cid : *gameState->possible_chargee_ids()) {
if (cid != -1) { possibleChargees.push_back(cid); }
}
@@ -196,6 +194,8 @@ auto AvailableCommandsFactoryImpl::GetAvailableCommands(
const std::optional<std::vector<Coords>> &reinforcementPositions) const -> CommandListSPtr {
CommandList commands{};
std::vector<AdjacentTile> adjacentTiles;
playerSetupCommandFactory
.AddAvailablePlayerSetupCommands(commands, playerId, gameState, reinforcementPositions);
if (!commands.empty()) return std::make_shared<CommandList>(commands);
@@ -238,7 +238,6 @@ auto AvailableCommandsFactoryImpl::GetAvailableCommands(
/* onlyFollowUps=*/true);
std::unordered_set<CommandType> followUpTypes{};
followUpTypes.reserve(followUpCommands.size());
for (const auto &followUpCmd : followUpCommands) {
if (followUpCmd->CanBeFollowUp()) {
followUpTypes.insert(followUpCmd->GetCommandType());
@@ -261,8 +261,13 @@ struct BattalionType {
trainingDifferenceMultiplierForStunChanceOnCharge * trainingDifference;
OtherFactor trainingFactor =
MakeOtherFactor(static_cast<int16_t>(trainingValue), "training difference");
const std::array otherFactors = {trainingFactor};
return MakeOdds(static_cast<int16_t>(baseStunChanceOnCharge), 0, 0, 0, {}, otherFactors);
return MakeOdds(
static_cast<int16_t>(baseStunChanceOnCharge),
0,
0,
0,
{},
{trainingFactor});
}
[[nodiscard]] auto GetDailyFoodCostPerTroop() const -> double {
@@ -9,7 +9,6 @@
#include "FireUtils.hpp"
#include <algorithm>
#include <array>
#include "src/main/cpp/net/eagle0/shardok/library/map/TileModifier.hpp"
@@ -104,14 +103,14 @@ auto GetExtinguishOdds(
const int16_t windFactor = -weather->wind().speed_in_mph();
const std::vector<double> stats = {agility, agility, intelligence};
std::vector<OtherFactor> others;
if (ownTile) {
const std::array others = {
MakeOtherFactor(settings.Backing().extinguish_own_tile_buf(), "ownTile")};
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, others);
others.push_back(MakeOtherFactor(settings.Backing().extinguish_own_tile_buf(), "ownTile"));
}
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats);
const std::vector<double> stats = {agility, agility, intelligence};
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, others);
}
auto GetStartFireOdds(
@@ -129,13 +128,12 @@ auto GetStartFireOdds(
const int16_t windFactor = -currentWeather.wind().speed_in_mph();
const std::vector<double> stats = {agility, intelligence, intelligence};
if (isMage) {
const std::array otherFactors = {MakeOtherFactor(+20, "mage")};
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
}
std::vector<OtherFactor> otherFactors;
if (isMage) { otherFactors.push_back(MakeOtherFactor(+20, "mage")); }
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats);
const std::vector<double> stats = {agility, intelligence, intelligence};
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
}
} // namespace shardok
@@ -13,7 +13,7 @@
#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
namespace shardok {
double BonusFromStat(const double stat) { return pow(stat - 50.0, 3.0) / 5000.0; }
double BonusFromStat(const double stat) { return pow(((double)stat - 50.0), 3.0) / 5000.0; }
int16_t AverageBonusFromStats(const std::vector<double> &stats) {
double totalBonus = 0.0;
@@ -23,7 +23,7 @@ int16_t AverageBonusFromStats(const std::vector<double> &stats) {
}
int16_t GetSuccessChance(const PercentileRollOdds &odds) {
return static_cast<int16_t>(std::floor(
return int16_t(std::floor(
100.0 * RandomGenerator::ChanceOpenEndedPercentileAtOrAbove(100 - GetTotalOdds(odds)) +
0.5));
}
@@ -42,7 +42,7 @@ PercentileRollOdds MakeOdds(
const int weath,
const int wind,
const int statF,
const std::span<const OtherFactor> others) {
const std::vector<OtherFactor> &others) {
PercentileRollOdds odds;
odds.set_base_difficulty(base);
@@ -61,7 +61,7 @@ PercentileRollOdds MakeOdds(
const int weath,
const int wind,
const std::vector<double> &stats,
const std::span<const OtherFactor> others) {
const std::vector<OtherFactor> &others) {
return MakeOdds(base, terr, weath, wind, AverageBonusFromStats(stats), others);
}
@@ -9,7 +9,6 @@
#ifndef EAGLE0_SHARDOK_LIBRARY_PERCENTILE_ROLL_ODDS_HPP
#define EAGLE0_SHARDOK_LIBRARY_PERCENTILE_ROLL_ODDS_HPP
#include <span>
#include <string>
#include <vector>
@@ -34,7 +33,7 @@ PercentileRollOdds MakeOdds(
int weath = 0,
int wind = 0,
int statF = 0,
std::span<const OtherFactor> others = {});
const std::vector<OtherFactor> &others = std::vector<OtherFactor>());
PercentileRollOdds MakeOdds(
int base,
@@ -42,7 +41,7 @@ PercentileRollOdds MakeOdds(
int weath,
int wind,
const std::vector<double> &stats,
std::span<const OtherFactor> others = {});
const std::vector<OtherFactor> &others = std::vector<OtherFactor>());
int16_t GetTotalOdds(const PercentileRollOdds &odds);
@@ -77,11 +77,11 @@ ShardokEngine::ShardokEngine(
ShardokEngine::ShardokEngine(
const GameSettingsSPtr &settings,
std::vector<Unit> &&unplacedUnorderedUnits,
vector<Unit> &&unplacedUnorderedUnits,
const HexMapProto &map,
const int month,
const GameId &existingGameId,
const std::vector<PlayerInfoProto> &playerInfos,
const vector<PlayerInfoProto> &playerInfos,
const bool trackHistory)
: gameSettings(settings),
settingsGetter(settings->GetGetter()),
@@ -106,7 +106,7 @@ ShardokEngine::ShardokEngine(
ApplyAndAddActionResult(setupStartAction);
}
void ShardokEngine::ApplyAndAddActionResults(const std::vector<ActionResultProto> &results) {
void ShardokEngine::ApplyAndAddActionResults(const vector<ActionResultProto> &results) {
for (const ActionResultProto &result : results) { ApplyAndAddActionResult(result); }
}
@@ -122,7 +122,7 @@ void ShardokEngine::ApplyAndAddActionResult(const ActionResultProto &result) {
ShardokEngine::ShardokEngine(
const GameSettingsSPtr &settings,
const std::vector<ShardokActionWithResultingState> &history,
const vector<ShardokActionWithResultingState> &history,
const bool trackHistory)
: gameSettings(settings),
settingsGetter(settings->GetGetter()),
@@ -156,11 +156,11 @@ auto ShardokEngine::GetGameStateAtStartOfAction(const ActionId startingActionId)
}
auto ShardokEngine::GetGameHistory(const ActionId lastUpdatedActionId) const
-> std::vector<ShardokActionWithResultingState> {
-> vector<ShardokActionWithResultingState> {
if (lastUpdatedActionId >= static_cast<int64_t>(actionHistory.size()) + startingHistoryCount) {
return std::vector<ShardokActionWithResultingState>();
return vector<ShardokActionWithResultingState>();
}
return std::vector<ShardokActionWithResultingState>(
return vector<ShardokActionWithResultingState>(
std::begin(actionHistory) + lastUpdatedActionId - startingHistoryCount,
std::end(actionHistory));
}
@@ -172,17 +172,14 @@ auto ShardokEngine::GetGameStateView(const PlayerId askingPlayer) const
[[nodiscard]] auto ShardokEngine::FilterNewResults(
PlayerId askingPlayer,
int64_t previousActionCount) const
-> std::vector<net::eagle0::shardok::api::ActionResultView> {
int64_t previousActionCount) const -> vector<net::eagle0::shardok::api::ActionResultView> {
if (previousActionCount < startingHistoryCount) {
throw ShardokInternalErrorException("Unable to fetch history before startingHistoryCount");
}
std::vector<ShardokActionWithResultingState> unfilteredHistory =
GetGameHistory(previousActionCount);
vector<ShardokActionWithResultingState> unfilteredHistory = GetGameHistory(previousActionCount);
std::vector<net::eagle0::shardok::api::ActionResultView> filteredHistory{};
filteredHistory.reserve(unfilteredHistory.size());
vector<net::eagle0::shardok::api::ActionResultView> filteredHistory{};
GameStateW startingState = (previousActionCount == 0)
? GameStateW{}
@@ -196,7 +193,9 @@ auto ShardokEngine::GetGameStateView(const PlayerId askingPlayer) const
GameState *previousStatePtr = nullptr;
GameStateView &previousView = startingView;
for (const ShardokActionWithResultingState &awrs : unfilteredHistory) {
for (vector<ShardokActionWithResultingState> newHistory =
{unfilteredHistory.begin(), unfilteredHistory.end()};
const ShardokActionWithResultingState &awrs : newHistory) {
GameStateView viewAfter = GameStateFilteredForPlayer(
settingsGetter,
GameStateW::FromByteString(awrs.state_after_fb()),
@@ -221,18 +220,16 @@ auto ShardokEngine::GetGameStateView(const PlayerId askingPlayer) const
[[nodiscard]] auto ShardokEngine::FilterNewResultsForWatcher(
const PlayerId askingPlayer,
const std::vector<PlayerId> &alliedPids,
const vector<PlayerId> &alliedPids,
const int64_t previousActionCount) const
-> std::vector<net::eagle0::shardok::api::ActionResultView> {
-> vector<net::eagle0::shardok::api::ActionResultView> {
if (previousActionCount < startingHistoryCount) {
throw ShardokInternalErrorException("Unable to fetch history before startingHistoryCount");
}
std::vector<ShardokActionWithResultingState> unfilteredHistory =
GetGameHistory(previousActionCount);
vector<ShardokActionWithResultingState> unfilteredHistory = GetGameHistory(previousActionCount);
std::vector<net::eagle0::shardok::api::ActionResultView> filteredHistory{};
filteredHistory.reserve(unfilteredHistory.size());
vector<net::eagle0::shardok::api::ActionResultView> filteredHistory{};
GameStateW startingState = (previousActionCount == 0)
? GameStateW{}
@@ -274,7 +271,7 @@ auto ShardokEngine::GetGameStateView(const PlayerId askingPlayer) const
}
auto ShardokEngine::GetFilteredGameHistory(const PlayerId askingPlayer) const
-> std::vector<net::eagle0::shardok::api::ActionResultView> {
-> vector<net::eagle0::shardok::api::ActionResultView> {
return FilterNewResults(askingPlayer, 0);
}
@@ -426,7 +423,7 @@ void ShardokEngine::ResumeAfterReinforcementPlacement(
void ShardokEngine::PostPlacementCommands(
const PlayerId player,
const std::vector<UnitPlacementInfo> &placementInfos,
const vector<UnitPlacementInfo> &placementInfos,
const std::shared_ptr<RandomGenerator> &randomGenerator) {
if (GetCurrentPlayerId() != player) {
throw ShardokClientErrorException("Posting for a player who's not the current player ID!");
@@ -474,8 +471,7 @@ void ShardokEngine::PostPlacementCommands(
return cmd->GetCommandProto().actor().value() == pi.unitId &&
cmd->GetCommandProto().target() == pi.location;
});
for (std::vector<ActionResult> onePlacementResults =
(*it)->Execute(gameState, randomGenerator);
for (vector<ActionResult> onePlacementResults = (*it)->Execute(gameState, randomGenerator);
const ActionResultProto &oneResult : onePlacementResults) {
HandleActionResult(oneResult, randomGenerator);
}
@@ -568,7 +564,7 @@ void ShardokEngine::PostActionUnchecked(
const CommandSPtr &command,
const std::shared_ptr<RandomGenerator> &randomGenerator,
const std::optional<int32_t> roll) {
for (const std::vector<ActionResultProto> results =
for (const vector<ActionResultProto> results =
command->CanUseClientRoll()
? command->ExecuteWithRoll(gameState, randomGenerator, roll)
: command->Execute(gameState, randomGenerator);
@@ -618,7 +614,7 @@ void ShardokEngine::HandleActionResult(
auto ShardokEngine::HandleUnitFallingIntoWater(
const Terrain *terrain,
const Unit *unit,
std::shared_ptr<RandomGenerator> randomGenerator) const -> std::vector<ActionResultProto> {
std::shared_ptr<RandomGenerator> randomGenerator) const -> vector<ActionResultProto> {
// Do nothing if this unit can travel across water
if (const auto &battType = settingsGetter.GetBattalionType(unit->battalion().type());
battType->GetCostToEnterTerrain(terrain).type != ActionCost::impossible) {
@@ -633,9 +629,8 @@ auto ShardokEngine::HandleUnitFallingIntoWater(
return fallAction.Execute(gameState, std::move(randomGenerator));
}
auto CommandProtosFromActionList(const CommandList &actionList) -> std::vector<CommandProto> {
std::vector<CommandProto> protos;
protos.reserve(actionList.size());
auto CommandProtosFromActionList(const CommandList &actionList) -> vector<CommandProto> {
vector<CommandProto> protos;
std::transform(
std::begin(actionList),
@@ -646,7 +641,7 @@ auto CommandProtosFromActionList(const CommandList &actionList) -> std::vector<C
return protos;
}
auto ShardokEngine::GetPreviewCommands(const PlayerId playerId) const -> std::vector<CommandProto> {
auto ShardokEngine::GetPreviewCommands(const PlayerId playerId) const -> vector<CommandProto> {
if (GameIsOver(GetGameStatus())) {
// The game is over
return {};
@@ -675,7 +670,7 @@ auto ShardokEngine::GetAvailableCommandsForAIPlayer(const PlayerId playerId) con
}
auto ShardokEngine::GetAvailableCommandProtos(const PlayerId playerId, const bool includeFollowUps)
const -> std::vector<CommandProto> {
const -> vector<CommandProto> {
if (GameIsOver(GetGameStatus())) {
// The game is over
return {};
@@ -708,8 +703,7 @@ auto ShardokEngine::UncachedGetAvailableCommands(
using SecuredByPlayerByUnitId = std::unordered_map<UnitId, PlayerId>;
auto SecuredByPlayerByUnitIdFromHistory(
const std::vector<ShardokActionWithResultingState> &actionHistory)
-> SecuredByPlayerByUnitId {
const vector<ShardokActionWithResultingState> &actionHistory) -> SecuredByPlayerByUnitId {
SecuredByPlayerByUnitId securedByPlayerByUnitId;
for (const auto &actionWithResultingState : actionHistory) {
for (const auto &resolvedUnit : actionWithResultingState.action_result().resolved_units()) {
@@ -726,7 +720,7 @@ auto SecuredByPlayerByUnitIdFromHistory(
}
void AddUnits(
std::vector<net::eagle0::shardok::storage::ResolvedUnit> &to,
vector<net::eagle0::shardok::storage::ResolvedUnit> &to,
const Units &from,
const SecuredByPlayerByUnitId &securedByPlayerByUnitId) {
for (const auto &unit : from) {
@@ -775,12 +769,11 @@ void AddUnits(
}
AddUnitToResolved(ru, *unit);
to.push_back(std::move(ru));
to.push_back(ru);
}
}
auto ShardokEngine::EndGameUnits() const
-> std::vector<net::eagle0::shardok::storage::ResolvedUnit> {
auto ShardokEngine::EndGameUnits() const -> vector<net::eagle0::shardok::storage::ResolvedUnit> {
if (const auto state = GetGameStatus()->state();
state != net::eagle0::shardok::storage::fb::GameStatus_::State_VICTORY) {
throw ShardokInternalErrorException(
@@ -789,8 +782,7 @@ auto ShardokEngine::EndGameUnits() const
const auto &gs = GetCurrentGameState();
std::vector<net::eagle0::shardok::storage::ResolvedUnit> endgameUnits;
endgameUnits.reserve(gs->units()->size());
vector<net::eagle0::shardok::storage::ResolvedUnit> endgameUnits;
AddUnits(endgameUnits, *gs->units(), SecuredByPlayerByUnitIdFromHistory(actionHistory));
return endgameUnits;
@@ -11,9 +11,7 @@
#include <flatbuffers/flatbuffers.h>
#include <memory>
#include <optional>
#include <vector>
#include "ShardokAction.hpp"
#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
@@ -29,6 +27,12 @@
#include "src/main/protobuf/net/eagle0/shardok/storage/action_with_resulting_state.pb.h"
namespace shardok {
using std::begin;
using std::end;
using std::optional;
using std::shared_ptr;
using std::vector;
using net::eagle0::shardok::api::UnitView;
using PlayerInfoProto = net::eagle0::shardok::common::PlayerInfo;
using net::eagle0::shardok::storage::ShardokActionWithResultingState;
@@ -39,14 +43,14 @@ class ShardokEngine {
private:
const GameSettingsSPtr gameSettings;
const SettingsGetter settingsGetter;
const std::shared_ptr<AvailableCommandsFactory> availableCommandsFactory;
const shared_ptr<AvailableCommandsFactory> availableCommandsFactory;
GameStateW gameState;
const bool trackHistory;
const int32_t startingHistoryCount = 0;
const GameStateW startingHistoryState;
mutable std::vector<ShardokActionWithResultingState> actionHistory;
mutable vector<ShardokActionWithResultingState> actionHistory;
const CoordsSet criticalTileCoords;
@@ -57,12 +61,12 @@ private:
void ApplyAndAddActionResult(const ActionResult &result);
void ApplyAndAddActionResults(const std::vector<ActionResult> &results);
void ApplyAndAddActionResults(const vector<ActionResult> &results);
[[nodiscard]] auto HandleUnitFallingIntoWater(
const Terrain *terrain,
const fb::Unit *unit,
std::shared_ptr<RandomGenerator> randomGenerator) const -> std::vector<ActionResult>;
std::shared_ptr<RandomGenerator> randomGenerator) const -> vector<ActionResult>;
void HandleActionResult(
const ActionResult &actionResult,
@@ -101,17 +105,17 @@ public:
// Constructor for a brand new game
ShardokEngine(
const GameSettingsSPtr &settings,
std::vector<Unit> &&unplacedUnorderedUnits,
vector<Unit> &&unplacedUnorderedUnits,
const HexMapProto &map,
int month,
const GameId &existingGameId,
const std::vector<PlayerInfoProto> &playerInfos,
const vector<PlayerInfoProto> &playerInfos,
bool trackHistory = true);
// Constructor for a game with history
ShardokEngine(
const GameSettingsSPtr &settings,
const std::vector<ShardokActionWithResultingState> &history,
const vector<ShardokActionWithResultingState> &history,
bool trackHistory = true);
// Copy constructor
@@ -130,12 +134,12 @@ public:
}
[[nodiscard]] auto EndGameUnits() const
-> std::vector<net::eagle0::shardok::storage::ResolvedUnit>; // Throws if the game is
// not over
-> vector<net::eagle0::shardok::storage::ResolvedUnit>; // Throws if the game is not
// over
// Controller API
[[nodiscard]] auto GetGameHistory(ActionId lastUpdatedActionId) const
-> std::vector<ShardokActionWithResultingState>;
-> vector<ShardokActionWithResultingState>;
[[nodiscard]] auto GetUnfilteredHistoryCount() const -> size_t {
return actionHistory.size() + startingHistoryCount;
@@ -146,7 +150,7 @@ public:
-> net::eagle0::shardok::api::GameStateView;
[[nodiscard]] auto FilterNewResults(PlayerId askingPlayer, int64_t previousActionCount) const
-> std::vector<net::eagle0::shardok::api::ActionResultView>;
-> vector<net::eagle0::shardok::api::ActionResultView>;
// Variant for non-participant watchers: askingPlayer is a sentinel (not in
// player_infos) and alliedPids names the Shardok pids whose units the
@@ -154,18 +158,18 @@ public:
// and hidden actions.
[[nodiscard]] auto FilterNewResultsForWatcher(
PlayerId askingPlayer,
const std::vector<PlayerId> &alliedPids,
const vector<PlayerId> &alliedPids,
int64_t previousActionCount) const
-> std::vector<net::eagle0::shardok::api::ActionResultView>;
-> vector<net::eagle0::shardok::api::ActionResultView>;
[[nodiscard]] auto GetFilteredGameHistory(PlayerId askingPlayer) const
-> std::vector<net::eagle0::shardok::api::ActionResultView>;
-> vector<net::eagle0::shardok::api::ActionResultView>;
[[nodiscard]] auto GetUnitById(PlayerId askingPlayer, UnitId unitId) const -> UnitView;
void PostPlacementCommands(
PlayerId player,
const std::vector<UnitPlacementInfo> &placementInfos,
const vector<UnitPlacementInfo> &placementInfos,
const std::shared_ptr<RandomGenerator> &randomGenerator = nullptr);
void PostFinishedPlacementCommand(
PlayerId player,
@@ -182,11 +186,11 @@ public:
PlayerId playerId,
std::shared_ptr<RandomGenerator> randomGenerator) -> int;
[[nodiscard]] auto GetPreviewCommands(PlayerId playerId) const -> std::vector<CommandProto>;
[[nodiscard]] auto GetPreviewCommands(PlayerId playerId) const -> vector<CommandProto>;
[[nodiscard]] auto GetAvailableCommandsForAIPlayer(PlayerId playerId) const -> CommandListSPtr;
[[nodiscard]] auto GetAvailableCommandProtos(PlayerId playerId, bool includeFollowUps) const
-> std::vector<CommandProto>;
-> vector<CommandProto>;
[[nodiscard]] auto GetGameId() const -> GameId {
return GetCurrentGameState()->game_id()->str();
@@ -194,9 +198,9 @@ public:
[[nodiscard]] auto GetMonth() const -> int { return GetCurrentGameState()->month(); }
[[nodiscard]] auto GetPlayerInfos() const -> std::vector<PlayerInfoProto> {
[[nodiscard]] auto GetPlayerInfos() const -> vector<PlayerInfoProto> {
const auto &currentGameState = GetCurrentGameState();
std::vector<PlayerInfoProto> protos{};
vector<PlayerInfoProto> protos{};
for (const auto *const piFB : *currentGameState->player_infos()) {
protos.push_back(fb::ToPlayerInfoProto(piFB));
}
@@ -79,8 +79,6 @@ auto PlayerSetupCommandFactory::AddAvailablePlayerSetupCommands(
const auto unplacedUnits = ReserveUnitsForPlayer(gameState->units(), playerId);
if (unplacedUnits.empty()) return;
existingCommands.reserve(
existingCommands.size() + (unplacedUnits.size() * reinforcementPositions->size()));
for (const auto &[unitId, unit] : unplacedUnits) {
for (const Coords &position : *reinforcementPositions) {
if (!gameState.GetOccupant(position)) {
@@ -29,7 +29,6 @@ auto ActionPointDistances::BraveWaterPossibleCoords(const HexMap* hexMap) const
std::shared_ptr<BraveableTileInfo> info = std::make_shared<BraveableTileInfo>(hexMap);
CoordsSet& braveWaterPossibleCoords = info->cs;
const int indexCount = hexMap->row_count() * hexMap->column_count();
info->details.reserve(indexCount);
const auto indexToCoords = CreateIndexToCoords(hexMap);
for (int i = 0; i < indexCount; i++) {
if (const Terrain* terrain = hexMap->terrain()->Get(i);
@@ -240,6 +239,7 @@ OnDemandActionPointDistances::OnDemandActionPointDistances(
battalionType(std::move(battTp)),
distances(static_cast<size_t>(map->row_count() * map->column_count())) {
const int indexCount = map->row_count() * map->column_count();
distances.resize(indexCount);
auto braveWaterPossibleCoords = includeBravingWater ? BraveWaterPossibleCoords(map) : nullptr;
@@ -12,7 +12,7 @@
// Dynamic thread count based on hardware capabilities
static const int ASYNC_COUNT = []() {
const int cores = static_cast<int>(std::thread::hardware_concurrency());
// Use cores-4 to leave room for OS and other processes, minimum 4 threads
// Use cores-2 to leave room for OS and other processes, minimum 4 threads
const int threadCount = std::max(4, cores - 4);
std::cerr << "ActionPointDistances using " << threadCount << " threads (detected " << cores
<< " cores)\n";
@@ -35,7 +35,7 @@ auto FixedActionPointDistances::Create(
const int indexCount = map->row_count() * map->column_count();
vector<std::future<vector<vector<DIST_T>>>> futures(ASYNC_COUNT);
vector<std::future<vector<vector<DIST_T>>>> futures(indexCount);
auto braveWaterPossibleCoords =
includeBravingWater ? apd->BraveWaterPossibleCoords(map) : nullptr;
@@ -302,17 +302,16 @@ void MutatingApplyResult(
const Unit *changedUnit = &UnitFromBytes(changedUnitBytes);
internalAssert(
changedUnit->unit_id() < static_cast<int32_t>(mutatingGameState->units()->size()),
changedUnit->unit_id() < (int32_t)mutatingGameState->units()->size(),
"Got a changed unit outside acceptable range");
const auto battalionSizeAfter = changedUnit->battalion().size();
const int battalionSizeBefore =
(changedUnit->unit_id() < static_cast<int32_t>(initialUnitCount))
? mutatingGameState->units()
->Get(changedUnit->unit_id())
->battalion()
.size()
: changedUnit->battalion().size();
const int battalionSizeBefore = (changedUnit->unit_id() < (int32_t)initialUnitCount)
? mutatingGameState->units()
->Get(changedUnit->unit_id())
->battalion()
.size()
: changedUnit->battalion().size();
auto status = changedUnit->status();
if (IsDestroyed(*changedUnit)) {
@@ -5,7 +5,6 @@
#include "src/main/cpp/net/eagle0/shardok/library/action_result_applier/GameStateCopier.hpp"
#include <cstring>
#include <utility>
namespace shardok {
@@ -32,7 +31,7 @@ auto CopyWithExtraUnits(const GameStateW& original, int additionalCount) -> Game
unit.mutable_location().mutate_row(-1);
unit.mutable_location().mutate_column(-1);
}
endGST.units.push_back(std::move(unit));
endGST.units.push_back(unit);
}
// Copy occupied tiles bitfield from original GameState (much faster than O(n) rebuild)
@@ -4,8 +4,6 @@
#include "src/main/cpp/net/eagle0/shardok/library/action_result_applier/UnitHelpers.hpp"
#include <cstring>
namespace shardok::fb {
using namespace net::eagle0::shardok::storage::fb;
@@ -29,7 +27,7 @@ auto ApplyUnit(
}
}
std::memcpy(toUnit, fromUnit, sizeof(Unit));
memcpy(toUnit, fromUnit, sizeof(Unit));
toUnit->mutate_status(status);
if (toUnit->has_attached_hero()) {
@@ -4,8 +4,6 @@
#include "EndPlayerSetupCommand.hpp"
#include <utility>
#include "src/main/cpp/net/eagle0/shardok/library/action_result_applier/ActionResultApplier.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/actions/NewRoundAction.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/actions/StartPlayerTurnAction.hpp"
@@ -35,9 +33,9 @@ auto EndPlayerSetupCommand::InternalExecute(
gameStartResult.mutable_game_status()->set_state(
GameStatus::State::GameStatus_State_GAME_RUNNING);
gameStartResult.mutable_game_status()->set_description("The game has begun!");
allResults.push_back(gameStartResult);
startingGameState = ApplyResult(startingGameState, gameStartResult, settings);
allResults.push_back(std::move(gameStartResult));
auto newRoundResults =
NewRoundAction(startingGameState, settings).Execute(currentState, generator);
@@ -63,4 +61,4 @@ auto EndPlayerSetupCommand::GetCommandProto() const -> shardok::CommandProto {
return proto;
}
} // namespace shardok
} // namespace shardok
@@ -8,7 +8,6 @@
#include "FallIntoWaterAction.hpp"
#include <array>
#include <utility>
#include "src/main/cpp/net/eagle0/shardok/library/map/Coordinates.hpp"
@@ -56,17 +55,16 @@ auto EscapeChance(
const int16_t windFactor = -currentWeather.wind().speed_in_mph() * 2;
const std::vector<double> stats = {intelligence, agility};
std::vector<OtherFactor> otherFactors;
if (terrain.modifier().fire().present()) {
const std::array otherFactors = {MakeOtherFactor(-50, "fire")};
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
otherFactors.push_back(MakeOtherFactor(-50, "fire"));
}
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats);
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
}
auto ToVector(const Units &units) -> vector<const Unit *> {
auto v = vector<const Unit *>{};
v.reserve(units.size());
for (const Unit *u : units) { v.push_back(u); }
return v;
}
@@ -78,7 +76,6 @@ auto FallIntoWaterAction::AdjacentsWithTerrain(
const CoordsSet adjacentCoords = HexMapUtils::GetAdjacentCoords(gameState->hex_map(), location);
vector<AdjacentWithTerrain> vec{};
vec.reserve(adjacentCoords.size());
for (const Coords &adjCoords : adjacentCoords) {
const auto *possibleOccupant = Occupant(gameState->units(), adjCoords);
@@ -106,7 +103,6 @@ auto FallIntoWaterAction::InternalExecute(
const GameStateW &currentState,
const std::shared_ptr<RandomGenerator> &generator) const -> vector<ActionResult> {
vector<ActionResult> results{};
results.reserve(2);
auto fallerTerrain =
*GetTerrain(currentState->hex_map(), currentState->units()->Get(fallerId)->location());
@@ -56,7 +56,7 @@ auto IceAdjustmentAction::InternalExecute(
*resultProto.mutable_target_coords() = ToCoordsProto(coords);
*resultProto.add_changed_tile_modifiers() = MakeTmc(coords, newTerrain.modifier());
results.push_back(std::move(resultProto));
results.push_back(resultProto);
if (terrain.modifier().ice().present() && !IsFrozen(newTerrain.modifier()) &&
fallAction != nullptr) {
@@ -71,4 +71,4 @@ auto IceAdjustmentAction::InternalExecute(
return results;
}
} // namespace shardok
} // namespace shardok
@@ -160,7 +160,6 @@ auto MeteorCastAction::InternalExecute(
const GameStateW &currentState,
const std::shared_ptr<RandomGenerator> &generator) const -> vector<ActionResultProto> {
vector<ActionResultProto> allResults{};
allResults.reserve(actorIds.size() * 16);
auto runningGameState = currentState;
for (const UnitId &actorId : actorIds) {
const Unit *actorBefore = currentState->units()->Get(actorId);
@@ -184,7 +183,7 @@ auto MeteorCastAction::PerformOneActorCast(
mainResult.mutable_actor()->set_value(actorBefore->unit_id());
*mainResult.mutable_target_coords() =
ToCoordsProto(actorBefore->attached_hero().profession_info().cast_target());
results.push_back(std::move(mainResult));
results.push_back(mainResult);
const Coords target = actorBefore->attached_hero().profession_info().cast_target();
if (const Unit *possibleOccupant = runningGameState.GetOccupant(target)) {
@@ -151,7 +151,7 @@ auto NewWeather(
return MakeWeather(
newConditions,
MakeWind(
static_cast<net::eagle0::shardok::common::HexMapDirection>(newWindDirection),
(net::eagle0::shardok::common::HexMapDirection)newWindDirection,
newWindSpeed));
}
@@ -159,7 +159,6 @@ auto NewRoundAction::InternalExecute(
const GameStateW &currentState,
const std::shared_ptr<RandomGenerator> &generator) const -> vector<ActionResultProto> {
vector<ActionResultProto> results{};
results.reserve(4);
GameStateW runningGameState = startingGameState;
@@ -178,7 +177,6 @@ auto NewRoundAction::InternalExecute(
ActionList fireSpreadAction = fireSpreadActionFactory->MakeFireSpreadActions(
runningGameState->hex_map(),
runningGameState->weather());
results.reserve(results.size() + fireSpreadAction.size() + 1);
for (const ActionSPtr &action : fireSpreadAction) {
auto fireSpreadResults = action->Execute(currentState, generator);
for (const auto &result : fireSpreadResults) {
@@ -193,7 +191,6 @@ auto NewRoundAction::InternalExecute(
*runningGameState->units(),
runningGameState->month(),
runningGameState->weather());
results.reserve(results.size() + iceAdjustmentAction.size());
for (const ActionSPtr &action : iceAdjustmentAction) {
auto iceAdjustmentResults = action->Execute(currentState, generator);
for (const auto &result : iceAdjustmentResults) {
@@ -223,7 +220,6 @@ auto NewRoundAction::InternalExecute(
ActionList fireOutActions = fireOutActionFactory->MakeFireOutActions(
runningGameState->hex_map(),
runningGameState->weather());
results.reserve(results.size() + fireOutActions.size());
for (const ActionSPtr &action : fireOutActions) {
auto fireOutResults = action->Execute(currentState, generator);
for (const auto &result : fireOutResults) {
@@ -25,7 +25,6 @@ auto PerformUndeadCommandsAction::InternalExecute(
const std::shared_ptr<RandomGenerator> &generator) const -> vector<ActionResultProto> {
GameStateW runningGameState = startingGameState;
vector<ActionResultProto> allResults{};
allResults.reserve(runningGameState->units()->size());
while (runningGameState->current_player() == UNCONTROLLED_PLAYER_ID) {
auto commands = availableCommandsFactory->GetAvailableCommands(
@@ -57,7 +56,6 @@ auto ChooseUndeadCommand(
const HexMap *hexMap,
const std::shared_ptr<RandomGenerator> &randomGenerator) -> CommandSPtr {
CommandList attackCommands;
attackCommands.reserve(commands->size());
for (const auto &command : *commands) {
const CommandProto &possibleAttackCommandProto = command->GetCommandProto();
@@ -78,7 +76,6 @@ auto ChooseUndeadCommand(
}
CommandList moveCommands;
moveCommands.reserve(commands->size());
CommandSPtr bestMoveCommand = nullptr;
for (const auto &command : *commands) {
@@ -4,8 +4,6 @@
#include "SnowAdjustmentAction.hpp"
#include <utility>
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/TileModifierHelpers.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/TileModifier.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/TileModifierWithCoords.hpp"
@@ -104,8 +102,8 @@ auto SnowAdjustmentAction::InternalExecute(
*resultProto.mutable_target_coords() = ToCoordsProto(coords);
*resultProto.add_changed_tile_modifiers() = MakeTmc(coords, newTerrain.modifier());
results.push_back(std::move(resultProto));
results.push_back(resultProto);
return results;
}
} // namespace shardok
} // namespace shardok
@@ -49,9 +49,7 @@ auto UpdateGameStatusAction::SurvivingPlayers() const -> std::unordered_set<Play
for (const Unit* unit : *gameState->units()) {
if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;
if (unit->player_id() < 0 ||
static_cast<uint32_t>(unit->player_id()) >= gameState->player_infos()->size())
continue;
if ((uint32_t)unit->player_id() >= gameState->player_infos()->size()) continue;
if (!unit->hidden() && (unit->battalion().size() > 0 ||
(unit->has_attached_hero() && unit->attached_hero().vigor() > 0))) {
@@ -4,8 +4,6 @@
#include "BlowBridgeCommandFactory.hpp"
#include <array>
#include "src/main/cpp/net/eagle0/shardok/library/commands/BlowBridgeCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
@@ -94,13 +92,13 @@ auto GetBlowBridgeOdds(
constexpr int16_t weatherFactor = 0;
constexpr int16_t windFactor = 0;
const std::vector stats = {strength, agility, intelligence};
std::vector<OtherFactor> otherFactors;
if (hasForestAccess) {
const std::array otherFactors = {
MakeOtherFactor(settings.Backing().blow_bridge_forest_bonus(), "forest nearby")};
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
otherFactors.push_back(
MakeOtherFactor(settings.Backing().blow_bridge_forest_bonus(), "forest nearby"));
}
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats);
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
}
} // namespace shardok
@@ -4,8 +4,6 @@
#include "BraveWaterCommandFactory.hpp"
#include <array>
#include "src/main/cpp/net/eagle0/shardok/library/commands/BraveWaterCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
@@ -41,20 +39,13 @@ PercentileRollOdds GetBraveWaterOdds(
wisdom * settings.Backing().brave_water_wisdom_factor());
const double armamentFactor = armament * settings.Backing().brave_water_armament_factor();
auto otherFactors = vector<OtherFactor>{MakeOtherFactor(armamentFactor, "Armament")};
if (isRanger) {
const std::array otherFactors = {
MakeOtherFactor(armamentFactor, "Armament"),
MakeOtherFactor(settings.Backing().brave_water_ranger_bonus(), "Ranger")};
return MakeOdds(
/*base*/ baseOdds,
/*terrain*/ 0,
weatherFactor,
/*wind*/ 0,
statFactor,
otherFactors);
otherFactors.emplace_back(
MakeOtherFactor(settings.Backing().brave_water_ranger_bonus(), "Ranger"));
}
const std::array otherFactors = {MakeOtherFactor(armamentFactor, "Armament")};
return MakeOdds(
/*base*/ baseOdds,
/*terrain*/ 0,
@@ -4,8 +4,6 @@
#include "BuildBridgeCommandFactory.hpp"
#include <array>
#include "src/main/cpp/net/eagle0/shardok/library/commands/BuildBridgeCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
@@ -83,13 +81,13 @@ auto GetBuildBridgeOdds(
constexpr int16_t weatherFactor = 0;
constexpr int16_t windFactor = 0;
const std::vector stats = {strength, agility, intelligence};
std::vector<OtherFactor> otherFactors;
if (hasForestAccess) {
const std::array otherFactors = {
MakeOtherFactor(settings.Backing().build_bridge_forest_bonus(), "forest nearby")};
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
otherFactors.push_back(
MakeOtherFactor(settings.Backing().build_bridge_forest_bonus(), "forest nearby"));
}
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats);
return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
}
} // namespace shardok
@@ -33,7 +33,6 @@ namespace {
const PlayerId actorPlayerId,
const vector<PlayerId>& allyPids) -> std::vector<UnitId> {
std::vector<UnitId> captiveUnitIds;
captiveUnitIds.reserve(units->size());
for (const auto* unit : *units) {
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT &&
unit->has_attached_hero() && IsHostileToActor(unit, actorPlayerId, allyPids)) {
@@ -9,7 +9,6 @@
#include "FleeCommandFactory.hpp"
#include <algorithm>
#include <array>
#include <ranges>
#include "src/main/cpp/net/eagle0/shardok/library/commands/BecomeOutlawCommand.hpp"
@@ -68,11 +67,15 @@ void FleeCommandFactory::AddAvailableFleeCommands(
}
}
const std::array otherFactors = {
MakeOtherFactor(adjacentFriendliesMod, "adjacentFriendlies"),
MakeOtherFactor(adjacentEnemiesMod, "adjacentEnemies"),
MakeOtherFactor(adjacentImpassableMod, "adjacentImpassable")};
odds = MakeOdds(settings.Backing().flee_success_base_chance(), 0, 0, 0, 0, otherFactors);
odds = MakeOdds(
settings.Backing().flee_success_base_chance(),
0,
0,
0,
0,
{MakeOtherFactor(adjacentFriendliesMod, "adjacentFriendlies"),
MakeOtherFactor(adjacentEnemiesMod, "adjacentEnemies"),
MakeOtherFactor(adjacentImpassableMod, "adjacentImpassable")});
}
if (unit->can_flee()) {
@@ -209,7 +209,6 @@ auto ConstructMoveDestinations(
const auto occupants = Occupants(*units, hexMap->row_count(), hexMap->column_count());
std::vector<AccumulatedMoveInfo> allAmis{};
allAmis.reserve(hexMap->row_count() * hexMap->column_count());
CoordsSet checkedDestinations(hexMap);
checkedDestinations.Add(startingLocation);
const AccumulatedMoveInfo baseInfo(startingLocation, startingLocation, 0, false, {});
@@ -4,7 +4,6 @@
#include "BraveWaterCommand.hpp"
#include <optional>
#include <utility>
#include "src/main/cpp/net/eagle0/shardok/library/actions/DefensiveAmbushAction.hpp"
@@ -18,7 +17,7 @@ using net::eagle0::shardok::common::ActionType;
vector<ActionResult> BraveWaterCommand::InternalExecuteWithRoll(
const GameStateW &currentState,
const std::shared_ptr<RandomGenerator> &generator,
const std::optional<int32_t> roll) const {
const optional<int32_t> roll) const {
// Succeeds on high roll
const auto successRoll = 101 - roll.value_or(generator->Percentile());
const auto lossesRoll = generator->Percentile();
@@ -55,14 +54,13 @@ vector<ActionResult> BraveWaterCommand::InternalExecuteWithRoll(
crossingResult.mutable_actor()->set_value(actorAfterSpend.unit_id());
vector<ActionResult> allResults{};
allResults.reserve(ambusherId.has_value() ? 2 : 1);
if (PercentileRollSucceeds(successOdds, successRoll)) {
if (ambusherId.has_value()) {
// Tile is occupied -- ambush!
crossingResult.set_type(ActionType::CROSSED_WATER);
AddChangedUnit(crossingResult, actorAfterSpend);
allResults.push_back(std::move(crossingResult));
allResults.push_back(crossingResult);
auto ambushResults =
DefensiveAmbushAction(ambusherId.value(), actorId, ambusherRoll, settings)
@@ -75,13 +73,13 @@ vector<ActionResult> BraveWaterCommand::InternalExecuteWithRoll(
crossingResult.set_type(ActionType::CROSSED_WATER);
actorAfterSpend.mutable_location() = target;
AddChangedUnit(crossingResult, actorAfterSpend);
allResults.push_back(std::move(crossingResult));
allResults.push_back(crossingResult);
}
} else {
crossingResult.set_type(ActionType::CROSS_WATER_FAILED);
AddChangedUnit(crossingResult, actorAfterSpend);
// location doesn't change
allResults.push_back(std::move(crossingResult));
allResults.push_back(crossingResult);
}
return allResults;
@@ -5,8 +5,6 @@
#ifndef EAGLE0_SHARDOK_LIBRARY_COMMANDS_BRAVE_WATER_COMMAND_HPP
#define EAGLE0_SHARDOK_LIBRARY_COMMANDS_BRAVE_WATER_COMMAND_HPP
#include <optional>
#include "src/main/cpp/net/eagle0/shardok/library/ActionCost.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/Coordinates.hpp"
@@ -18,7 +16,7 @@ protected:
[[nodiscard]] auto InternalExecuteWithRoll(
const GameStateW &currentState,
const std::shared_ptr<RandomGenerator> &generator,
std::optional<int32_t> roll) const -> vector<ActionResult> override;
optional<int32_t> roll) const -> vector<ActionResult> override;
const ActionCost cost;
const UnitId actorId;
@@ -48,10 +48,8 @@ auto EndTurnCommand::InternalExecute(
GameStateW runningGameState = currentState;
vector<ActionResultProto> allResults{};
allResults.reserve(runningGameState->units()->size() + 2);
vector<UnitId> castingUnitIds{};
castingUnitIds.reserve(runningGameState->units()->size());
for (const auto &unit : *runningGameState->units()) {
if (unit->player_id() != GetPlayerId()) continue;
@@ -4,9 +4,6 @@
#include "FearCommand.hpp"
#include <array>
#include <string>
#include "src/main/cpp/net/eagle0/shardok/library/actions/ActionRequiresHeroException.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/unit/Unit.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/ActionResultFlatbufferHelpers.hpp"
@@ -16,7 +13,7 @@ namespace shardok {
using net::eagle0::shardok::common::ActionType;
static const std::string opponentCharismaString = "opponentCharisma";
static const string opponentCharismaString = "opponentCharisma";
auto FearCommand::InternalExecute(
const GameStateW& currentState,
@@ -84,15 +81,13 @@ auto FearCommand::GetFearOdds(
double attackerCharisma,
double attackerWisdom,
const double defenderCharisma) -> PercentileRollOdds {
const std::array otherFactors = {
MakeOtherFactor(int(50.0 - defenderCharisma), opponentCharismaString)};
return MakeOdds(
fearBaseOdds,
0,
0,
0,
{attackerCharisma, attackerCharisma, attackerWisdom},
otherFactors);
{MakeOtherFactor(int(50.0 - defenderCharisma), opponentCharismaString)});
}
int32_t FearCommand::GetOddsPercentile() const {
@@ -4,8 +4,6 @@
#include "FreezeWaterCommand.hpp"
#include <utility>
#include "src/main/cpp/net/eagle0/shardok/library/actions/ActionRequiresHeroException.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/actions/UndeadFrozenAction.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/TileModifierHelpers.hpp"
@@ -47,7 +45,6 @@ auto FreezeWaterCommand::InternalExecute(
*freezeResult.mutable_odds() = odds;
vector<ActionResult> allResults{};
allResults.reserve(occupantId != -1 ? 2 : 1);
if (PercentileRollSucceeds(odds, roll)) {
freezeResult.set_type(ActionType::FREEZE_WATER);
@@ -59,7 +56,7 @@ auto FreezeWaterCommand::InternalExecute(
freezeResult.set_type(ActionType::FREEZE_WATER_FAILED);
}
allResults.push_back(std::move(freezeResult));
allResults.push_back(freezeResult);
if (occupantId != -1 && PercentileRollSucceeds(odds, roll)) {
auto destroyedResults = UndeadFrozenAction(currentState->units()->Get(occupantId))
.Execute(currentState, generator);
@@ -56,7 +56,6 @@ auto HideCommand::InternalExecute(
auto alliedPids = AlliedPids(currentState, GetPlayerId());
vector<Unit> adjacentEnemyRangers{};
adjacentEnemyRangers.reserve(6);
for (const auto &adjCoords :
HexMapUtils::GetAdjacentCoords(currentState->hex_map(), target)) {
const auto *oneOverOccupant = currentState.GetOccupant(adjCoords);
@@ -77,7 +77,6 @@ auto HolyWaveDamageAction::MakeDamageActions(
const vector<UnitId> &damagedUnitIds,
double damagePercentage) -> ActionList {
ActionList actions;
actions.reserve(damagedUnitIds.size());
std::transform(
std::begin(damagedUnitIds),
std::end(damagedUnitIds),
@@ -170,7 +169,6 @@ auto HolyWaveInspireAction::MakeInspireActions(
const double maxInspireOverBase,
const double vigorBuf) -> ActionList {
ActionList actions;
actions.reserve(friendlyUnitIds.size());
std::transform(
std::begin(friendlyUnitIds),
std::end(friendlyUnitIds),
@@ -202,7 +200,6 @@ auto HolyWaveCommand::InternalExecute(
const GameStateW &currentState,
const std::shared_ptr<RandomGenerator> & /*generator*/) const -> vector<ActionResult> {
vector<ActionResult> results;
results.reserve(1 + 6 + 1);
const Unit *actorBefore = currentState->units()->Get(actorId);
@@ -231,8 +228,6 @@ auto HolyWaveCommand::InternalExecute(
vector<UnitId> undeadIds{};
vector<UnitId> allFriendlyUnitIds{};
undeadIds.reserve(6);
allFriendlyUnitIds.reserve(7);
auto allyPids = AlliedPids(runningState, GetPlayerId());
for (const auto &coords :
HexMapUtils::GetAdjacentCoords(runningState->hex_map(), actorBefore->location())) {
@@ -8,7 +8,6 @@
#include "MeleeCommand.hpp"
#include <optional>
#include <utility>
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/TerrainHelpers.hpp"
@@ -37,7 +36,7 @@ MeleeCommand::MeleeCommand(
auto MeleeCommand::InternalExecuteWithRoll(
const GameStateW& currentState,
const std::shared_ptr<RandomGenerator>& generator,
const std::optional<int32_t> roll) const -> vector<ActionResult> {
const optional<int32_t> roll) const -> vector<ActionResult> {
const auto ambushRoll = generator->Percentile();
const auto attackerRoll = roll.value_or(generator->OpenEndedPercentile());
const auto defenderRoll = generator->OpenEndedPercentile();
@@ -8,8 +8,6 @@
#include "src/main/cpp/net/eagle0/shardok/library/commands/challenge_duel_command/DuelUtils.hpp"
#include <array>
#include "src/main/cpp/net/eagle0/shardok/library/unit/Unit.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/CombatUtils.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/view_filters/UnitFilter.hpp"
@@ -29,6 +27,7 @@ auto GetAcceptOdds(
net::eagle0::shardok::api::UnitView filteredView =
UnitFilteredForPlayer(settings, challenger, defender->player_id(), {});
std::vector<double> stats = {static_cast<double>(defender->attached_hero().bravery())};
std::vector<OtherFactor> otherFactors{};
Unit believedChallenger = *challenger;
MutatingSpendActionPoints(
@@ -101,6 +100,8 @@ auto GetAcceptOdds(
double battalionStrengthSwing = (NUM_RUNS - 2.0 * defenderWins) / NUM_RUNS;
double battalionStrengthFactor =
BATTALION_STRENGTH_WEIGHT * battalionStrengthSwing * defender->attached_hero().wisdom();
otherFactors.push_back(
MakeOtherFactor(static_cast<int>(battalionStrengthFactor), "battalion strength"));
bool heroStatsKnown = filteredView.attached_hero().has_stats();
@@ -143,10 +144,8 @@ auto GetAcceptOdds(
double duelOddsSwing = (2.0 * defenderWins - NUM_RUNS) / NUM_RUNS;
double expectedDuelOutcomeFactor =
HERO_STRENGTH_WEIGHT * duelOddsSwing * defender->attached_hero().wisdom();
const std::array otherFactors = {
MakeOtherFactor(static_cast<int>(battalionStrengthFactor), "battalion strength"),
MakeOtherFactor(static_cast<int>(expectedDuelOutcomeFactor), "expected duel outcome")};
otherFactors.push_back(
MakeOtherFactor(static_cast<int>(expectedDuelOutcomeFactor), "expected duel outcome"));
return MakeOdds(settings.Backing().duel_base_accept_odds(), 0, 0, 0, stats, otherFactors);
}
@@ -8,7 +8,6 @@
#include <flatbuffers/flatbuffers.h>
#include <cstdlib>
#include <cstring>
#include <utility>
#include "src/main/cpp/net/eagle0/common/ByteHasher.hpp"
@@ -35,7 +34,7 @@ private:
static auto CopyBuffer(const uint8_t* data, const size_t size) -> uint8_t* {
auto* b = WrapperAllocator::GetDefaultAllocator()->allocate(size);
std::memcpy(b, data, size);
memcpy(b, data, size);
return b;
}
@@ -82,7 +81,7 @@ public:
}
buffer = CopyBuffer(other.buffer, other.size);
} else if (buffer != nullptr && other.buffer != nullptr) {
std::memcpy(buffer, other.buffer, other.size);
memcpy(buffer, other.buffer, other.size);
}
size = other.size;
@@ -142,7 +141,7 @@ public:
}
size_t offset;
std::memcpy(&offset, str.data(), sizeof(size_t));
memcpy(&offset, str.data(), sizeof(size_t));
size_t size = str.size() - sizeof(size_t);
auto toReturn = Wrapper(
@@ -160,7 +159,7 @@ public:
}
size_t offset;
std::memcpy(&offset, bv.data(), sizeof(size_t));
memcpy(&offset, bv.data(), sizeof(size_t));
size_t size = bv.size() - sizeof(size_t);
// Note: CopyBuffer will handle const_cast safely by making a copy
@@ -7,7 +7,6 @@
#include <flatbuffers/flatbuffers.h>
#include <unordered_map>
#include <utility>
#include <vector>
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/HexMapHelpers.hpp"
@@ -47,7 +46,6 @@ auto ConvertVictoryCondition(const net::eagle0::shardok::common::VictoryConditio
auto FromPlayerInfoProto(flatbuffers::FlatBufferBuilder& fbb, const PlayerInfoProto& piProto)
-> flatbuffers::Offset<net::eagle0::shardok::storage::fb::PlayerInfo> {
auto alliesVec = std::vector<AlliedPlayer>();
alliesVec.reserve(piProto.allies_size());
for (const auto& apProto : piProto.allies()) { alliesVec.emplace_back(apProto.player_id()); }
const auto alliesOffset = fbb.CreateVectorOfStructs(alliesVec);
@@ -82,17 +80,18 @@ auto FoodPercentageByPid(
const std::vector<Unit>& units,
const SettingsGetter& settings) -> std::unordered_map<int, double> {
std::unordered_map<int, double> foodRequirementsByPid;
foodRequirementsByPid.reserve(playerInfoProtos.size());
for (const auto& unit : units) {
foodRequirementsByPid[unit.player_id()] += RequiredMonthlyFood(unit, settings);
const auto cost = RequiredMonthlyFood(unit, settings);
if (foodRequirementsByPid.count(unit.player_id()) > 0) {
foodRequirementsByPid[unit.player_id()] += cost;
} else {
foodRequirementsByPid[unit.player_id()] = cost;
}
}
std::unordered_map<int, double> foodPercentByPid;
foodPercentByPid.reserve(playerInfoProtos.size());
for (const auto& pi : playerInfoProtos) {
const auto requirementIt = foodRequirementsByPid.find(pi.player_id());
const double required =
requirementIt == foodRequirementsByPid.end() ? 0.0 : requirementIt->second;
const double required = foodRequirementsByPid[pi.player_id()];
const double available = pi.starting_food();
foodPercentByPid[pi.player_id()] =
required == 0 ? 1.0 : std::max(1.0, available / required);
@@ -143,7 +142,6 @@ auto SetupInitialGameState(
auto gameIdOffset = fbb.CreateString(gameId);
auto playerInfoVec = std::vector<Offset<PlayerInfo>>();
playerInfoVec.reserve(playerInfoProtos.size());
for (const auto& piProto : playerInfoProtos) {
playerInfoVec.push_back(FromPlayerInfoProto(fbb, piProto));
}
@@ -160,7 +158,7 @@ auto SetupInitialGameState(
}
// Add slack units: 3 per necromancer, minimum 5 for general use
const int slackUnits = std::max(5, necromancerCount * 3);
int slackUnits = std::max(5, necromancerCount * 3);
auto unitsVec = std::vector<Unit>();
unitsVec.reserve(units.size() + slackUnits);
@@ -187,7 +185,7 @@ auto SetupInitialGameState(
modifiedUnit.mutate_status(net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT);
}
unitsVec.push_back(std::move(modifiedUnit));
unitsVec.push_back(modifiedUnit);
}
// Add slack units for future expansion (e.g., necromancer raise dead)
@@ -200,7 +198,7 @@ auto SetupInitialGameState(
slackUnit.mutate_eagle_player_id(-1);
slackUnit.mutable_location().mutate_row(-1);
slackUnit.mutable_location().mutate_column(-1);
unitsVec.push_back(std::move(slackUnit));
unitsVec.push_back(slackUnit);
}
auto unitsOffset = fbb.CreateVectorOfSortedStructs(&unitsVec);
@@ -231,11 +229,9 @@ auto SetupInitialGameState(
auto ToProto(const GameStatus* fbStatus) -> GameStatusProto {
GameStatusProto protoStatus{};
protoStatus.set_state(
static_cast<net::eagle0::shardok::common::GameStatus_State>(fbStatus->state()));
protoStatus.set_state((net::eagle0::shardok::common::GameStatus_State)fbStatus->state());
if (fbStatus->description()) { protoStatus.set_description(fbStatus->description()->str()); }
if (fbStatus->winning_shardok_ids()) {
protoStatus.mutable_winning_shardok_ids()->Reserve(fbStatus->winning_shardok_ids()->size());
for (const auto pid : *fbStatus->winning_shardok_ids()) {
if (pid != -1) { protoStatus.add_winning_shardok_ids(pid); }
}
@@ -278,7 +274,6 @@ auto ToPlayerInfoProto(const PlayerInfo* fbPI) -> PlayerInfoProto {
fbPI->victory_conditions()->end()};
if (fbPI->allies()) {
piProto.mutable_allies()->Reserve(fbPI->allies()->size());
for (const auto& ally : *fbPI->allies()) {
piProto.add_allies()->set_player_id(ally->player_id());
}
@@ -65,7 +65,6 @@ auto ToProto(const HexMap* hexMap) -> HexMapProto {
hexMapProto.set_column_count(hexMap->column_count());
if (hexMap->monthly_weather()) {
hexMapProto.mutable_monthly_weather()->Reserve(hexMap->monthly_weather()->size());
for (const auto& mw : *hexMap->monthly_weather()) {
auto* mwProto = hexMapProto.add_monthly_weather();
mwProto->set_is_winter(mw->is_winter());
@@ -80,15 +79,12 @@ auto ToProto(const HexMap* hexMap) -> HexMapProto {
}
}
hexMapProto.mutable_terrain()->Reserve(hexMap->terrain()->size());
for (const auto& terr : *hexMap->terrain()) {
*hexMapProto.add_terrain() = ToTerrainProto(terr);
}
if (hexMap->defender_starting_positions() &&
hexMap->defender_starting_positions()->positions()) {
hexMapProto.mutable_defender_starting_positions()->mutable_positions()->Reserve(
hexMap->defender_starting_positions()->positions()->size());
for (const auto& dsp : *hexMap->defender_starting_positions()->positions()) {
auto* pos = hexMapProto.mutable_defender_starting_positions()->add_positions();
pos->set_row(dsp->row());
@@ -97,13 +93,10 @@ auto ToProto(const HexMap* hexMap) -> HexMapProto {
}
if (hexMap->attacker_starting_positions()) {
hexMapProto.mutable_attacker_starting_positions()->Reserve(
hexMap->attacker_starting_positions()->size());
for (const auto& aspl : *hexMap->attacker_starting_positions()) {
auto* list = hexMapProto.add_attacker_starting_positions();
if (aspl->positions()) {
list->mutable_positions()->Reserve(aspl->positions()->size());
for (const auto& asp : *aspl->positions()) {
auto* pos = list->add_positions();
pos->set_row(asp->row());
@@ -59,9 +59,7 @@ private:
public:
CoordsSet(int w, int h) : mapWidth(w), mapHeight(h), indexCount(w * h) {
if (kCoordsSetIndexChecks) {
assert(static_cast<size_t>(w) * static_cast<size_t>(h) < STANDARD_SIZE);
}
if (kCoordsSetIndexChecks) { assert(w * h < STANDARD_SIZE); }
clear();
}
explicit CoordsSet(const HexMap* map) : CoordsSet(map->column_count(), map->row_count()) {}
@@ -28,11 +28,11 @@ using net::eagle0::shardok::common::HexMapDirection::SOUTHWEST;
using net::eagle0::shardok::common::HexMapDirection::WEST;
inline HexMapDirection operator+(const HexMapDirection& dir, const int delta) {
return static_cast<HexMapDirection>((static_cast<int>(dir) + delta) % 6);
return HexMapDirection((int(dir) + delta) % 6);
}
inline HexMapDirection operator-(const HexMapDirection& dir, const int delta) {
return static_cast<HexMapDirection>((static_cast<int>(dir) - delta) % 6);
return HexMapDirection((int(dir) - delta) % 6);
}
inline HexMapDirection& operator++(HexMapDirection& dir) {
@@ -96,7 +96,7 @@ void GameSettings::Setter::SetRandomGenerator(std::shared_ptr<RandomGenerator> g
}
void GameSettings::Setter::RegisterBattalionType(const BattalionTypeSPtr& newType) {
if (static_cast<int64_t>(settings.battalionTypes.size()) <= newType->typeId) {
if ((int64_t)settings.battalionTypes.size() <= newType->typeId) {
settings.battalionTypes.resize(newType->typeId + 1);
}
settings.battalionTypes[newType->typeId] = newType;
@@ -9,7 +9,6 @@
#include "TutorialBattleController.hpp"
#include <ranges>
#include <utility>
#include <vector>
#include "src/main/cpp/net/eagle0/shardok/library/util/ActionResultFlatbufferHelpers.hpp"
@@ -39,7 +38,6 @@ auto TutorialBattleController::CheckAndExecuteEvents(
if (!enabled_) { return {}; }
TutorialEventResults combined;
combined.actionResults.reserve(config_.events_size());
// Check events in config order
for (const auto& event : config_.events()) {
@@ -197,13 +195,12 @@ auto TutorialBattleController::ExecuteFleeAction(
const FleeActionProto& fleeAction,
const GameStateW& state) const -> std::vector<ActionResult> {
std::vector<ActionResult> results;
results.reserve(state->units()->size() + 1);
const PlayerId fleeingPlayerId = fleeAction.player_id();
// First, create a "tutorial enemy fled" notification
ActionResult fleeNotification{};
fleeNotification.set_type(ActionType::TUTORIAL_ENEMY_FLED);
results.push_back(std::move(fleeNotification));
results.push_back(fleeNotification);
// Determine which units should flee
std::set<int32_t> specificUnitIds(fleeAction.unit_ids().begin(), fleeAction.unit_ids().end());
@@ -236,7 +233,7 @@ auto TutorialBattleController::ExecuteFleeAction(
AddUnitToResolved(*resolvedUnit, *unit);
resolvedUnit->set_status(net::eagle0::shardok::storage::ResolvedUnit_UnitStatus_FLED_UNIT);
results.push_back(std::move(fleeResult));
results.push_back(fleeResult);
}
return results;
@@ -258,9 +255,7 @@ auto TutorialBattleController::ExecuteReinforcementsAction(
const auto* attackerPositions = state->hex_map()->attacker_starting_positions();
if (startingPosGroupIndex >= 0 &&
startingPosGroupIndex < static_cast<int>(attackerPositions->size())) {
const auto* startingPositions = attackerPositions->Get(startingPosGroupIndex)->positions();
availablePositions.reserve(startingPositions->size());
for (const auto* pos : *startingPositions) {
for (const auto* pos : *attackerPositions->Get(startingPosGroupIndex)->positions()) {
if (!state.GetOccupant(*pos)) { availablePositions.push_back(*pos); }
}
}
@@ -291,7 +286,7 @@ auto TutorialBattleController::ExecuteReinforcementsAction(
AddChangedUnit(reinforcementsResult, changedUnit);
}
eventResults.actionResults.push_back(std::move(reinforcementsResult));
eventResults.actionResults.push_back(reinforcementsResult);
return eventResults;
}
@@ -19,6 +19,12 @@
namespace shardok {
using std::begin;
using std::end;
using std::find_if;
using std::optional;
using std::remove_if;
using Unit = net::eagle0::shardok::storage::fb::Unit;
using Units = flatbuffers::Vector<const Unit *>;
@@ -21,8 +21,7 @@ static inline void AddChangedUnit(ActionResult& result, const Unit& unit) {
}
static inline void AddUnitToResolved(ResolvedUnit& resolvedUnit, const Unit& unit) {
*resolvedUnit.mutable_unit_bytes() =
std::string(reinterpret_cast<const char*>(&unit), sizeof(Unit));
*resolvedUnit.mutable_unit_bytes() = std::string((char*)&unit, sizeof(Unit));
}
} // namespace shardok
@@ -28,7 +28,6 @@ auto CubeToOffset(const Cube &cube) -> Coords {
auto CubesWithinDistance(const Cube &c1, const int distance) -> std::vector<Cube> {
std::vector<Cube> cubes{};
cubes.reserve(1 + 3 * distance * (distance + 1));
for (int x = -distance; x <= distance; x++) {
for (int y = std::max(-distance, -x - distance); y <= std::min(distance, -x + distance);
@@ -43,7 +42,6 @@ auto CubesWithinDistance(const Cube &c1, const int distance) -> std::vector<Cube
auto CubesWithExactDistance(const Cube &c1, const int distance) -> std::vector<Cube> {
std::vector<Cube> cubes{};
cubes.reserve(distance == 0 ? 1 : 6 * distance);
for (int x = -distance; x <= distance; x++) {
for (int y = std::max(-distance, -x - distance); y <= std::min(distance, -x + distance);
@@ -72,7 +70,7 @@ auto FixupCube(
newZ = -newY - newX;
}
return Cube(static_cast<int16_t>(newX), static_cast<int16_t>(newY), static_cast<int16_t>(newZ));
return Cube(int16_t(newX), int16_t(newY), int16_t(newZ));
}
// Always rounds half up, instead of away from zero.
@@ -92,7 +90,6 @@ auto CubeLerp(const Cube &c1, const Cube &c2, const double t) -> std::vector<Cub
const auto zDiff = abs(newZ - floatZ);
std::vector<Cube> cubes{};
cubes.reserve(2);
cubes.push_back(FixupCube(newX, xDiff, newY, yDiff, newZ, zDiff));
// The 0.5 case represents a situation where the line passes exactly between two hexes. In that
@@ -114,9 +111,6 @@ auto CubeLineInclusive(const Cube &c1, const Cube &c2) -> std::vector<Cube> {
std::vector<Cube> cubes;
const auto dist = CubeDistance(c1, c2);
if (dist == 0) { return {c1}; }
cubes.reserve(2 * (dist + 1));
const double reciprocal = 1.0 / dist;
for (int i = 0; i <= dist; i++) {
@@ -9,7 +9,6 @@
#include "HexMapUtils.hpp"
#include <algorithm>
#include <array>
#include <memory>
#include <ranges>
@@ -21,10 +20,10 @@ namespace shardok {
using Terrain = net::eagle0::shardok::storage::fb::Terrain;
const std::array<Coords, 6> ADJACENT_TILE_MODS_EVEN_ROW =
const vector<Coords> ADJACENT_TILE_MODS_EVEN_ROW =
{Coords(-1, 0), Coords(0, 1), Coords(1, 0), Coords(1, -1), Coords(0, -1), Coords(-1, -1)};
const std::array<Coords, 6> ADJACENT_TILE_MODS_ODD_ROW =
const vector<Coords> ADJACENT_TILE_MODS_ODD_ROW =
{Coords(-1, 1), Coords(0, 1), Coords(1, 1), Coords(1, 0), Coords(0, -1), Coords(-1, 0)};
auto AllCastleCoords(const HexMap *hexMap) -> CoordsSet {
@@ -79,7 +78,7 @@ auto HasForestAccess(
const Coords &coords,
const Units *units,
const HexMap *hexMap,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const PlayerId player) -> bool {
if (GetTerrain(hexMap, coords)->type() ==
net::eagle0::shardok::storage::fb::Terrain_::Type_FOREST)
@@ -177,13 +176,12 @@ auto HexMapUtils::GetAdjacentCoords(const HexMap *map, const Coords &origin) ->
}
auto HexMapUtils::GetAdjacentTiles(const HexMap *map, const Coords &origin)
-> std::vector<AdjacentTile> {
std::vector<AdjacentTile> adjacentTiles{};
adjacentTiles.reserve(6);
-> vector<AdjacentTile> {
vector<AdjacentTile> adjacentTiles{};
const auto &modifierSet =
(origin.row() % 2 ? ADJACENT_TILE_MODS_ODD_ROW : ADJACENT_TILE_MODS_EVEN_ROW);
auto dir = static_cast<HexMapDirection>(0);
auto dir = HexMapDirection(0);
for (const Coords &modifier : modifierSet) {
Coords adjacentCoords =
Coords(origin.row() + modifier.row(), origin.column() + modifier.column());
@@ -198,17 +196,17 @@ auto HexMapUtils::GetAdjacentTiles(const HexMap *map, const Coords &origin)
}
[[nodiscard]] auto GetTilesAcrossWater(const HexMap *map, const Coords &coords) -> CoordsSet {
const std::vector<AdjacentTile> adjacentTiles = HexMapUtils::GetAdjacentTiles(map, coords);
const vector<AdjacentTile> adjacentTiles = HexMapUtils::GetAdjacentTiles(map, coords);
CoordsSet braveWaterTargets(map);
for (const AdjacentTile &adjacentTile : adjacentTiles) {
const auto *terrain = GetTerrain(map, adjacentTile.coords);
if (IsWater(static_cast<net::eagle0::shardok::common::Terrain_Type>(terrain->type()))) {
if (IsWater((net::eagle0::shardok::common::Terrain_Type)terrain->type())) {
if (terrain->modifier().bridge().present()) continue;
if (terrain->modifier().ice().present()) continue;
for (int i = -1; i <= 1; i++) {
const auto nextDirection =
static_cast<HexMapDirection>((adjacentTile.directionTo + i + 6) % 6);
(HexMapDirection)((adjacentTile.directionTo + i + 6) % 6);
const auto &maybeNext =
MaybeTileInDirection(map, adjacentTile.coords, nextDirection);
@@ -680,13 +678,11 @@ auto KnownAdjacentEnemies(
const PlayerId playerId,
const Units *units,
const HexMap *map,
const std::vector<PlayerId> &allyPids,
const Coords &coords) -> std::vector<const Unit *> {
std::vector<const Unit *> knownAdjacentEnemies{};
const CoordsSet &adjacentCoords = HexMapUtils::GetAdjacentCoords(map, coords);
knownAdjacentEnemies.reserve(adjacentCoords.size());
const vector<PlayerId> &allyPids,
const Coords &coords) -> vector<const Unit *> {
vector<const Unit *> knownAdjacentEnemies{};
for (const Coords &adjCoords : adjacentCoords) {
for (const Coords &adjCoords : HexMapUtils::GetAdjacentCoords(map, coords)) {
const auto *possibleEnemy = KnownEnemyOccupant(playerId, units, allyPids, adjCoords);
if (possibleEnemy) { knownAdjacentEnemies.push_back(possibleEnemy); }
}
@@ -697,7 +693,7 @@ auto KnownAdjacentEnemies(
auto KnownEnemyOccupant(
const PlayerId playerId,
const Units *units,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> const Unit * {
const auto *occupant = Occupant(units, coords);
if (occupant) {
@@ -29,6 +29,7 @@ using Terrain = net::eagle0::shardok::storage::fb::Terrain;
using UnitView = net::eagle0::shardok::api::UnitView;
using Unit = net::eagle0::shardok::storage::fb::Unit;
using Units = flatbuffers::Vector<const Unit *>;
using std::vector;
struct alignas(16) AdjacentTile {
HexMapDirection directionTo;
@@ -58,7 +59,7 @@ public:
-> const CoordsSet &;
[[nodiscard]] static auto GetAdjacentTiles(const HexMap *map, const Coords &coords)
-> std::vector<AdjacentTile>;
-> vector<AdjacentTile>;
[[nodiscard]] static auto LineIsBlockedByMountains(
const HexMap *map,
@@ -77,7 +78,7 @@ auto HasForestAccess(
const Coords &coords,
const Units *units,
const HexMap *hexMap,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
PlayerId player) -> bool;
auto GetTerrain(const HexMap *map, const Coords &coords) -> const Terrain *;
@@ -123,10 +124,10 @@ static inline auto Occupant(const Units &units, const Coords &coords) -> const U
}
static inline auto Occupants(
const std::vector<const Unit *> &units,
const vector<const Unit *> &units,
const int rowCount,
const int columnCount) -> std::vector<const Unit *> {
std::vector<const Unit *> positions(rowCount * columnCount);
const int columnCount) -> vector<const Unit *> {
vector<const Unit *> positions(rowCount * columnCount);
for (const auto &unit : units) {
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) {
@@ -141,8 +142,8 @@ static inline auto Occupants(
static inline auto Occupants(
const flatbuffers::Vector<const Unit *> &units,
const int rowCount,
const int columnCount) -> std::vector<const Unit *> {
std::vector<const Unit *> positions(rowCount * columnCount);
const int columnCount) -> vector<const Unit *> {
vector<const Unit *> positions(rowCount * columnCount);
for (const auto &unit : units) {
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) {
@@ -154,7 +155,7 @@ static inline auto Occupants(
return positions;
}
static inline auto Occupant(const std::vector<const Unit *> &units, const Coords &coords)
static inline auto Occupant(const vector<const Unit *> &units, const Coords &coords)
-> const Unit * {
for (const auto &unit : units) {
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT &&
@@ -207,7 +208,7 @@ auto Occupant(const UnitContainer &units, const Coords &coords) -> std::optional
static inline auto FriendlyOccupant(
const PlayerId pid,
const Units *units,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> const Unit * {
const auto *occupant = Occupant(units, coords);
if (occupant) {
@@ -224,7 +225,7 @@ template<class U, class UnitContainer>
auto FriendlyOccupant(
PlayerId pid,
const UnitContainer &units,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> std::optional<U> {
auto occupantOptional = Occupant(units, coords);
if (occupantOptional.has_value()) {
@@ -240,14 +241,14 @@ auto FriendlyOccupant(
auto KnownEnemyOccupant(
PlayerId playerId,
const Units *units,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> const Unit *;
template<class UnitContainer>
auto KnownEnemyOccupant(
PlayerId playerId,
const UnitContainer &units,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> std::optional<typename UnitContainer::value_type> {
auto occupantOptional = Occupant(units, coords);
if (occupantOptional.has_value()) {
@@ -264,7 +265,7 @@ auto KnownEnemyOccupant(
[[nodiscard]] static inline auto KnownOccupant(
const PlayerId playerId,
const Units *units,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> const Unit * {
const auto *occupant = Occupant(units, coords);
if (occupant) {
@@ -281,8 +282,8 @@ auto KnownAdjacentEnemies(
PlayerId playerId,
const Units *units,
const HexMap *map,
const std::vector<PlayerId> &allyPids,
const Coords &coords) -> std::vector<const Unit *>;
const vector<PlayerId> &allyPids,
const Coords &coords) -> vector<const Unit *>;
auto DirectionsTo(int columnCount, const Coords &origin, const Coords &destination)
-> HexMapDirectionsTo;
@@ -41,7 +41,6 @@ auto AlliedPids(const GameState *gameState, int playerId) -> std::vector<PlayerI
std::vector<PlayerId> allyPids{};
if (pi->allies()) {
allyPids.reserve(pi->allies()->size());
for (const auto &ally : *pi->allies()) { allyPids.push_back(ally->player_id()); }
}
return allyPids;
@@ -121,7 +121,7 @@ auto AttackOrientationForAttack(
const PlayerId playerId,
const Units *units,
const HexMap *hexMap,
const std::vector<PlayerId> &allyPids) -> CoordsSet {
const vector<PlayerId> &allyPids) -> CoordsSet {
CoordsSet cs(hexMap); // NOLINT(readability-identifier-length)
for (const Unit *unit : *units) {
if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;
@@ -139,7 +139,7 @@ auto AttackOrientationForAttack(
const PlayerId playerId,
const Units *units,
const HexMap *hexMap,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> bool {
CoordsSet adjCoords = HexMapUtils::GetAdjacentCoords(hexMap, coords);
return std::any_of(std::begin(adjCoords), std::end(adjCoords), [&](const Coords &adjCoords) {
@@ -27,14 +27,14 @@ ExertsZoneOfControl(const SettingsGetter &settings, const Unit *unit, PlayerId t
PlayerId playerId,
const Units *units,
const HexMap *hexMap,
const std::vector<PlayerId> &allyPids) -> CoordsSet;
const vector<PlayerId> &allyPids) -> CoordsSet;
[[nodiscard]] auto IsInEnemyZoc(
const SettingsGetter &settings,
PlayerId playerId,
const Units *units,
const HexMap *hexMap,
const std::vector<PlayerId> &allyPids,
const vector<PlayerId> &allyPids,
const Coords &coords) -> bool;
[[nodiscard]] auto AttackOrientationForAttack(
@@ -93,9 +93,9 @@ auto TargetIsHiddenFromOpponents(const ActionResultType type) -> bool {
hiddenActor = unit.hidden();
}
}
wasHidden = beforeState && actorId >= 0 &&
wasHidden = beforeState &&
beforeState->units()->size() > static_cast<unsigned int>(actorId) &&
beforeState->units()->Get(actorId)->hidden();
beforeState->units()->Get(result.actor().value())->hidden();
}
const PlayerId actorPlayer = result.has_player() ? result.player().value() : -1;
const bool actorIsSelfOrAlly =
@@ -38,41 +38,10 @@ auto GameStateFilteredForPlayerWithAllies(
gsv.set_game_id(gameState->game_id()->str());
gsv.set_month(gameState->month());
gsv.mutable_player_infos()->Reserve(gameState->player_infos()->size());
for (const auto& info : *gameState->player_infos()) {
*gsv.add_player_infos() = fb::ToPlayerInfoProto(info);
}
int reserveUnitCount = 0;
int normalUnitCount = 0;
for (const auto& unit : *gameState->units()) {
switch (unit->status()) {
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_NEVER_ENTERED_UNIT:
++reserveUnitCount;
break;
case net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT:
++normalUnitCount;
break;
case net::eagle0::shardok::storage::fb::UnitStatus_DESTROYED_SUMMONED_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_FLED_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_EVACUATED_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_SECURED_UNIT:
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT:
case net::eagle0::shardok::storage::fb::UnitStatus_PENDING_REINFORCEMENT: break;
case net::eagle0::shardok::storage::fb::UnitStatus_UNKNOWN_UNIT:
throw ShardokInternalErrorException("Unknown unit status");
}
}
gsv.mutable_reserve_units()->Reserve(reserveUnitCount);
gsv.mutable_units()->Reserve(normalUnitCount);
for (const auto& unit : *gameState->units()) {
switch (unit->status()) {
case net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT:
@@ -111,14 +80,12 @@ auto GameStateFilteredForPlayerWithAllies(
if (gameState->eligible_charger_id() > -1) {
gsv.mutable_eligible_charger_id()->set_value(gameState->eligible_charger_id());
}
gsv.mutable_possible_chargee_ids()->Reserve(gameState->possible_chargee_ids()->size());
gsv.mutable_possible_chargee_ids()->Add(
gameState->possible_chargee_ids()->begin(),
gameState->possible_chargee_ids()->end());
*gsv.mutable_status() = fb::ToProto(gameState->status());
gsv.mutable_player_totals()->Reserve(gameState->player_infos()->size());
for (const auto* player : *gameState->player_infos()) {
auto* newTotal = gsv.add_player_totals();
newTotal->set_player_id(player->player_id());
@@ -59,7 +59,6 @@ auto GuessMeteorTarget(
// Build a set of valid coords within meteor range
std::vector<Coords> validCoordsInRange;
validCoordsInRange.reserve(1 + 3 * meteorRange * (meteorRange + 1));
for (const Cube &cube : CubesWithinDistance(mageCube, meteorRange)) {
Coords coords = CubeToOffset(cube);
if (CoordsAreValidProto(hexMap, coords) && coords != mageLocation) {
@@ -95,7 +94,6 @@ auto GuessMeteorTarget(
// Priority 3: Find a castle not occupied by the caster's player
// Build set of occupied coords by caster's player
std::unordered_set<int> casterOccupiedIndices;
casterOccupiedIndices.reserve(units.size());
for (const auto &unit : units) {
if (unit.player_id() == casterPlayerId) {
Coords loc = FromCoordsProto(unit.location());
@@ -156,7 +154,6 @@ auto GameStateGuesser::GuessedState(
auto gameIdOffset = fbb.CreateString(gameStateView.game_id());
std::vector<Offset<net::eagle0::shardok::storage::fb::PlayerInfo>> piVec{};
piVec.reserve(gameStateView.player_infos_size());
for (const PlayerInfoProto &pi : gameStateView.player_infos()) {
piVec.push_back(fb::FromPlayerInfoProto(fbb, pi));
}
@@ -164,12 +161,9 @@ auto GameStateGuesser::GuessedState(
auto statusDescription = fbb.CreateString(gameStateView.status().description());
std::vector<PlayerId> winningShardokIdsVec;
winningShardokIdsVec.reserve(10);
winningShardokIdsVec.insert(
winningShardokIdsVec.end(),
std::vector<PlayerId> winningShardokIdsVec = {
std::begin(gameStateView.status().winning_shardok_ids()),
std::end(gameStateView.status().winning_shardok_ids()));
std::end(gameStateView.status().winning_shardok_ids())};
for (int i = gameStateView.status().winning_shardok_ids_size(); i < 10; i++) {
winningShardokIdsVec.push_back(-1);
}
@@ -216,9 +210,6 @@ auto GameStateGuesser::GuessedState(
std::vector<UnitId> foundUnits{};
std::vector<Unit> unitsVec{};
const int knownUnitCount = gameStateView.units_size() + gameStateView.reserve_units_size();
foundUnits.reserve(knownUnitCount);
unitsVec.reserve(knownUnitCount + 6);
for (const auto &uv : gameStateView.units()) {
unitsVec.push_back(GuessedUnit(
settings,
@@ -353,10 +344,7 @@ auto GameStateGuesser::GuessedState(
auto unitsOffset = fbb.CreateVectorOfSortedStructs(&unitsVec);
std::vector<UnitId> chargeeIdsVec;
chargeeIdsVec.reserve(6);
chargeeIdsVec.insert(
chargeeIdsVec.end(),
auto chargeeIdsVec = std::vector<UnitId>(
std::begin(gameStateView.possible_chargee_ids()),
std::end(gameStateView.possible_chargee_ids()));
for (int i = gameStateView.possible_chargee_ids_size(); i < 6; i++) {
@@ -35,6 +35,8 @@ using net::eagle0::common::ShardokLatencyFields;
using net::eagle0::common::VictoryCondition;
using net::eagle0::shardok::api::PlacementCommand;
using net::eagle0::shardok::common::EndGameCondition;
using std::begin;
using std::end;
class NewGameException : public std::exception {
private:
@@ -85,7 +87,7 @@ auto EagleInterfaceImpl::ControllerForGame(const GameId &gameId, const GameSetup
}
EagleInterfaceImpl::EagleInterfaceImpl(
std::shared_ptr<ShardokGamesManager> manager,
shared_ptr<ShardokGamesManager> manager,
std::string authToken)
: gamesManager(std::move(manager)),
tokenValidator_(std::move(authToken)) {
@@ -117,7 +119,7 @@ auto ConvertPlayerInfo(
std::ranges::transform(
pi.units(),
std::back_inserter(shardokUnits),
[shardokPid, &allPids](const net::eagle0::common::CommonUnit &unit) {
[shardokPid, allPids](const net::eagle0::common::CommonUnit &unit) {
return ConvertUnit(unit, shardokPid, allPids);
});
@@ -219,7 +221,6 @@ void EagleInterfaceImpl::StartGame(const NewGameRequest &request) {
watcherAllies.reserve(request.watcher_eagle_faction_ids_size());
for (const int32_t watcherFid : request.watcher_eagle_faction_ids()) {
std::vector<PlayerId> allyPids;
allyPids.reserve(userInfoCount);
for (int i = 0; i < userInfoCount; i++) {
for (const auto &ally : request.user_infos(i).allies()) {
if (ally.eagle_faction_id() == watcherFid) {
@@ -259,10 +260,10 @@ auto EagleInterfaceImpl::PostCommand(
// Validate auth token
if (auto authStatus = tokenValidator_.ValidateOrStatus(context)) { return *authStatus; }
std::shared_ptr<ShardokGameController> controller;
shared_ptr<ShardokGameController> controller;
try {
controller = ControllerForGame(request->game_id(), request->game_setup_info());
} catch (const NewGameException &e) { return e.GetStatus(); }
} catch (NewGameException &e) { return e.GetStatus(); }
try {
{
ScopedShardokLatencyTrace trace(
@@ -290,13 +291,13 @@ auto EagleInterfaceImpl::PostCommand(
request->game_setup_info().known_result_count(),
response);
}
} catch (const ShardokClientErrorException &e) {
} catch (ShardokClientErrorException &e) {
std::cerr << "Bad request from client: " << e.what() << '\n';
return Status(StatusCode::INVALID_ARGUMENT, e.what());
} catch (const ShardokInternalErrorException &e) {
} catch (ShardokInternalErrorException &e) {
std::cerr << "Internal error: " << e.what() << '\n';
return Status(StatusCode::INTERNAL, e.what());
} catch (const std::exception &e) {
} catch (std::exception &e) {
std::cerr << "Unknown exception: " << e.what() << '\n';
return Status(StatusCode::INTERNAL, e.what());
}
@@ -320,10 +321,10 @@ auto EagleInterfaceImpl::PostPlacementCommands(
// Validate auth token
if (auto authStatus = tokenValidator_.ValidateOrStatus(context)) { return *authStatus; }
std::shared_ptr<ShardokGameController> controller;
shared_ptr<ShardokGameController> controller;
try {
controller = ControllerForGame(request->game_id(), request->game_setup_info());
} catch (const NewGameException &e) { return e.GetStatus(); }
} catch (NewGameException &e) { return e.GetStatus(); }
try {
vector<UnitPlacementInfo> upis;
upis.reserve(request->placement_commands().size());
@@ -359,13 +360,13 @@ auto EagleInterfaceImpl::PostPlacementCommands(
request->game_setup_info().known_result_count(),
response);
}
} catch (const ShardokClientErrorException &e) {
} catch (ShardokClientErrorException &e) {
std::cerr << "Bad request from client: " << e.what() << '\n';
return Status(StatusCode::INVALID_ARGUMENT, e.what());
} catch (const ShardokInternalErrorException &e) {
} catch (ShardokInternalErrorException &e) {
std::cerr << "Internal error: " << e.what() << '\n';
return Status(StatusCode::INTERNAL, e.what());
} catch (const std::exception &e) {
} catch (std::exception &e) {
std::cerr << "Unknown exception: " << e.what() << '\n';
return Status(StatusCode::INTERNAL, e.what());
}
@@ -374,7 +375,7 @@ auto EagleInterfaceImpl::PostPlacementCommands(
}
void EagleInterfaceImpl::PopulateGameStatusResponse(
std::shared_ptr<ShardokGameController> &controller,
shared_ptr<ShardokGameController> &controller,
const int64_t startingActionId,
GameStatusResponse *response) {
ScopedShardokLatencyTrace trace(
@@ -399,15 +400,15 @@ void EagleInterfaceImpl::PopulateGameStatusResponse(
}
} else {
response->mutable_game_update_response()->mutable_update_responses()->Add(
std::begin(results.mainResults),
std::end(results.mainResults));
begin(results.mainResults),
end(results.mainResults));
for (const auto &op : results.filteredResults) {
auto *newFilteredResponse =
response->mutable_game_update_response()->add_filtered_update_responses();
newFilteredResponse->set_eagle_faction_id(op.eagleFactionId);
newFilteredResponse->mutable_action_result_views()->Add(
std::begin(op.resultViews),
std::end(op.resultViews));
begin(op.resultViews),
end(op.resultViews));
if (op.availableCommands) {
*newFilteredResponse->mutable_available_commands() = *op.availableCommands;
@@ -466,18 +467,16 @@ auto EagleInterfaceImpl::SubscribeToGame(
// Validate auth token
if (auto authStatus = tokenValidator_.ValidateOrStatus(context)) { return *authStatus; }
std::shared_ptr<ShardokGameController> controller;
shared_ptr<ShardokGameController> controller;
try {
controller = ControllerForGame(request->game_id(), request->game_setup_info());
} catch (const NewGameException &e) {
return e.GetStatus();
} catch (const ShardokClientErrorException &e) {
} catch (NewGameException &e) { return e.GetStatus(); } catch (ShardokClientErrorException &e) {
std::cerr << "Bad request from client: " << e.what() << '\n';
return Status(StatusCode::INVALID_ARGUMENT, e.what());
} catch (const ShardokInternalErrorException &e) {
} catch (ShardokInternalErrorException &e) {
std::cerr << "Internal error: " << e.what() << '\n';
return Status(StatusCode::INTERNAL, e.what());
} catch (const std::exception &e) {
} catch (std::exception &e) {
std::cerr << "Unknown exception in SubscribeToGame: " << e.what() << '\n';
return Status(StatusCode::INTERNAL, e.what());
}
@@ -546,8 +545,8 @@ auto EagleInterfaceImpl::SubscribeToGame(
response.set_game_id(gameId_);
response.mutable_game_update_response()->mutable_update_responses()->Add(
std::begin(mainResults),
std::end(mainResults));
begin(mainResults),
end(mainResults));
// Add filtered results for each player with their faction IDs
for (const auto &playerUpdate : filteredResults) {
@@ -555,8 +554,8 @@ auto EagleInterfaceImpl::SubscribeToGame(
response.mutable_game_update_response()->add_filtered_update_responses();
filtered->set_eagle_faction_id(playerUpdate.eagleFactionId);
filtered->mutable_action_result_views()->Add(
std::begin(playerUpdate.resultViews),
std::end(playerUpdate.resultViews));
begin(playerUpdate.resultViews),
end(playerUpdate.resultViews));
if (playerUpdate.availableCommands) {
*filtered->mutable_available_commands() = *playerUpdate.availableCommands;
}
@@ -42,6 +42,7 @@ using net::eagle0::common::NewGameRequest;
using net::eagle0::common::PlacementCommandsRequest;
using net::eagle0::common::PostCommandRequest;
using net::eagle0::common::ShardokInternalInterface;
using std::shared_ptr;
class EagleInterfaceImpl final : public ShardokInternalInterface::Service {
private:
@@ -53,7 +54,7 @@ private:
void StartGame(const NewGameRequest& newGameRequest);
void PopulateGameStatusResponse(
std::shared_ptr<ShardokGameController>& controller,
shared_ptr<ShardokGameController>& controller,
int64_t startingActionId,
GameStatusResponse* response);
@@ -8,11 +8,10 @@
#include "ServerConfiguration.hpp"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
@@ -51,14 +50,12 @@ ServerConfiguration::ServerConfiguration(const std::string& filePath) {
}
std::ifstream istr(filePath);
std::string line;
while (std::getline(istr, line)) {
std::ranges::replace(line, '=', ' ');
std::istringstream lineStream(line);
std::string key;
std::string value;
if (!(lineStream >> key >> value)) { continue; }
char line[256];
while (!istr.eof()) {
istr.getline(line, 256);
if (strnlen(line, 256) < 3) continue;
std::string key = strtok(line, " =");
std::string value = strtok(nullptr, " =");
configValues[key] = value;
}
@@ -16,6 +16,7 @@
#include "ServerConfiguration.hpp"
#include "src/main/cpp/net/eagle0/common/UnitConversions.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/ShardokAIClient.hpp"
#include "src/main/cpp/net/eagle0/shardok/controller/ShardokGameController.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/settings_loader/SettingsLoader.hpp"
#include "src/main/cpp/net/eagle0/shardok/util/MapLoader.hpp"
@@ -51,21 +52,6 @@ ShardokGamesManager::ShardokGamesManager(const std::vector<std::string> &extraSe
}
}
auto TutorialReinforcementUnitCount(const TutorialBattleConfigProto &tutorialConfig) -> size_t {
if (!tutorialConfig.enabled()) { return 0; }
size_t unitCount = 0;
for (const auto &event : tutorialConfig.events()) {
if (!event.has_action() || event.action().action_type_case() !=
net::eagle0::common::TutorialAction::kReinforcements) {
continue;
}
unitCount += event.action().reinforcements().units_size();
}
return unitCount;
}
auto ShardokGamesManager::GetController(const GameId &gameId)
-> std::shared_ptr<ShardokGameController> {
std::shared_lock<std::shared_mutex> lk(runningControllersLock);
@@ -119,7 +105,6 @@ auto ShardokGamesManager::UnlockedCreateSpecifiedGame(
}
std::vector<PlayerInfoProto> playerInfos{};
playerInfos.reserve(playerSetupInfos.size());
std::transform(
std::begin(playerSetupInfos),
std::end(playerSetupInfos),
@@ -127,9 +112,6 @@ auto ShardokGamesManager::UnlockedCreateSpecifiedGame(
[](const PlayerInfoWithUnits &psi) { return psi.playerInfo; });
std::vector<Unit> units{};
size_t unitCount = 0;
for (const auto &info : playerSetupInfos) { unitCount += info.units.size(); }
units.reserve(unitCount);
for (const auto &info : playerSetupInfos) {
units.insert(std::end(units), std::begin(info.units), std::end(info.units));
}
@@ -153,6 +135,8 @@ auto ShardokGamesManager::LockedCreateGame(
const std::shared_ptr<ShardokGameController> &controller,
const std::vector<PlayerInfoWithUnits> & /*playerInfos*/,
const std::string & /*serializedRequest*/) -> GameId {
std::vector<std::shared_ptr<ShardokAIClient>> aiClients{};
GameId newGameId = controller->GetGameId();
runningControllers[newGameId] = controller;
@@ -173,7 +157,6 @@ auto SetUpController(
const std::vector<std::pair<int32_t, std::vector<PlayerId>>> &watcherAllies)
-> std::shared_ptr<ShardokGameController> {
auto unplacedUnits = std::vector<Unit>();
unplacedUnits.reserve(units.size() + TutorialReinforcementUnitCount(tutorialConfig));
UnitId nextUnitId = 0;
for (auto up : units) {
@@ -197,7 +180,7 @@ auto SetUpController(
}
}
unplacedUnits.push_back(std::move(up));
unplacedUnits.push_back(up);
}
// Add reinforcement units from tutorial config as PENDING_REINFORCEMENT.
@@ -205,7 +188,6 @@ auto SetUpController(
// until the TutorialBattleController activates them at the triggered round.
if (tutorialConfig.enabled()) {
std::vector<PlayerId> allPlayerIds;
allPlayerIds.reserve(playerInfos.size());
for (size_t i = 0; i < playerInfos.size(); i++) {
allPlayerIds.push_back(static_cast<PlayerId>(i));
}
@@ -224,7 +206,7 @@ auto SetUpController(
unit.mutate_unit_id(nextUnitId++);
unit.mutate_status(
net::eagle0::shardok::storage::fb::UnitStatus_PENDING_REINFORCEMENT);
unplacedUnits.push_back(std::move(unit));
unplacedUnits.push_back(unit);
}
}
}
@@ -236,7 +218,7 @@ auto SetUpController(
effectiveSettings->GetSetter().SetInt(kSettingsKeys.duelBaseAcceptOdds, 200);
}
std::unique_ptr<ShardokEngine> engine = std::make_unique<ShardokEngine>(
unique_ptr<ShardokEngine> engine = std::make_unique<ShardokEngine>(
effectiveSettings,
std::move(unplacedUnits),
customMapBytes.empty() ? LoadMap(mapName) : LoadMapFromBytes(customMapBytes),
@@ -38,10 +38,10 @@ auto GetMapNames() -> std::vector<std::string> {
kShardokMapExtension);
std::vector<std::string> names{};
names.reserve(mapFileNames.size());
for (const auto& fileName : mapFileNames) {
names.push_back(fileName.substr(0, fileName.size() - kShardokMapExtension.size()));
const size_t lastindex = fileName.find_last_of('.');
names.push_back(fileName.substr(0, lastindex));
}
return names;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
<linker>
<assembly fullname="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="eagle.AnimalEffect" preserve="all" />
<type fullname="eagle.BeastsEffect" preserve="all" />
<type fullname="eagle.DragonEffect" preserve="all" />
<type fullname="eagle.MonsterEffect" preserve="all" />
</assembly>
<assembly fullname="Unity.2D.Animation.Runtime, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="UnityEngine.U2D.Animation.SpriteSkin" preserve="all" />
</assembly>
<assembly fullname="Unity.Addressables, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" preserve="all">
<type fullname="UnityEngine.AddressableAssets.Addressables" preserve="all" />
</assembly>
<assembly fullname="Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" preserve="all">
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider" preserve="all" />
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider" preserve="all" />
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.InstanceProvider" preserve="all" />
<type fullname="UnityEngine.ResourceManagement.ResourceProviders.SceneProvider" preserve="all" />
</assembly>
<assembly fullname="UnityEngine.AnimationModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="UnityEngine.Animation" preserve="all" />
<type fullname="UnityEngine.AnimationClip" preserve="all" />
<type fullname="UnityEngine.Animator" preserve="all" />
<type fullname="UnityEngine.Avatar" preserve="all" />
<type fullname="UnityEngine.RuntimeAnimatorController" preserve="all" />
</assembly>
<assembly fullname="UnityEngine.AudioModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="UnityEngine.AudioClip" preserve="all" />
</assembly>
<assembly fullname="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="UnityEngine.GameObject" preserve="all" />
<type fullname="UnityEngine.Material" preserve="all" />
<type fullname="UnityEngine.Mesh" preserve="all" />
<type fullname="UnityEngine.MeshFilter" preserve="all" />
<type fullname="UnityEngine.MeshRenderer" preserve="all" />
<type fullname="UnityEngine.Object" preserve="all" />
<type fullname="UnityEngine.RectTransform" preserve="all" />
<type fullname="UnityEngine.Shader" preserve="all" />
<type fullname="UnityEngine.SkinnedMeshRenderer" preserve="all" />
<type fullname="UnityEngine.Sprite" preserve="all" />
<type fullname="UnityEngine.SpriteRenderer" preserve="all" />
<type fullname="UnityEngine.Texture2D" preserve="all" />
<type fullname="UnityEngine.Transform" preserve="all" />
</assembly>
<assembly fullname="UnityEngine.ParticleSystemModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="UnityEngine.ParticleSystem" preserve="all" />
<type fullname="UnityEngine.ParticleSystemRenderer" preserve="all" />
</assembly>
<assembly fullname="UnityEngine.PhysicsModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="UnityEngine.BoxCollider" preserve="all" />
<type fullname="UnityEngine.CapsuleCollider" preserve="all" />
<type fullname="UnityEngine.Rigidbody" preserve="all" />
</assembly>
</linker>
@@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: dd998d23eb8ce477b9a60c6a6f216ede
PrefabImporter:
guid: e1726d04d53cd49ea89c9f2e40f02676
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
@@ -3,7 +3,7 @@
/// This file is auto-generated by BuildInfoGenerator - do not edit manually.
/// </summary>
public static class BuildInfo {
public const string GitCommitHash = "development";
public const string GitCommitShort = "dev";
public const string BuildTimestamp = "";
public const string GitCommitHash = "c97c1411559f13cfdb5620f98d05186c006b7c35";
public const string GitCommitShort = "c97c141";
public const string BuildTimestamp = "2026-06-22 16:04:42 UTC";
}
@@ -38,10 +38,8 @@ public class AvailableGameItem : MonoBehaviour {
}
}
private void Awake() {
TouchTargetUtils.EnsureMinimumTouchTargets(joinButton, dropButton);
TouchTargetUtils.EnsureMinimumTouchHeight(heroDropdownController);
}
// Use this for initialization
void Start() {}
public void JoinClicked() {
// Track multiplayer status for tutorial system
@@ -206,7 +206,6 @@ public class ConnectionHandler : MonoBehaviour, ILobbySubscriber, IDisposable {
void Start() {
MainQueue.EnsureExists();
EnsureOAuthManager();
EnsureConnectionTouchTargets();
var currentResolution = Screen.currentResolution;
var resolutions = Screen.resolutions;
@@ -240,24 +239,6 @@ public class ConnectionHandler : MonoBehaviour, ILobbySubscriber, IDisposable {
TryRestoreSession();
}
private void EnsureConnectionTouchTargets() {
TouchTargetUtils.EnsureMinimumTouchHeight(resolutionDropdown);
TouchTargetUtils.EnsureMinimumTouchTargets(
discordLoginButton,
googleLoginButton,
githubLoginButton,
appleLoginButton,
steamLoginButton,
twitchLoginButton,
connectToQAButton,
logoutButton,
customBattleButton,
cancelCustomBattleButton,
lobbyConnectToQAButton,
startTutorialButton);
TouchTargetUtils.EnsureMinimumTouchHeight(tutorialPhaseDropdown);
}
private static void EnsureOAuthManager() {
if (OAuthManager.Instance != null) return;
@@ -22,12 +22,6 @@ public class CreateGameItem : MonoBehaviour {
private List<AvailableLeader> availableLeaders;
private void Awake() {
TouchTargetUtils.EnsureMinimumTouchTarget(createButton);
TouchTargetUtils.EnsureMinimumTouchHeight(heroDropdownController);
TouchTargetUtils.EnsureMinimumTouchHeight(totalHumansDropdown);
}
public string SelectedLeaderTextId {
get {
if (heroDropdownController.SelectedHeroId == 0) {
@@ -38,11 +38,6 @@ public class CustomBattleHandler : MonoBehaviour, IClientConnectionSubscriber {
private ShardokGameModel _shardokModel;
private void Awake() {
TouchTargetUtils.EnsureMinimumTouchTarget(defenderToggle);
TouchTargetUtils.EnsureMinimumTouchHeights(mapDropdown, monthDropdown);
}
public void SetActive(
EagleConnection conn,
PersistentClientConnection persistentClientConnection) {

Some files were not shown because too many files have changed in this diff Show More