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