Add AI follow-up scoring experiment switch

This commit is contained in:
2026-07-07 08:03:49 -07:00
parent 91ec899247
commit 560ceb8097
10 changed files with 40 additions and 16 deletions
@@ -155,10 +155,12 @@ static auto LeafMoveFollowUpScoreBonus(
AICommandEvaluator::AICommandEvaluator(
const AIScoreCalculator& scorer,
const APDCache& apdCache,
BattalionTypeGetter battalionTypeGetter)
BattalionTypeGetter battalionTypeGetter,
const bool scoreMoveFollowUps)
: scorer_(scorer),
apdCache_(apdCache),
battalionTypeGetter_(std::move(battalionTypeGetter)) {}
battalionTypeGetter_(std::move(battalionTypeGetter)),
scoreMoveFollowUps_(scoreMoveFollowUps) {}
auto AICommandEvaluator::PerformLookahead(
const PlayerId pid,
@@ -293,7 +295,8 @@ auto AICommandEvaluator::EvaluateWithRandomness(
const auto guessedCommands = guessedEngine.GetAvailableCommandsForAIPlayer(pid);
const ScoreValue leafMoveFollowUpBonus =
remainingLookahead <= 0 ? LeafMoveFollowUpScoreBonus(guessedEngine, pid, commandIndex)
scoreMoveFollowUps_ && remainingLookahead <= 0
? LeafMoveFollowUpScoreBonus(guessedEngine, pid, commandIndex)
: 0.0;
auto innerEngine = std::make_shared<ShardokEngine>(guessedEngine, false);
try {
@@ -35,7 +35,8 @@ public:
AICommandEvaluator(
const AIScoreCalculator& scorer,
const APDCache& apdCache,
BattalionTypeGetter battalionTypeGetter); // Pass by value
BattalionTypeGetter battalionTypeGetter,
bool scoreMoveFollowUps = true); // Pass by value
/// Evaluates the score for a particular command index with lookahead.
struct EvaluationResult {
@@ -79,6 +80,7 @@ private:
const AIScoreCalculator& scorer_;
const APDCache& apdCache_;
BattalionTypeGetter battalionTypeGetter_;
bool scoreMoveFollowUps_;
struct ImmediateAndLookaheadScore {
ScoreValue immediateScore{};
@@ -44,14 +44,16 @@ IterativeDeepeningAI::IterativeDeepeningAI(
const CoordsSet& castleCoords,
const AIScoreCalculator& scorer,
const APDCache& apdCache,
BattalionTypeGetter battalionTypeGetter)
BattalionTypeGetter battalionTypeGetter,
const bool scoreMoveFollowUps)
: playerId(playerId),
isDefender(isDefender),
strategy(std::move(strategy)),
castleCoords(castleCoords),
scorer(scorer),
apdCache(apdCache),
battalionTypeGetter(std::move(battalionTypeGetter)) {}
battalionTypeGetter(std::move(battalionTypeGetter)),
scoreMoveFollowUps(scoreMoveFollowUps) {}
auto IterativeDeepeningAI::IterativeSearch(
const GameSettingsSPtr& settings,
@@ -333,7 +335,7 @@ auto IterativeDeepeningAI::SearchCommandAtDepthWithEngine(
const auto deadline = startTime + timeBudget.remainingBudget;
// Create command evaluator for lookahead search
AICommandEvaluator evaluator(scorer, apdCache, battalionTypeGetter);
AICommandEvaluator evaluator(scorer, apdCache, battalionTypeGetter, scoreMoveFollowUps);
// Get the future from EvaluateCommand - don't wait yet
// Note: EvaluateCommand expects remainingLookahead, not desiredDepth
@@ -64,7 +64,8 @@ public:
const CoordsSet& castleCoords,
const AIScoreCalculator& scorer,
const APDCache& apdCache,
BattalionTypeGetter battalionTypeGetter); // Pass by value
BattalionTypeGetter battalionTypeGetter,
bool scoreMoveFollowUps); // Pass by value
[[nodiscard]] SearchResult IterativeSearch(
const GameSettingsSPtr& settings,
@@ -80,6 +81,7 @@ private:
const AIScoreCalculator& scorer;
const APDCache& apdCache;
BattalionTypeGetter battalionTypeGetter;
bool scoreMoveFollowUps;
// Reusable vectors to reduce memory allocations
mutable std::vector<std::vector<ScoreValue>> scoresByDepth;
@@ -65,12 +65,14 @@ ShardokAIClient::ShardokAIClient(
const SettingsGetter &settings,
const AIAlgorithmType aiAlgorithmType,
const ScoringCalculatorType scoringCalculatorType,
const mcts::MCTSConfig &mctsConfig)
const mcts::MCTSConfig &mctsConfig,
const bool scoreMoveFollowUps)
: playerId(playerId),
isDefender(isDefender),
isAllAiBattle(isAllAiBattle),
aiAlgorithmType(aiAlgorithmType),
scoringCalculatorType(scoringCalculatorType),
scoreMoveFollowUps(scoreMoveFollowUps),
alCache(std::make_unique<AttackLocationsCache>(hexMap, settings)),
waterCrossingCommandChooser(playerId, apdCache),
mctsConfig(mctsConfig) {
@@ -365,7 +367,8 @@ auto ShardokAIClient::StandardChooseCommandIndex(
castleCoords,
*scorer,
apdCache,
battalionTypeGetter);
battalionTypeGetter,
scoreMoveFollowUps);
search_result =
ai.IterativeSearch(settings, guessedState, realAvailableCommands, timeBudget);
}
@@ -44,6 +44,7 @@ private:
const bool isAllAiBattle; // Whether this battle has only AI players (for faster time budgets)
const AIAlgorithmType aiAlgorithmType;
const ScoringCalculatorType scoringCalculatorType;
const bool scoreMoveFollowUps;
APDCache apdCache = std::make_shared<ActionPointDistancesCache>();
ALCache alCache;
@@ -75,7 +76,8 @@ public:
const SettingsGetter& settings,
AIAlgorithmType aiAlgorithmType,
ScoringCalculatorType scoringCalculatorType,
const mcts::MCTSConfig& mctsConfig);
const mcts::MCTSConfig& mctsConfig,
bool scoreMoveFollowUps = true);
~ShardokAIClient() = default;
[[nodiscard]] auto GetPlayerId() const -> PlayerId { return playerId; }
@@ -571,6 +571,8 @@ std::unique_ptr<ShardokAIClient> AiBattleSimulator::CreateAIClient(
const ScoringCalculatorType scoringType =
ConvertScoringCalculatorType(playerConfig.scoring_calculator());
const auto mctsConfig = BuildMCTSConfig(playerConfig, config_.random_seed(), playerId);
const bool scoreMoveFollowUps =
!playerConfig.has_score_move_follow_ups() || playerConfig.score_move_follow_ups();
return ai_testing_common::AIClientFactory::Create(
playerId,
@@ -580,7 +582,8 @@ std::unique_ptr<ShardokAIClient> AiBattleSimulator::CreateAIClient(
algorithmType,
scoringType,
true,
mctsConfig);
mctsConfig,
scoreMoveFollowUps);
}
BattleResult AiBattleSimulator::RunSetupPhase(
@@ -17,7 +17,8 @@ auto AIClientFactory::Create(
AIAlgorithmType algorithmType,
ScoringCalculatorType scoringType,
bool isAllAiBattle,
mcts::MCTSConfig mctsConfig) -> std::unique_ptr<ShardokAIClient> {
mcts::MCTSConfig mctsConfig,
const bool scoreMoveFollowUps) -> std::unique_ptr<ShardokAIClient> {
return std::make_unique<ShardokAIClient>(
playerId,
isDefender,
@@ -26,7 +27,8 @@ auto AIClientFactory::Create(
settings,
algorithmType,
scoringType,
mctsConfig);
mctsConfig,
scoreMoveFollowUps);
}
} // namespace shardok::ai_testing_common
@@ -62,7 +62,8 @@ public:
AIAlgorithmType algorithmType = AIAlgorithmType::ITERATIVE_DEEPENING,
ScoringCalculatorType scoringType = ScoringCalculatorType::STANDARD,
bool isAllAiBattle = false,
mcts::MCTSConfig mctsConfig = mcts::MCTSConfig{}) -> std::unique_ptr<ShardokAIClient>;
mcts::MCTSConfig mctsConfig = mcts::MCTSConfig{},
bool scoreMoveFollowUps = true) -> std::unique_ptr<ShardokAIClient>;
};
} // namespace ai_testing_common
@@ -126,6 +126,10 @@ message PlayerConfig {
// MCTS settings, used only when ai_algorithm is MCTS.
MctsConfig mcts_config = 4;
// Whether iterative deepening should add leaf-score bonuses for move follow-up actions.
// Defaults to true, matching production behavior.
optional bool score_move_follow_ups = 5;
}
// Complete battle configuration