mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Cache player roles during AI scoring (#8793)
* Cache player roles during AI scoring * Use dense player IDs for AI scoring
This commit is contained in:
@@ -7,7 +7,6 @@
|
||||
#include <array>
|
||||
|
||||
#include "AIScoreCalculatorSharedUtilities.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIStrategy.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIUnitScoreCalculator.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIWaterCrossingCalculator.hpp"
|
||||
@@ -92,13 +91,24 @@ auto AbstractAIScoreCalculator::CalculateUnitsScoreComponents(
|
||||
|
||||
auto occupants = Occupants(*cachedUnits, cachedRowCount, cachedColumnCount);
|
||||
|
||||
// StartGame assigns real player IDs densely from zero, matching their player_infos indexes.
|
||||
// Use that invariant instead of scanning PlayerInfo for every unit or initializing a separate
|
||||
// lookup table for every evaluated state.
|
||||
const auto *playerInfos = gameStateRawPtr->player_infos();
|
||||
if (playerInfos == nullptr) { return UnitsScoreComponents{0.0, 0.0}; }
|
||||
|
||||
for (const Unit *unit : *cachedUnits) {
|
||||
const auto *pi = PlayerInfoForPid(gameState, unit->player_id());
|
||||
if (pi == nullptr) { continue; }
|
||||
const PlayerId playerId = unit->player_id();
|
||||
if (playerId < 0 || static_cast<flatbuffers::uoffset_t>(playerId) >= playerInfos->size()) {
|
||||
continue;
|
||||
}
|
||||
const auto *playerInfo = playerInfos->Get(static_cast<flatbuffers::uoffset_t>(playerId));
|
||||
if (playerInfo->player_id() != playerId) { continue; }
|
||||
const bool isDefender = playerInfo->is_defender();
|
||||
|
||||
switch (unit->status()) {
|
||||
case net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT: {
|
||||
if (pi->is_defender()) {
|
||||
if (isDefender) {
|
||||
defenderUnits.push_back(unit);
|
||||
} else {
|
||||
attackerUnits.push_back(unit);
|
||||
@@ -109,7 +119,7 @@ auto AbstractAIScoreCalculator::CalculateUnitsScoreComponents(
|
||||
double thisScore = unit->has_attached_hero() && unit->attached_hero().is_vip()
|
||||
? CAPTURED_VIP_SCORE
|
||||
: CAPTURED_UNIT_SCORE;
|
||||
if (pi->is_defender()) {
|
||||
if (isDefender) {
|
||||
defenderUnitsValue += thisScore;
|
||||
} else {
|
||||
attackerUnitsValue += thisScore;
|
||||
|
||||
@@ -48,7 +48,7 @@ protected:
|
||||
}
|
||||
|
||||
// Helper to create a running game state
|
||||
auto CreateRunningGameState() -> GameStateW {
|
||||
auto CreateRunningGameState(const std::vector<Unit>& additionalUnits = {}) -> GameStateW {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
fbb.ForceDefaults(true);
|
||||
|
||||
@@ -64,6 +64,7 @@ protected:
|
||||
AddGenericUnit(0, 0, Coords(0, 0)), // Attacker unit
|
||||
AddGenericUnit(1, 1, Coords(5, 5)) // Defender unit
|
||||
};
|
||||
units.insert(units.end(), additionalUnits.begin(), additionalUnits.end());
|
||||
const auto unitsOffset = fbb.CreateVectorOfSortedStructs(&units);
|
||||
|
||||
net::eagle0::shardok::storage::fb::GameStateBuilder gsb(fbb);
|
||||
@@ -96,6 +97,22 @@ TEST_F(MCTSOptimizedAIScoreCalculatorTest, NonTerminalState_ScoreIsBounded) {
|
||||
EXPECT_LE(score, 200.0) << "Score should be bounded (<= 200)";
|
||||
}
|
||||
|
||||
TEST_F(MCTSOptimizedAIScoreCalculatorTest, UnitsWithoutPlayerInfoAreIgnored) {
|
||||
auto reservedUnit = AddGenericUnit(-1, 2, Coords(0, 1));
|
||||
reservedUnit.mutate_status(net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT);
|
||||
auto uncontrolledUnit = AddGenericUnit(UNCONTROLLED_PLAYER_ID, 3, Coords(0, 2));
|
||||
uncontrolledUnit.mutate_status(
|
||||
net::eagle0::shardok::storage::fb::UnitStatus_DESTROYED_SUMMONED_UNIT);
|
||||
|
||||
const auto baseState = CreateRunningGameState();
|
||||
const auto stateWithSpecialPlayerIds = CreateRunningGameState({reservedUnit, uncontrolledUnit});
|
||||
const CoordsSet castleCoords(BASIC_MAP_FB);
|
||||
|
||||
EXPECT_DOUBLE_EQ(
|
||||
scorer->GuessedStateScore(false, baseState, strategy, castleCoords),
|
||||
scorer->GuessedStateScore(false, stateWithSpecialPlayerIds, strategy, castleCoords));
|
||||
}
|
||||
|
||||
// Test with stronger attacker
|
||||
TEST_F(MCTSOptimizedAIScoreCalculatorTest, StrongerAttacker_PositiveScore) {
|
||||
// Create state with multiple attacker units vs one defender
|
||||
|
||||
Reference in New Issue
Block a user