mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Avoid copying fire-free maps for water crossing (#8775)
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "AIWaterCrossingCalculator.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "AIMinimumDistanceAndTarget.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/HexMapHelpers.hpp"
|
||||
@@ -22,6 +23,19 @@ auto RecomputeModifierHash(HexMap *hexMap) -> MapId {
|
||||
return ActionPointDistancesCache::GetMapId(hexMap);
|
||||
}
|
||||
|
||||
auto HasNonWaterFire(const HexMap *hexMap) -> bool {
|
||||
return std::ranges::any_of(*hexMap->terrain(), [](const auto *terrain) {
|
||||
return !IsWater(terrain->type()) && terrain->modifier().fire().present();
|
||||
});
|
||||
}
|
||||
|
||||
auto CanReachWithDistances(
|
||||
const Coords &origin,
|
||||
const Coords &destination,
|
||||
const ActionPointDistances *distances) -> bool {
|
||||
return distances->Distance(origin, destination) != ActionPointDistances::IMPOSSIBLE;
|
||||
}
|
||||
|
||||
auto CanCreateWaterCrossing(
|
||||
const net::eagle0::shardok::storage::fb::Unit *unit,
|
||||
const BattalionTypeGetter &battalionTypeGetter) -> bool {
|
||||
@@ -43,19 +57,36 @@ auto UnitIdsRequiringWaterCrossing(
|
||||
const CoordsSet &destinations,
|
||||
const APDCache &apdCache,
|
||||
const BattalionTypeGetter &battalionTypeGetter) -> vector<UnitId> {
|
||||
// Put out all the fires, except on bridges
|
||||
fb::HexMapW mapCopy = fb::CopyHexMap(gameState->hex_map());
|
||||
for (uint32_t index = 0; index < mapCopy->terrain()->size(); ++index) {
|
||||
if (IsWater(mapCopy->terrain()->Get(index)->type())) continue;
|
||||
// const_cast is safe because we own the mutable buffer (mapCopy)
|
||||
const_cast<net::eagle0::shardok::storage::fb::Terrain *>(
|
||||
mapCopy->mutable_terrain()->GetMutableObject(index))
|
||||
->mutable_modifier()
|
||||
.mutable_fire()
|
||||
.mutate_present(false);
|
||||
const HexMap *map = gameState->hex_map();
|
||||
MapId mapId = ActionPointDistancesCache::GetMapId(map);
|
||||
|
||||
// Most states have no relevant fire, so avoid copying the entire map unless it needs mutation.
|
||||
fb::HexMapW fireClearedMap;
|
||||
if (HasNonWaterFire(map)) {
|
||||
fireClearedMap = fb::CopyHexMap(map);
|
||||
for (uint32_t index = 0; index < fireClearedMap->terrain()->size(); ++index) {
|
||||
if (IsWater(fireClearedMap->terrain()->Get(index)->type())) continue;
|
||||
// const_cast is safe because we own the mutable buffer (fireClearedMap)
|
||||
const_cast<net::eagle0::shardok::storage::fb::Terrain *>(
|
||||
fireClearedMap->mutable_terrain()->GetMutableObject(index))
|
||||
->mutable_modifier()
|
||||
.mutable_fire()
|
||||
.mutate_present(false);
|
||||
}
|
||||
|
||||
map = fireClearedMap.Get();
|
||||
mapId = RecomputeModifierHash(fireClearedMap.Get());
|
||||
}
|
||||
|
||||
const auto mapId = RecomputeModifierHash(mapCopy.Get());
|
||||
std::array<const ActionPointDistances *, BattalionTypeId::BattalionTypeId_MAX + 1>
|
||||
distancesByBattalionType{};
|
||||
const auto distancesFor = [&](const BattalionTypeSPtr &battalionType) {
|
||||
auto &distances = distancesByBattalionType[battalionType->typeId];
|
||||
if (distances == nullptr) {
|
||||
distances = apdCache->GetRaw(map, mapId, battalionType, false);
|
||||
}
|
||||
return distances;
|
||||
};
|
||||
|
||||
vector<UnitId> unitIdsToReturn{};
|
||||
|
||||
@@ -67,8 +98,9 @@ auto UnitIdsRequiringWaterCrossing(
|
||||
const auto &battType = battalionTypeGetter(unit->battalion().type());
|
||||
|
||||
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) {
|
||||
const auto *distances = distancesFor(battType);
|
||||
for (const Coords &destination : destinations) {
|
||||
if (!CanReach(unit->location(), destination, mapCopy, mapId, apdCache, battType)) {
|
||||
if (!CanReachWithDistances(unit->location(), destination, distances)) {
|
||||
unitIdsToReturn.push_back(unit->unit_id());
|
||||
break;
|
||||
}
|
||||
@@ -76,6 +108,7 @@ auto UnitIdsRequiringWaterCrossing(
|
||||
} else if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT) {
|
||||
if (startingPositionIndices[unit->starting_position_index()] == -1) {
|
||||
startingPositionIndices[unit->starting_position_index()] = 0;
|
||||
const auto *distances = distancesFor(battType);
|
||||
const auto *startingPositions = gameState->hex_map()
|
||||
->attacker_starting_positions()
|
||||
->Get(unit->starting_position_index())
|
||||
@@ -83,7 +116,7 @@ auto UnitIdsRequiringWaterCrossing(
|
||||
|
||||
for (const Coords *pos : *startingPositions) {
|
||||
for (const Coords &destination : destinations) {
|
||||
if (!CanReach(*pos, destination, mapCopy, mapId, apdCache, battType)) {
|
||||
if (!CanReachWithDistances(*pos, destination, distances)) {
|
||||
startingPositionIndices[unit->starting_position_index()] = 1;
|
||||
break;
|
||||
}
|
||||
@@ -128,10 +161,10 @@ auto CanReach(
|
||||
const MapId &mapId,
|
||||
const APDCache &apdCache,
|
||||
const BattalionTypeSPtr &battalionType) -> bool {
|
||||
const DIST_T startingDistance =
|
||||
apdCache->GetRaw(hexMap, mapId, battalionType, false)->Distance(origin, destination);
|
||||
|
||||
return startingDistance != ActionPointDistances::IMPOSSIBLE;
|
||||
return CanReachWithDistances(
|
||||
origin,
|
||||
destination,
|
||||
apdCache->GetRaw(hexMap, mapId, battalionType, false));
|
||||
}
|
||||
|
||||
// Returns the *non*-water tiles from which you could bridge/freeze water to allow a unit to cross
|
||||
|
||||
@@ -165,7 +165,8 @@ TEST_F(AIWaterCrossingCalculatorTest,
|
||||
battalionTypeGetter);
|
||||
EXPECT_EQ(1, ActionPointDistancesCache::GetThreadLocalCacheSize());
|
||||
|
||||
MutableTerrain(hexMap, Coords(0, 0))->mutable_modifier().mutable_fire().mutate_present(true);
|
||||
auto* fireTerrain = MutableTerrain(hexMap, Coords(0, 0));
|
||||
fireTerrain->mutable_modifier().mutable_fire().mutate_present(true);
|
||||
hexMap->mutate_modifier_hash(0);
|
||||
hexMap->mutate_modifier_hash(ActionPointDistancesCache::GetMapId(hexMap).modifierId);
|
||||
|
||||
@@ -177,6 +178,7 @@ TEST_F(AIWaterCrossingCalculatorTest,
|
||||
battalionTypeGetter);
|
||||
EXPECT_EQ(beforeFire, afterFire);
|
||||
EXPECT_EQ(1, ActionPointDistancesCache::GetThreadLocalCacheSize());
|
||||
EXPECT_TRUE(fireTerrain->modifier().fire().present());
|
||||
}
|
||||
|
||||
TEST_F(AIWaterCrossingCalculatorTest, CanReach_sameSide_returnsTrue) {
|
||||
|
||||
Reference in New Issue
Block a user