Compare commits

...
Author SHA1 Message Date
admin 2ab9332d21 Regenerate Shardok AI baselines after timeout fix 2026-06-04 10:55:47 -07:00
admin c380d167ca Fix ID timeout score pollution 2026-06-04 06:17:09 -07:00
14 changed files with 366 additions and 191 deletions
@@ -91,15 +91,15 @@ auto AICommandEvaluator::PerformLookahead(
const ScoreValue currentUtility,
const AIStrategy& attackerStrategy,
const CoordsSet& allCastleCoords,
std::chrono::steady_clock::time_point deadline) const -> std::future<ScoreValue> {
std::chrono::steady_clock::time_point deadline) const -> std::future<EvaluationResult> {
// Check transposition table before expensive computation
auto cachedScore =
g_transpositionTable.probe(innerEngine->GetCurrentGameState(), remainingLookahead, pid);
if (cachedScore.has_value()) {
// Return cached result immediately
std::promise<ScoreValue> p;
p.set_value(*cachedScore);
std::promise<EvaluationResult> p;
p.set_value(EvaluationResult{.score = *cachedScore, .completed = true});
return p.get_future();
}
const auto nextUtility = currentUtility;
@@ -111,8 +111,8 @@ auto AICommandEvaluator::PerformLookahead(
// table
g_transpositionTable.store(innerEngine->GetCurrentGameState(), 1, pid, nextUtility);
std::promise<ScoreValue> p;
p.set_value(nextUtility);
std::promise<EvaluationResult> p;
p.set_value(EvaluationResult{.score = nextUtility, .completed = true});
return p.get_future();
}
@@ -137,16 +137,18 @@ auto AICommandEvaluator::PerformLookahead(
innerEngine,
pid,
nextUtility,
remainingLookahead]() mutable -> ScoreValue {
const auto [index, type, lookaheadScore, immediateScore] =
bestCommandFuture.get();
remainingLookahead]() mutable -> EvaluationResult {
const auto bestCommand = bestCommandFuture.get();
if (!bestCommand.completed) {
return EvaluationResult{.score = nextUtility, .completed = false};
}
ScoreValue resultScore;
if (auto& nextCommand =
innerEngine->GetAvailableCommandsForAIPlayer(pid)->at(index);
if (auto& nextCommand = innerEngine->GetAvailableCommandsForAIPlayer(pid)->at(
bestCommand.index);
nextCommand->GetCommandType() !=
net::eagle0::shardok::common::END_TURN_COMMAND) {
resultScore = immediateScore;
resultScore = bestCommand.immediateScore;
} else {
resultScore = nextUtility;
}
@@ -158,7 +160,7 @@ auto AICommandEvaluator::PerformLookahead(
pid,
resultScore);
return resultScore;
return EvaluationResult{.score = resultScore, .completed = true};
});
}
@@ -166,8 +168,8 @@ auto AICommandEvaluator::PerformLookahead(
g_transpositionTable
.store(innerEngine->GetCurrentGameState(), remainingLookahead, pid, nextUtility);
std::promise<ScoreValue> p;
p.set_value(nextUtility);
std::promise<EvaluationResult> p;
p.set_value(EvaluationResult{.score = nextUtility, .completed = true});
return p.get_future();
}
@@ -186,10 +188,10 @@ auto AICommandEvaluator::EvaluateWithRandomness(
// Check if we've exceeded the deadline
if (std::chrono::steady_clock::now() > deadline) {
// Return with a default score and an empty future that resolves immediately
std::promise<ScoreValue> p;
p.set_value(0.0); // Default timeout score
std::promise<EvaluationResult> p;
p.set_value(EvaluationResult{.score = 0.0, .completed = false});
returnValue.immediateScore = 0.0;
returnValue.completed = false;
returnValue.lookaheadScore = p.get_future();
return returnValue;
}
@@ -204,11 +206,12 @@ auto AICommandEvaluator::EvaluateWithRandomness(
allCastleCoords);
returnValue.immediateScore = innerUtility;
returnValue.completed = true;
if (remainingLookahead <= 0) {
std::promise<ScoreValue> p;
std::promise<EvaluationResult> p;
returnValue.lookaheadScore = p.get_future();
p.set_value(innerUtility);
p.set_value(EvaluationResult{.score = innerUtility, .completed = true});
} else {
auto lookaheadLambda = [this,
pid,
@@ -219,7 +222,7 @@ auto AICommandEvaluator::EvaluateWithRandomness(
attackerStrategy,
innerUtility,
&allCastleCoords,
deadline]() -> ScoreValue {
deadline]() -> EvaluationResult {
auto lookaheadFuture = PerformLookahead(
pid,
isDefender,
@@ -237,7 +240,7 @@ auto AICommandEvaluator::EvaluateWithRandomness(
auto launchPolicy = remainingLookahead == 1 ? std::launch::async : std::launch::deferred;
returnValue.lookaheadScore = std::async(launchPolicy, lookaheadLambda);
#else
std::promise<ScoreValue> p;
std::promise<EvaluationResult> p;
returnValue.lookaheadScore = p.get_future();
auto lambdaResult = lookaheadLambda();
p.set_value(lambdaResult);
@@ -322,7 +325,8 @@ auto AICommandEvaluator::FindBestCommand(
size_t index;
CommandType type;
ScoreValue immediateScore;
std::vector<std::future<ScoreValue>> lookaheadFutures;
bool immediateCompleted = true;
std::vector<std::future<EvaluationResult>> lookaheadFutures;
};
std::vector<CommandEvaluation> commandEvaluations(commandCount);
@@ -336,12 +340,12 @@ auto AICommandEvaluator::FindBestCommand(
commandEvaluations[index].type = guessedCommandType;
if (guessedCommandType == net::eagle0::shardok::common::END_TURN_COMMAND) {
std::promise<ScoreValue> p;
std::promise<EvaluationResult> p;
commandEvaluations[index].lookaheadFutures.push_back(p.get_future());
p.set_value(currentUtility);
p.set_value(EvaluationResult{.score = currentUtility, .completed = true});
commandEvaluations[index].immediateScore = currentUtility;
} else if (IsDeterministic(guessedCommandType)) {
auto [immediateScore, lookaheadScore] = EvaluateWithRandomness(
auto evaluation = EvaluateWithRandomness(
pid,
isDefender,
originalIndex,
@@ -353,14 +357,16 @@ auto AICommandEvaluator::FindBestCommand(
allCastleCoords,
deadline);
commandEvaluations[index].immediateScore = immediateScore;
commandEvaluations[index].lookaheadFutures.push_back(std::move(lookaheadScore));
commandEvaluations[index].immediateScore = evaluation.immediateScore;
commandEvaluations[index].immediateCompleted = evaluation.completed;
commandEvaluations[index].lookaheadFutures.push_back(
std::move(evaluation.lookaheadScore));
} else if (guessedDescriptor->HasOdds()) {
const auto successChancePercentile = guessedDescriptor->GetOddsPercentile();
const double successChance = static_cast<double>(successChancePercentile) / 100.0;
// Success attempt uses 1.0 - (successChance / 2) as the roll
auto [successImmediateScore, successLookaheadScore] = EvaluateWithRandomness(
auto successEvaluation = EvaluateWithRandomness(
pid,
isDefender,
originalIndex,
@@ -374,7 +380,7 @@ auto AICommandEvaluator::FindBestCommand(
deadline);
// Failure attempt uses the average of (1 - successChance) and 0 as the roll
auto [failureImmediateScore, failureLookaheadScore] = EvaluateWithRandomness(
auto failureEvaluation = EvaluateWithRandomness(
pid,
isDefender,
originalIndex,
@@ -387,15 +393,26 @@ auto AICommandEvaluator::FindBestCommand(
allCastleCoords,
deadline);
commandEvaluations[index].immediateScore =
std::lerp(failureImmediateScore, successImmediateScore, successChance);
commandEvaluations[index].immediateCompleted =
failureEvaluation.completed && successEvaluation.completed;
commandEvaluations[index].immediateScore = std::lerp(
failureEvaluation.immediateScore,
successEvaluation.immediateScore,
successChance);
auto successSF = successLookaheadScore.share();
auto failureSF = failureLookaheadScore.share();
auto successSF = successEvaluation.lookaheadScore.share();
auto failureSF = failureEvaluation.lookaheadScore.share();
commandEvaluations[index].lookaheadFutures.push_back(std::async(
std::launch::deferred,
[successSF, failureSF, successChance]() -> double {
return std::lerp(failureSF.get(), successSF.get(), successChance);
[successSF, failureSF, successChance]() -> EvaluationResult {
const auto failure = failureSF.get();
const auto success = successSF.get();
if (!failure.completed || !success.completed) {
return EvaluationResult{.score = 0.0, .completed = false};
}
return EvaluationResult{
.score = std::lerp(failure.score, success.score, successChance),
.completed = true};
}));
} else {
ScoreValue sum = 0.0;
@@ -404,7 +421,7 @@ auto AICommandEvaluator::FindBestCommand(
// In each iteration, use a double from [0, 1] as the random roll
auto sequence =
std::vector{RandomnessSampleForRepeat(repeatIteration, sampleCount)};
auto [immediateScore, lookaheadScore] = EvaluateWithRandomness(
auto evaluation = EvaluateWithRandomness(
pid,
isDefender,
originalIndex,
@@ -416,8 +433,10 @@ auto AICommandEvaluator::FindBestCommand(
allCastleCoords,
deadline);
sum += immediateScore;
commandEvaluations[index].lookaheadFutures.push_back(std::move(lookaheadScore));
if (!evaluation.completed) { commandEvaluations[index].immediateCompleted = false; }
sum += evaluation.immediateScore;
commandEvaluations[index].lookaheadFutures.push_back(
std::move(evaluation.lookaheadScore));
}
commandEvaluations[index].immediateScore = sum / sampleCount;
}
@@ -433,19 +452,33 @@ auto AICommandEvaluator::FindBestCommand(
// Wait for all futures and compute final scores
for (auto& eval : evals) {
ScoreValue totalLookaheadScore = 0.0;
bool completed = eval.immediateCompleted;
for (auto& future : eval.lookaheadFutures) {
totalLookaheadScore += future.get();
const auto lookahead = future.get();
if (!lookahead.completed) { completed = false; }
totalLookaheadScore += lookahead.score;
}
ScoreValue avgLookaheadScore =
eval.lookaheadFutures.empty()
? eval.immediateScore
: totalLookaheadScore / eval.lookaheadFutures.size();
if (!completed) { continue; }
allResults.push_back(IndexAndScore{
.index = eval.index,
.type = eval.type,
.lookaheadScore = avgLookaheadScore,
.immediateScore = eval.immediateScore});
.immediateScore = eval.immediateScore,
.completed = true});
}
if (allResults.empty()) {
return IndexAndScore{
.index = 0,
.type = net::eagle0::shardok::common::UNKNOWN_COMMAND,
.lookaheadScore = 0.0,
.immediateScore = 0.0,
.completed = false};
}
// Find the best command using the existing sorter
auto bestIt = std::ranges::max_element(allResults, CommandSorter);
@@ -463,12 +496,12 @@ auto AICommandEvaluator::EvaluateCommand(
const ScoreValue currentUtility,
const CoordsSet& allCastleCoords,
const size_t commandIndex,
std::chrono::steady_clock::time_point deadline) const -> std::future<ScoreValue> {
std::chrono::steady_clock::time_point deadline) const -> std::future<EvaluationResult> {
const CommandListSPtr guessedDescriptors = guessedEngine.GetAvailableCommandsForAIPlayer(pid);
if (commandIndex >= guessedDescriptors->size()) {
std::promise<ScoreValue> p;
p.set_value(currentUtility);
std::promise<EvaluationResult> p;
p.set_value(EvaluationResult{.score = currentUtility, .completed = true});
return p.get_future();
}
@@ -476,11 +509,11 @@ auto AICommandEvaluator::EvaluateCommand(
if (const auto guessedCommandType = guessedDescriptor->GetCommandType();
guessedCommandType == net::eagle0::shardok::common::END_TURN_COMMAND) {
std::promise<ScoreValue> p;
p.set_value(currentUtility);
std::promise<EvaluationResult> p;
p.set_value(EvaluationResult{.score = currentUtility, .completed = true});
return p.get_future();
} else if (IsDeterministic(guessedCommandType)) {
auto [immediateScore, lookaheadScore] = EvaluateWithRandomness(
auto evaluation = EvaluateWithRandomness(
pid,
isDefender,
commandIndex,
@@ -491,13 +524,13 @@ auto AICommandEvaluator::EvaluateCommand(
attackerStrategy,
allCastleCoords,
deadline);
return std::move(lookaheadScore);
return std::move(evaluation.lookaheadScore);
} else if (guessedDescriptor->HasOdds()) {
const auto successChancePercentile = guessedDescriptor->GetOddsPercentile();
const double successChance = static_cast<double>(successChancePercentile) / 100.0;
// Success attempt
auto [successImmediateScore, successLookaheadScore] = EvaluateWithRandomness(
auto successEvaluation = EvaluateWithRandomness(
pid,
isDefender,
commandIndex,
@@ -510,7 +543,7 @@ auto AICommandEvaluator::EvaluateCommand(
deadline);
// Failure attempt
auto [failureImmediateScore, failureLookaheadScore] = EvaluateWithRandomness(
auto failureEvaluation = EvaluateWithRandomness(
pid,
isDefender,
commandIndex,
@@ -523,20 +556,29 @@ auto AICommandEvaluator::EvaluateCommand(
deadline);
// Return weighted average of success and failure
auto successSF = successLookaheadScore.share();
auto failureSF = failureLookaheadScore.share();
return std::async(std::launch::deferred, [successSF, failureSF, successChance]() -> double {
return std::lerp(failureSF.get(), successSF.get(), successChance);
});
auto successSF = successEvaluation.lookaheadScore.share();
auto failureSF = failureEvaluation.lookaheadScore.share();
return std::async(
std::launch::deferred,
[successSF, failureSF, successChance]() -> EvaluationResult {
const auto failure = failureSF.get();
const auto success = successSF.get();
if (!failure.completed || !success.completed) {
return EvaluationResult{.score = 0.0, .completed = false};
}
return EvaluationResult{
.score = std::lerp(failure.score, success.score, successChance),
.completed = true};
});
} else {
// For non-deterministic commands without odds, use multiple attempts
std::vector<std::future<ScoreValue>> lookaheadFutures;
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 [immediateScore, lookaheadScore] = EvaluateWithRandomness(
auto evaluation = EvaluateWithRandomness(
pid,
isDefender,
commandIndex,
@@ -548,16 +590,25 @@ auto AICommandEvaluator::EvaluateCommand(
allCastleCoords,
deadline);
lookaheadFutures.push_back(std::move(lookaheadScore));
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 -> double {
[lookaheadFutures = std::move(lookaheadFutures),
sampleCount]() mutable -> EvaluationResult {
ScoreValue total = 0.0;
for (auto& future : lookaheadFutures) { total += future.get(); }
return total / sampleCount;
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};
});
}
}
@@ -38,6 +38,11 @@ public:
BattalionTypeGetter battalionTypeGetter); // Pass by value
/// Evaluates the score for a particular command index with lookahead.
struct EvaluationResult {
ScoreValue score;
bool completed;
};
[[nodiscard]] auto EvaluateCommand(
PlayerId pid,
bool isDefender,
@@ -48,7 +53,7 @@ public:
ScoreValue currentUtility,
const CoordsSet& allCastleCoords,
size_t commandIndex,
std::chrono::steady_clock::time_point deadline) const -> std::future<ScoreValue>;
std::chrono::steady_clock::time_point deadline) const -> std::future<EvaluationResult>;
/// Find the best command among all available commands at the given depth.
struct IndexAndScore {
@@ -56,6 +61,7 @@ public:
CommandType type;
ScoreValue lookaheadScore;
ScoreValue immediateScore;
bool completed;
};
[[nodiscard]] auto FindBestCommand(
@@ -76,7 +82,8 @@ private:
struct ImmediateAndLookaheadScore {
ScoreValue immediateScore;
std::future<ScoreValue> lookaheadScore;
bool completed;
std::future<EvaluationResult> lookaheadScore;
};
/// Recursive lookahead calculator
@@ -89,7 +96,7 @@ private:
ScoreValue currentUtility,
const AIStrategy& attackerStrategy,
const CoordsSet& allCastleCoords,
std::chrono::steady_clock::time_point deadline) const -> std::future<ScoreValue>;
std::chrono::steady_clock::time_point deadline) const -> std::future<EvaluationResult>;
/// Evaluate single command execution with randomness handling
[[nodiscard]] auto EvaluateWithRandomness(
@@ -131,6 +131,10 @@ auto IterativeDeepeningAI::IterativeSearch(
// Now wait for all futures and collect results
for (auto& [cmdIndex, future] : futures) {
auto cmdResult = future.get();
if (!cmdResult.searchCompleted) {
allEvaluated = false;
continue;
}
// Ensure scoresByDepth[cmdIndex] has enough space
if (scoresByDepth[cmdIndex].size() <= currentDepth) {
@@ -252,7 +256,9 @@ auto IterativeDeepeningAI::IterativeSearch(
completionReason = EvaluationCompletionReason::RAN_OUT_OF_COMMANDS;
}
// Select best result from highest depth achieved for each command
// Select best result from the highest completed depth achieved for each command. Commands that
// timed out at a depth are not recorded for that depth, so partial deeper work can help without
// letting timeout fallback scores compete.
result = SelectBestResult(scoresByDepth, highestDepthCompleted);
result.minimumDepthCompleted = result.depthAchieved >= timeBudget.minDepthRequired;
result.searchCompleted = result.minimumDepthCompleted;
@@ -343,7 +349,9 @@ auto IterativeDeepeningAI::SearchCommandAtDepthWithEngine(
// Deduct adjusted time from remaining budget
timeBudget.remainingBudget -= adjustedElapsedMs;
result.bestScore = commandScore;
result.bestScore = commandScore.score;
result.searchCompleted = commandScore.completed;
result.minimumDepthCompleted = commandScore.completed;
std::promise<SearchResult> p;
p.set_value(result);
@@ -356,7 +364,7 @@ auto IterativeDeepeningAI::GetCommandsSortedByPreviousDepth(
const std::vector<size_t>& highestDepthCompleted,
const std::vector<size_t>& filteredIndices) -> std::vector<size_t> {
if (currentDepth == 1) {
// For depth 1, return filtered indices in natural order
// For depth 1, preserve the upstream command-factory priority order.
return filteredIndices;
}
@@ -111,4 +111,4 @@ private:
} // namespace shardok
#endif // EAGLE0_ITERATIVEDEEPENINGAI_HPP
#endif // EAGLE0_ITERATIVEDEEPENINGAI_HPP
@@ -1,32 +1,32 @@
action_seq,shardok_game_id,attacker_factions,defender_factions,real_winner,sim_winner,winner_match,real_round,sim_round,real_attacker_troops,real_defender_troops,sim_attacker_troops,sim_defender_troops
306,389ffb1901903118_1_b3e7d8ab,1,2,attacker,attacker,true,0,17,1454,0,1343,0
306,389ffb1901903118_1_b3e7d8ab,1,2,attacker,attacker,true,0,19,1454,0,1388,0
307,389ffb1901903118_2_bf499d3c,2,1,defender,defender,true,0,16,0,699,0,648
399,389ffb1901903118_3_cd9141a,2,1,defender,defender,true,0,6,0,1310,0,1315
631,389ffb1901903118_4_6e7b9b,1,2,defender,defender,true,0,5,0,3766,0,3654
1326,389ffb1901903118_5_8a57255a,9,6,attacker,attacker,true,0,11,1715,0,1701,0
2437,389ffb1901903118_7_5cf74e0,2,1,attacker,attacker,true,0,15,3146,0,3219,0
2584,389ffb1901903118_8_41ce0ebd,1,2,defender,defender,true,0,30,0,2028,0,174
2437,389ffb1901903118_7_5cf74e0,2,1,attacker,attacker,true,0,16,3146,0,3247,0
2584,389ffb1901903118_8_41ce0ebd,1,2,defender,defender,true,0,31,0,2028,0,168
2654,389ffb1901903118_9_eb43d421,2,1,defender,defender,true,0,28,0,335,0,467
3536,389ffb1901903118_a_950de1c1,1,2,attacker,attacker,true,0,16,2172,0,1596,0
3701,389ffb1901903118_b_5d1d76e0,8,2,defender,defender,true,0,30,0,2819,0,1344
3536,389ffb1901903118_a_950de1c1,1,2,attacker,attacker,true,0,12,2172,0,2528,0
3701,389ffb1901903118_b_5d1d76e0,8,2,defender,defender,true,0,30,0,2819,0,1348
4298,389ffb1901903118_c_7bdb54dd,2,1,defender,defender,true,0,3,0,3514,0,1977
4576,389ffb1901903118_d_689ee2fa,2,1,defender,defender,true,0,5,0,2028,0,1920
6014,389ffb1901903118_e_89c90dc6,2,8,defender,defender,true,0,12,0,1942,0,1474
4576,389ffb1901903118_d_689ee2fa,2,1,defender,defender,true,0,4,0,2028,0,1986
6014,389ffb1901903118_e_89c90dc6,2,8,defender,defender,true,0,6,0,1942,0,2252
6248,389ffb1901903118_f_3a9d9136,7,9,attacker,attacker,true,0,25,520,0,676,0
6705,389ffb1901903118_11_e0611233,1,2,defender,defender,true,0,31,0,3316,0,702
6705,389ffb1901903118_11_e0611233,1,2,defender,defender,true,0,31,0,3316,0,535
7212,389ffb1901903118_12_84a36154,2,8,defender,defender,true,0,8,0,1253,0,1273
8217,389ffb1901903118_13_2e98b1cf,8,2,attacker,attacker,true,0,29,920,0,1892,0
8217,389ffb1901903118_13_2e98b1cf,8,2,attacker,attacker,true,0,22,920,0,2167,0
8394,389ffb1901903118_14_b5570863,2,8,attacker,attacker,true,0,9,2449,0,3186,0
9975,389ffb1901903118_16_3d6ec341,2,1,defender,defender,true,0,5,0,7420,0,7437
9975,389ffb1901903118_16_3d6ec341,2,1,defender,defender,true,0,4,0,7420,0,7840
10229,389ffb1901903118_17_3ba689e3,5,7,attacker,attacker,true,0,23,2135,0,1816,0
10631,389ffb1901903118_18_9e463db3,1,2,attacker,attacker,true,0,1,4000,0,4000,0
11011,389ffb1901903118_19_239725d3,2,1,defender,defender,true,0,4,0,3532,0,3593
11011,389ffb1901903118_19_239725d3,2,1,defender,defender,true,0,5,0,3532,0,3658
11012,389ffb1901903118_1a_16e1ebf,9,6,defender,defender,true,0,30,0,846,0,1347
11297,389ffb1901903118_1c_529ba6e5,1,2,attacker,attacker,true,0,1,2400,0,2400,0
11469,389ffb1901903118_1d_49993012,5,9,defender,defender,true,0,8,0,787,0,1489
11470,389ffb1901903118_1e_fd8c60a8,7,5,attacker,attacker,true,0,12,3220,0,3400,0
11470,389ffb1901903118_1e_fd8c60a8,7,5,attacker,attacker,true,0,17,3220,0,3425,0
11565,389ffb1901903118_1f_53309484,5,7,defender,defender,true,0,31,190,3420,78,3600
12136,389ffb1901903118_24_f0d61a5a,9,7,attacker,attacker,true,0,21,1829,0,1581,0
12137,389ffb1901903118_25_a19c4d87,2,1,defender,defender,true,0,8,0,1894,0,1892
12765,389ffb1901903118_27_c008989b,6,9,defender,defender,true,0,9,0,1773,0,1747
12765,389ffb1901903118_27_c008989b,6,9,defender,defender,true,0,11,0,1773,0,1763
13752,389ffb1901903118_28_b33e2868,2,1,defender,defender,true,0,7,0,943,0,932
1 action_seq shardok_game_id attacker_factions defender_factions real_winner sim_winner winner_match real_round sim_round real_attacker_troops real_defender_troops sim_attacker_troops sim_defender_troops
2 306 389ffb1901903118_1_b3e7d8ab 1 2 attacker attacker true 0 17 19 1454 0 1343 1388 0
3 307 389ffb1901903118_2_bf499d3c 2 1 defender defender true 0 16 0 699 0 648
4 399 389ffb1901903118_3_cd9141a 2 1 defender defender true 0 6 0 1310 0 1315
5 631 389ffb1901903118_4_6e7b9b 1 2 defender defender true 0 5 0 3766 0 3654
6 1326 389ffb1901903118_5_8a57255a 9 6 attacker attacker true 0 11 1715 0 1701 0
7 2437 389ffb1901903118_7_5cf74e0 2 1 attacker attacker true 0 15 16 3146 0 3219 3247 0
8 2584 389ffb1901903118_8_41ce0ebd 1 2 defender defender true 0 30 31 0 2028 0 174 168
9 2654 389ffb1901903118_9_eb43d421 2 1 defender defender true 0 28 0 335 0 467
10 3536 389ffb1901903118_a_950de1c1 1 2 attacker attacker true 0 16 12 2172 0 1596 2528 0
11 3701 389ffb1901903118_b_5d1d76e0 8 2 defender defender true 0 30 0 2819 0 1344 1348
12 4298 389ffb1901903118_c_7bdb54dd 2 1 defender defender true 0 3 0 3514 0 1977
13 4576 389ffb1901903118_d_689ee2fa 2 1 defender defender true 0 5 4 0 2028 0 1920 1986
14 6014 389ffb1901903118_e_89c90dc6 2 8 defender defender true 0 12 6 0 1942 0 1474 2252
15 6248 389ffb1901903118_f_3a9d9136 7 9 attacker attacker true 0 25 520 0 676 0
16 6705 389ffb1901903118_11_e0611233 1 2 defender defender true 0 31 0 3316 0 702 535
17 7212 389ffb1901903118_12_84a36154 2 8 defender defender true 0 8 0 1253 0 1273
18 8217 389ffb1901903118_13_2e98b1cf 8 2 attacker attacker true 0 29 22 920 0 1892 2167 0
19 8394 389ffb1901903118_14_b5570863 2 8 attacker attacker true 0 9 2449 0 3186 0
20 9975 389ffb1901903118_16_3d6ec341 2 1 defender defender true 0 5 4 0 7420 0 7437 7840
21 10229 389ffb1901903118_17_3ba689e3 5 7 attacker attacker true 0 23 2135 0 1816 0
22 10631 389ffb1901903118_18_9e463db3 1 2 attacker attacker true 0 1 4000 0 4000 0
23 11011 389ffb1901903118_19_239725d3 2 1 defender defender true 0 4 5 0 3532 0 3593 3658
24 11012 389ffb1901903118_1a_16e1ebf 9 6 defender defender true 0 30 0 846 0 1347
25 11297 389ffb1901903118_1c_529ba6e5 1 2 attacker attacker true 0 1 2400 0 2400 0
26 11469 389ffb1901903118_1d_49993012 5 9 defender defender true 0 8 0 787 0 1489
27 11470 389ffb1901903118_1e_fd8c60a8 7 5 attacker attacker true 0 12 17 3220 0 3400 3425 0
28 11565 389ffb1901903118_1f_53309484 5 7 defender defender true 0 31 190 3420 78 3600
29 12136 389ffb1901903118_24_f0d61a5a 9 7 attacker attacker true 0 21 1829 0 1581 0
30 12137 389ffb1901903118_25_a19c4d87 2 1 defender defender true 0 8 0 1894 0 1892
31 12765 389ffb1901903118_27_c008989b 6 9 defender defender true 0 9 11 0 1773 0 1747 1763
32 13752 389ffb1901903118_28_b33e2868 2 1 defender defender true 0 7 0 943 0 932
@@ -12,34 +12,34 @@ than an exact replay assertion.
| Action seq | Factions | Real winner | Sim winner | Real troops | Sim troops |
| ---: | --- | --- | --- | ---: | ---: |
| 306 | 1 vs 2 | attacker | attacker | 1454 vs 0 | 1343 vs 0 |
| 306 | 1 vs 2 | attacker | attacker | 1454 vs 0 | 1388 vs 0 |
| 307 | 2 vs 1 | defender | defender | 0 vs 699 | 0 vs 648 |
| 399 | 2 vs 1 | defender | defender | 0 vs 1310 | 0 vs 1315 |
| 631 | 1 vs 2 | defender | defender | 0 vs 3766 | 0 vs 3654 |
| 1326 | 9 vs 6 | attacker | attacker | 1715 vs 0 | 1701 vs 0 |
| 2437 | 2 vs 1 | attacker | attacker | 3146 vs 0 | 3219 vs 0 |
| 2584 | 1 vs 2 | defender | defender | 0 vs 2028 | 0 vs 174 |
| 2437 | 2 vs 1 | attacker | attacker | 3146 vs 0 | 3247 vs 0 |
| 2584 | 1 vs 2 | defender | defender | 0 vs 2028 | 0 vs 168 |
| 2654 | 2 vs 1 | defender | defender | 0 vs 335 | 0 vs 467 |
| 3536 | 1 vs 2 | attacker | attacker | 2172 vs 0 | 1596 vs 0 |
| 3701 | 8 vs 2 | defender | defender | 0 vs 2819 | 0 vs 1344 |
| 3536 | 1 vs 2 | attacker | attacker | 2172 vs 0 | 2528 vs 0 |
| 3701 | 8 vs 2 | defender | defender | 0 vs 2819 | 0 vs 1348 |
| 4298 | 2 vs 1 | defender | defender | 0 vs 3514 | 0 vs 1977 |
| 4576 | 2 vs 1 | defender | defender | 0 vs 2028 | 0 vs 1920 |
| 6014 | 2 vs 8 | defender | defender | 0 vs 1942 | 0 vs 1474 |
| 4576 | 2 vs 1 | defender | defender | 0 vs 2028 | 0 vs 1986 |
| 6014 | 2 vs 8 | defender | defender | 0 vs 1942 | 0 vs 2252 |
| 6248 | 7 vs 9 | attacker | attacker | 520 vs 0 | 676 vs 0 |
| 6705 | 1 vs 2 | defender | defender | 0 vs 3316 | 0 vs 702 |
| 6705 | 1 vs 2 | defender | defender | 0 vs 3316 | 0 vs 535 |
| 7212 | 2 vs 8 | defender | defender | 0 vs 1253 | 0 vs 1273 |
| 8217 | 8 vs 2 | attacker | attacker | 920 vs 0 | 1892 vs 0 |
| 8217 | 8 vs 2 | attacker | attacker | 920 vs 0 | 2167 vs 0 |
| 8394 | 2 vs 8 | attacker | attacker | 2449 vs 0 | 3186 vs 0 |
| 9975 | 2 vs 1 | defender | defender | 0 vs 7420 | 0 vs 7437 |
| 9975 | 2 vs 1 | defender | defender | 0 vs 7420 | 0 vs 7840 |
| 10229 | 5 vs 7 | attacker | attacker | 2135 vs 0 | 1816 vs 0 |
| 10631 | 1 vs 2 | attacker | attacker | 4000 vs 0 | 4000 vs 0 |
| 11011 | 2 vs 1 | defender | defender | 0 vs 3532 | 0 vs 3593 |
| 11011 | 2 vs 1 | defender | defender | 0 vs 3532 | 0 vs 3658 |
| 11012 | 9 vs 6 | defender | defender | 0 vs 846 | 0 vs 1347 |
| 11297 | 1 vs 2 | attacker | attacker | 2400 vs 0 | 2400 vs 0 |
| 11469 | 5 vs 9 | defender | defender | 0 vs 787 | 0 vs 1489 |
| 11470 | 7 vs 5 | attacker | attacker | 3220 vs 0 | 3400 vs 0 |
| 11470 | 7 vs 5 | attacker | attacker | 3220 vs 0 | 3425 vs 0 |
| 11565 | 5 vs 7 | defender | defender | 190 vs 3420 | 78 vs 3600 |
| 12136 | 9 vs 7 | attacker | attacker | 1829 vs 0 | 1581 vs 0 |
| 12137 | 2 vs 1 | defender | defender | 0 vs 1894 | 0 vs 1892 |
| 12765 | 6 vs 9 | defender | defender | 0 vs 1773 | 0 vs 1747 |
| 12765 | 6 vs 9 | defender | defender | 0 vs 1773 | 0 vs 1763 |
| 13752 | 2 vs 1 | defender | defender | 0 vs 943 | 0 vs 932 |
@@ -1,41 +1,41 @@
variant,action_seq,config,winner,end_reason,result,total_rounds,total_commands,attacker_ai_commands,defender_ai_commands,attacker_avg_depth,defender_avg_depth,attacker_surviving_units,defender_surviving_units,attacker_surviving_troops,defender_surviving_troops,elapsed_seconds
expected_impact_archery_standard_baseline,306,/private/tmp/eagle0-standard-baselines/configs/306_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,17,85,38,23,4.57895,3.30435,2,0,1343,0,
expected_impact_archery_standard_baseline,307,/private/tmp/eagle0-standard-baselines/configs/307_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,16,74,36,15,3.55556,2.86667,0,1,0,648,
expected_impact_archery_standard_baseline,306,/private/tmp/eagle0-standard-baselines/configs/306_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,19,99,41,32,4.04878,3.65625,2,0,1388,0,
expected_impact_archery_standard_baseline,307,/private/tmp/eagle0-standard-baselines/configs/307_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,16,75,37,15,3.54054,2.86667,0,1,0,648,
expected_impact_archery_standard_baseline,399,/private/tmp/eagle0-standard-baselines/configs/399_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,6,30,7,14,3,3.85714,0,3,0,1315,
expected_impact_archery_standard_baseline,631,/private/tmp/eagle0-standard-baselines/configs/631_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,5,38,12,14,3.58333,3.85714,0,5,0,3654,
expected_impact_archery_standard_baseline,1326,/private/tmp/eagle0-standard-baselines/configs/1326_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,11,72,34,19,3.35294,3.63158,2,0,1701,0,
expected_impact_archery_standard_baseline,1472,/private/tmp/eagle0-standard-baselines/configs/1472_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,31,143,59,67,3.27119,3.50746,0,2,0,944,
expected_impact_archery_standard_baseline,2437,/private/tmp/eagle0-standard-baselines/configs/2437_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,15,141,86,38,3.2093,3.18421,5,0,3219,0,
expected_impact_archery_standard_baseline,2584,/private/tmp/eagle0-standard-baselines/configs/2584_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,30,183,45,78,3.6,3.34615,0,1,0,174,
expected_impact_archery_standard_baseline,2654,/private/tmp/eagle0-standard-baselines/configs/2654_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,28,198,78,72,3.21795,4.02778,0,2,0,467,
expected_impact_archery_standard_baseline,3536,/private/tmp/eagle0-standard-baselines/configs/3536_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,16,155,78,42,3.5641,2.95238,3,0,1596,0,
expected_impact_archery_standard_baseline,3701,/private/tmp/eagle0-standard-baselines/configs/3701_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,30,152,57,71,3.33333,3.11268,0,4,0,1344,
expected_impact_archery_standard_baseline,4298,/private/tmp/eagle0-standard-baselines/configs/4298_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,3,39,14,11,3,3,0,6,0,1977,
expected_impact_archery_standard_baseline,4576,/private/tmp/eagle0-standard-baselines/configs/4576_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,5,52,31,9,3.16129,3,0,3,0,1920,
expected_impact_archery_standard_baseline,6014,/private/tmp/eagle0-standard-baselines/configs/6014_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,12,67,35,17,3.31429,3.52941,0,2,0,1474,
expected_impact_archery_standard_baseline,6248,/private/tmp/eagle0-standard-baselines/configs/6248_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,25,153,75,46,3.68,2.86957,2,0,676,0,
expected_impact_archery_standard_baseline,6408,/private/tmp/eagle0-standard-baselines/configs/6408_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,10,103,67,17,3.49254,3.88235,5,0,4022,0,
expected_impact_archery_standard_baseline,6705,/private/tmp/eagle0-standard-baselines/configs/6705_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,31,188,75,82,3.09333,3.53659,0,3,0,702,
expected_impact_archery_standard_baseline,631,/private/tmp/eagle0-standard-baselines/configs/631_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,5,38,12,14,3.58333,4.14286,0,5,0,3654,
expected_impact_archery_standard_baseline,1326,/private/tmp/eagle0-standard-baselines/configs/1326_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,11,72,34,19,3.26471,3.63158,2,0,1701,0,
expected_impact_archery_standard_baseline,1472,/private/tmp/eagle0-standard-baselines/configs/1472_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,31,143,59,67,3.25424,3.46269,0,2,0,944,
expected_impact_archery_standard_baseline,2437,/private/tmp/eagle0-standard-baselines/configs/2437_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,16,148,89,43,3.37079,3.32558,5,0,3247,0,
expected_impact_archery_standard_baseline,2584,/private/tmp/eagle0-standard-baselines/configs/2584_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,31,190,48,85,3.45833,3.16471,0,1,0,168,
expected_impact_archery_standard_baseline,2654,/private/tmp/eagle0-standard-baselines/configs/2654_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,28,198,78,72,3.19231,4.04167,0,2,0,467,
expected_impact_archery_standard_baseline,3536,/private/tmp/eagle0-standard-baselines/configs/3536_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,12,120,57,36,3.40351,2.88889,4,0,2528,0,
expected_impact_archery_standard_baseline,3701,/private/tmp/eagle0-standard-baselines/configs/3701_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,30,155,62,70,3.5,3.24286,0,4,0,1348,
expected_impact_archery_standard_baseline,4298,/private/tmp/eagle0-standard-baselines/configs/4298_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,3,38,13,11,3.07692,2.90909,0,6,0,1977,
expected_impact_archery_standard_baseline,4576,/private/tmp/eagle0-standard-baselines/configs/4576_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,4,35,17,7,3,3.28571,0,3,0,1986,
expected_impact_archery_standard_baseline,6014,/private/tmp/eagle0-standard-baselines/configs/6014_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,6,39,20,7,3.15,3.42857,0,3,0,2252,
expected_impact_archery_standard_baseline,6248,/private/tmp/eagle0-standard-baselines/configs/6248_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,25,153,75,46,3.65333,2.8913,2,0,676,0,
expected_impact_archery_standard_baseline,6408,/private/tmp/eagle0-standard-baselines/configs/6408_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,11,107,68,18,3.5,3.33333,5,0,4020,0,
expected_impact_archery_standard_baseline,6705,/private/tmp/eagle0-standard-baselines/configs/6705_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,31,205,87,86,3.01149,3.74419,0,2,0,535,
expected_impact_archery_standard_baseline,7212,/private/tmp/eagle0-standard-baselines/configs/7212_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,8,39,18,11,3.38889,3.63636,0,2,0,1273,
expected_impact_archery_standard_baseline,8217,/private/tmp/eagle0-standard-baselines/configs/8217_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,29,197,97,58,3.42268,3.44828,3,0,1892,0,
expected_impact_archery_standard_baseline,8394,/private/tmp/eagle0-standard-baselines/configs/8394_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,9,81,48,17,3.29167,2.64706,4,0,3186,0,
expected_impact_archery_standard_baseline,8628,/private/tmp/eagle0-standard-baselines/configs/8628_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,5,41,27,3,3,2.33333,4,0,2760,0,
expected_impact_archery_standard_baseline,9975,/private/tmp/eagle0-standard-baselines/configs/9975_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,5,80,24,35,2.79167,2.22857,0,10,0,7437,
expected_impact_archery_standard_baseline,10229,/private/tmp/eagle0-standard-baselines/configs/10229_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,23,105,58,4,3.63793,3,3,0,1816,0,
expected_impact_archery_standard_baseline,10631,/private/tmp/eagle0-standard-baselines/configs/10631_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,1,16,7,1,2.85714,3,5,1,4000,0,
expected_impact_archery_standard_baseline,11011,/private/tmp/eagle0-standard-baselines/configs/11011_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,4,47,16,18,3.4375,3.22222,0,5,0,3593,
expected_impact_archery_standard_baseline,11012,/private/tmp/eagle0-standard-baselines/configs/11012_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,30,146,47,67,3.44681,3.64179,0,3,0,1347,
expected_impact_archery_standard_baseline,11112,/private/tmp/eagle0-standard-baselines/configs/11112_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,3,31,7,8,3.14286,2,0,11,0,7595,
expected_impact_archery_standard_baseline,8217,/private/tmp/eagle0-standard-baselines/configs/8217_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,22,164,79,49,3.35443,3.08163,3,0,2167,0,
expected_impact_archery_standard_baseline,8394,/private/tmp/eagle0-standard-baselines/configs/8394_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,9,81,48,17,3.25,2.64706,4,0,3186,0,
expected_impact_archery_standard_baseline,8628,/private/tmp/eagle0-standard-baselines/configs/8628_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,5,41,27,3,2.96296,2,4,0,2760,0,
expected_impact_archery_standard_baseline,9975,/private/tmp/eagle0-standard-baselines/configs/9975_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,4,70,23,27,2.65217,2.40741,0,10,0,7840,
expected_impact_archery_standard_baseline,10229,/private/tmp/eagle0-standard-baselines/configs/10229_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,23,104,57,4,3.77193,3,3,0,1816,0,
expected_impact_archery_standard_baseline,10631,/private/tmp/eagle0-standard-baselines/configs/10631_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,1,16,7,1,3,3,5,1,4000,0,
expected_impact_archery_standard_baseline,11011,/private/tmp/eagle0-standard-baselines/configs/11011_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,5,51,22,18,3.5,3,0,5,0,3658,
expected_impact_archery_standard_baseline,11012,/private/tmp/eagle0-standard-baselines/configs/11012_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,30,146,47,67,3.40426,3.62687,0,3,0,1347,
expected_impact_archery_standard_baseline,11112,/private/tmp/eagle0-standard-baselines/configs/11112_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,3,31,7,8,3.42857,2,0,11,0,7595,
expected_impact_archery_standard_baseline,11297,/private/tmp/eagle0-standard-baselines/configs/11297_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,1,13,5,1,3,3,3,1,2400,0,
expected_impact_archery_standard_baseline,11469,/private/tmp/eagle0-standard-baselines/configs/11469_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,8,58,30,12,3.2,3.16667,0,2,0,1489,
expected_impact_archery_standard_baseline,11470,/private/tmp/eagle0-standard-baselines/configs/11470_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,12,104,68,20,3.44118,3,4,0,3400,0,
expected_impact_archery_standard_baseline,11565,/private/tmp/eagle0-standard-baselines/configs/11565_standard.json,defender,max_rounds_reached,Defender won by surviving maximum rounds,31,103,30,34,4.26667,3.61765,1,4,78,3600,
expected_impact_archery_standard_baseline,11662,/private/tmp/eagle0-standard-baselines/configs/11662_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,9,133,98,18,2.86735,3.61111,9,0,6627,0,
expected_impact_archery_standard_baseline,11754,/private/tmp/eagle0-standard-baselines/configs/11754_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,30,163,72,76,3.22222,3.26316,0,3,0,1000,
expected_impact_archery_standard_baseline,11941,/private/tmp/eagle0-standard-baselines/configs/11941_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,6,64,44,7,3.11364,3,6,0,4777,0,
expected_impact_archery_standard_baseline,11942,/private/tmp/eagle0-standard-baselines/configs/11942_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,5,91,59,16,2.61017,3.375,12,0,8572,0,
expected_impact_archery_standard_baseline,12136,/private/tmp/eagle0-standard-baselines/configs/12136_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,21,141,79,34,3.72152,4.52941,3,0,1581,0,
expected_impact_archery_standard_baseline,12137,/private/tmp/eagle0-standard-baselines/configs/12137_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,8,50,19,17,3.21053,3.11765,0,3,0,1892,
expected_impact_archery_standard_baseline,12138,/private/tmp/eagle0-standard-baselines/configs/12138_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,13,98,68,16,3.45588,2.875,2,0,1758,0,
expected_impact_archery_standard_baseline,12765,/private/tmp/eagle0-standard-baselines/configs/12765_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,9,40,21,10,3.66667,3.2,0,2,0,1747,
expected_impact_archery_standard_baseline,11469,/private/tmp/eagle0-standard-baselines/configs/11469_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,8,58,30,12,3.16667,3.16667,0,2,0,1489,
expected_impact_archery_standard_baseline,11470,/private/tmp/eagle0-standard-baselines/configs/11470_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,17,99,65,22,3.50769,2.68182,4,0,3425,0,
expected_impact_archery_standard_baseline,11565,/private/tmp/eagle0-standard-baselines/configs/11565_standard.json,defender,max_rounds_reached,Defender won by surviving maximum rounds,31,103,30,34,4.26667,3.91176,1,4,78,3600,
expected_impact_archery_standard_baseline,11662,/private/tmp/eagle0-standard-baselines/configs/11662_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,8,120,85,17,2.85882,3.23529,9,0,6624,0,
expected_impact_archery_standard_baseline,11754,/private/tmp/eagle0-standard-baselines/configs/11754_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,30,163,72,76,3.31944,3.28947,0,3,0,1000,
expected_impact_archery_standard_baseline,11941,/private/tmp/eagle0-standard-baselines/configs/11941_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,6,64,44,7,3.25,3,6,0,4777,0,
expected_impact_archery_standard_baseline,11942,/private/tmp/eagle0-standard-baselines/configs/11942_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,5,91,59,16,2.64407,3.375,12,0,8572,0,
expected_impact_archery_standard_baseline,12136,/private/tmp/eagle0-standard-baselines/configs/12136_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,21,141,79,34,3.70886,4.52941,3,0,1581,0,
expected_impact_archery_standard_baseline,12137,/private/tmp/eagle0-standard-baselines/configs/12137_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,8,50,19,17,3.21053,3.29412,0,3,0,1892,
expected_impact_archery_standard_baseline,12138,/private/tmp/eagle0-standard-baselines/configs/12138_standard.json,attacker,last_player_standing,Attacker won by eliminating all defenders,14,102,69,16,3.43478,2.9375,3,0,2512,0,
expected_impact_archery_standard_baseline,12765,/private/tmp/eagle0-standard-baselines/configs/12765_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,11,46,26,11,3.61538,3.09091,0,2,0,1763,
expected_impact_archery_standard_baseline,13752,/private/tmp/eagle0-standard-baselines/configs/13752_standard.json,defender,last_player_standing,Defender won by eliminating all attackers,7,38,16,10,3.5,3.3,0,2,0,932,
1 variant action_seq config winner end_reason result total_rounds total_commands attacker_ai_commands defender_ai_commands attacker_avg_depth defender_avg_depth attacker_surviving_units defender_surviving_units attacker_surviving_troops defender_surviving_troops elapsed_seconds
2 expected_impact_archery_standard_baseline 306 /private/tmp/eagle0-standard-baselines/configs/306_standard.json attacker last_player_standing Attacker won by eliminating all defenders 17 19 85 99 38 41 23 32 4.57895 4.04878 3.30435 3.65625 2 0 1343 1388 0
3 expected_impact_archery_standard_baseline 307 /private/tmp/eagle0-standard-baselines/configs/307_standard.json defender last_player_standing Defender won by eliminating all attackers 16 74 75 36 37 15 3.55556 3.54054 2.86667 0 1 0 648
4 expected_impact_archery_standard_baseline 399 /private/tmp/eagle0-standard-baselines/configs/399_standard.json defender last_player_standing Defender won by eliminating all attackers 6 30 7 14 3 3.85714 0 3 0 1315
5 expected_impact_archery_standard_baseline 631 /private/tmp/eagle0-standard-baselines/configs/631_standard.json defender last_player_standing Defender won by eliminating all attackers 5 38 12 14 3.58333 3.85714 4.14286 0 5 0 3654
6 expected_impact_archery_standard_baseline 1326 /private/tmp/eagle0-standard-baselines/configs/1326_standard.json attacker last_player_standing Attacker won by eliminating all defenders 11 72 34 19 3.35294 3.26471 3.63158 2 0 1701 0
7 expected_impact_archery_standard_baseline 1472 /private/tmp/eagle0-standard-baselines/configs/1472_standard.json defender last_player_standing Defender won by eliminating all attackers 31 143 59 67 3.27119 3.25424 3.50746 3.46269 0 2 0 944
8 expected_impact_archery_standard_baseline 2437 /private/tmp/eagle0-standard-baselines/configs/2437_standard.json attacker last_player_standing Attacker won by eliminating all defenders 15 16 141 148 86 89 38 43 3.2093 3.37079 3.18421 3.32558 5 0 3219 3247 0
9 expected_impact_archery_standard_baseline 2584 /private/tmp/eagle0-standard-baselines/configs/2584_standard.json defender last_player_standing Defender won by eliminating all attackers 30 31 183 190 45 48 78 85 3.6 3.45833 3.34615 3.16471 0 1 0 174 168
10 expected_impact_archery_standard_baseline 2654 /private/tmp/eagle0-standard-baselines/configs/2654_standard.json defender last_player_standing Defender won by eliminating all attackers 28 198 78 72 3.21795 3.19231 4.02778 4.04167 0 2 0 467
11 expected_impact_archery_standard_baseline 3536 /private/tmp/eagle0-standard-baselines/configs/3536_standard.json attacker last_player_standing Attacker won by eliminating all defenders 16 12 155 120 78 57 42 36 3.5641 3.40351 2.95238 2.88889 3 4 0 1596 2528 0
12 expected_impact_archery_standard_baseline 3701 /private/tmp/eagle0-standard-baselines/configs/3701_standard.json defender last_player_standing Defender won by eliminating all attackers 30 152 155 57 62 71 70 3.33333 3.5 3.11268 3.24286 0 4 0 1344 1348
13 expected_impact_archery_standard_baseline 4298 /private/tmp/eagle0-standard-baselines/configs/4298_standard.json defender last_player_standing Defender won by eliminating all attackers 3 39 38 14 13 11 3 3.07692 3 2.90909 0 6 0 1977
14 expected_impact_archery_standard_baseline 4576 /private/tmp/eagle0-standard-baselines/configs/4576_standard.json defender last_player_standing Defender won by eliminating all attackers 5 4 52 35 31 17 9 7 3.16129 3 3 3.28571 0 3 0 1920 1986
15 expected_impact_archery_standard_baseline 6014 /private/tmp/eagle0-standard-baselines/configs/6014_standard.json defender last_player_standing Defender won by eliminating all attackers 12 6 67 39 35 20 17 7 3.31429 3.15 3.52941 3.42857 0 2 3 0 1474 2252
16 expected_impact_archery_standard_baseline 6248 /private/tmp/eagle0-standard-baselines/configs/6248_standard.json attacker last_player_standing Attacker won by eliminating all defenders 25 153 75 46 3.68 3.65333 2.86957 2.8913 2 0 676 0
17 expected_impact_archery_standard_baseline 6408 /private/tmp/eagle0-standard-baselines/configs/6408_standard.json attacker last_player_standing Attacker won by eliminating all defenders 10 11 103 107 67 68 17 18 3.49254 3.5 3.88235 3.33333 5 0 4022 4020 0
18 expected_impact_archery_standard_baseline 6705 /private/tmp/eagle0-standard-baselines/configs/6705_standard.json defender last_player_standing Defender won by eliminating all attackers 31 188 205 75 87 82 86 3.09333 3.01149 3.53659 3.74419 0 3 2 0 702 535
19 expected_impact_archery_standard_baseline 7212 /private/tmp/eagle0-standard-baselines/configs/7212_standard.json defender last_player_standing Defender won by eliminating all attackers 8 39 18 11 3.38889 3.63636 0 2 0 1273
20 expected_impact_archery_standard_baseline 8217 /private/tmp/eagle0-standard-baselines/configs/8217_standard.json attacker last_player_standing Attacker won by eliminating all defenders 29 22 197 164 97 79 58 49 3.42268 3.35443 3.44828 3.08163 3 0 1892 2167 0
21 expected_impact_archery_standard_baseline 8394 /private/tmp/eagle0-standard-baselines/configs/8394_standard.json attacker last_player_standing Attacker won by eliminating all defenders 9 81 48 17 3.29167 3.25 2.64706 4 0 3186 0
22 expected_impact_archery_standard_baseline 8628 /private/tmp/eagle0-standard-baselines/configs/8628_standard.json attacker last_player_standing Attacker won by eliminating all defenders 5 41 27 3 3 2.96296 2.33333 2 4 0 2760 0
23 expected_impact_archery_standard_baseline 9975 /private/tmp/eagle0-standard-baselines/configs/9975_standard.json defender last_player_standing Defender won by eliminating all attackers 5 4 80 70 24 23 35 27 2.79167 2.65217 2.22857 2.40741 0 10 0 7437 7840
24 expected_impact_archery_standard_baseline 10229 /private/tmp/eagle0-standard-baselines/configs/10229_standard.json attacker last_player_standing Attacker won by eliminating all defenders 23 105 104 58 57 4 3.63793 3.77193 3 3 0 1816 0
25 expected_impact_archery_standard_baseline 10631 /private/tmp/eagle0-standard-baselines/configs/10631_standard.json attacker last_player_standing Attacker won by eliminating all defenders 1 16 7 1 2.85714 3 3 5 1 4000 0
26 expected_impact_archery_standard_baseline 11011 /private/tmp/eagle0-standard-baselines/configs/11011_standard.json defender last_player_standing Defender won by eliminating all attackers 4 5 47 51 16 22 18 3.4375 3.5 3.22222 3 0 5 0 3593 3658
27 expected_impact_archery_standard_baseline 11012 /private/tmp/eagle0-standard-baselines/configs/11012_standard.json defender last_player_standing Defender won by eliminating all attackers 30 146 47 67 3.44681 3.40426 3.64179 3.62687 0 3 0 1347
28 expected_impact_archery_standard_baseline 11112 /private/tmp/eagle0-standard-baselines/configs/11112_standard.json defender last_player_standing Defender won by eliminating all attackers 3 31 7 8 3.14286 3.42857 2 0 11 0 7595
29 expected_impact_archery_standard_baseline 11297 /private/tmp/eagle0-standard-baselines/configs/11297_standard.json attacker last_player_standing Attacker won by eliminating all defenders 1 13 5 1 3 3 3 1 2400 0
30 expected_impact_archery_standard_baseline 11469 /private/tmp/eagle0-standard-baselines/configs/11469_standard.json defender last_player_standing Defender won by eliminating all attackers 8 58 30 12 3.2 3.16667 3.16667 0 2 0 1489
31 expected_impact_archery_standard_baseline 11470 /private/tmp/eagle0-standard-baselines/configs/11470_standard.json attacker last_player_standing Attacker won by eliminating all defenders 12 17 104 99 68 65 20 22 3.44118 3.50769 3 2.68182 4 0 3400 3425 0
32 expected_impact_archery_standard_baseline 11565 /private/tmp/eagle0-standard-baselines/configs/11565_standard.json defender max_rounds_reached Defender won by surviving maximum rounds 31 103 30 34 4.26667 3.61765 3.91176 1 4 78 3600
33 expected_impact_archery_standard_baseline 11662 /private/tmp/eagle0-standard-baselines/configs/11662_standard.json attacker last_player_standing Attacker won by eliminating all defenders 9 8 133 120 98 85 18 17 2.86735 2.85882 3.61111 3.23529 9 0 6627 6624 0
34 expected_impact_archery_standard_baseline 11754 /private/tmp/eagle0-standard-baselines/configs/11754_standard.json defender last_player_standing Defender won by eliminating all attackers 30 163 72 76 3.22222 3.31944 3.26316 3.28947 0 3 0 1000
35 expected_impact_archery_standard_baseline 11941 /private/tmp/eagle0-standard-baselines/configs/11941_standard.json attacker last_player_standing Attacker won by eliminating all defenders 6 64 44 7 3.11364 3.25 3 6 0 4777 0
36 expected_impact_archery_standard_baseline 11942 /private/tmp/eagle0-standard-baselines/configs/11942_standard.json attacker last_player_standing Attacker won by eliminating all defenders 5 91 59 16 2.61017 2.64407 3.375 12 0 8572 0
37 expected_impact_archery_standard_baseline 12136 /private/tmp/eagle0-standard-baselines/configs/12136_standard.json attacker last_player_standing Attacker won by eliminating all defenders 21 141 79 34 3.72152 3.70886 4.52941 3 0 1581 0
38 expected_impact_archery_standard_baseline 12137 /private/tmp/eagle0-standard-baselines/configs/12137_standard.json defender last_player_standing Defender won by eliminating all attackers 8 50 19 17 3.21053 3.11765 3.29412 0 3 0 1892
39 expected_impact_archery_standard_baseline 12138 /private/tmp/eagle0-standard-baselines/configs/12138_standard.json attacker last_player_standing Attacker won by eliminating all defenders 13 14 98 102 68 69 16 3.45588 3.43478 2.875 2.9375 2 3 0 1758 2512 0
40 expected_impact_archery_standard_baseline 12765 /private/tmp/eagle0-standard-baselines/configs/12765_standard.json defender last_player_standing Defender won by eliminating all attackers 9 11 40 46 21 26 10 11 3.66667 3.61538 3.2 3.09091 0 2 0 1747 1763
41 expected_impact_archery_standard_baseline 13752 /private/tmp/eagle0-standard-baselines/configs/13752_standard.json defender last_player_standing Defender won by eliminating all attackers 7 38 16 10 3.5 3.3 0 2 0 932
@@ -25,15 +25,15 @@ Survivor margin is `attacker_surviving_troops - defender_surviving_troops`.
| Action seq | Winner | End reason | Attacker troops | Defender troops | Margin |
| ---: | --- | --- | ---: | ---: | ---: |
| 2584 | defender | last_player_standing | 0 | 174 | -174 |
| 2584 | defender | last_player_standing | 0 | 168 | -168 |
| 2654 | defender | last_player_standing | 0 | 467 | -467 |
| 6705 | defender | last_player_standing | 0 | 535 | -535 |
| 307 | defender | last_player_standing | 0 | 648 | -648 |
| 6248 | attacker | last_player_standing | 676 | 0 | +676 |
| 6705 | defender | last_player_standing | 0 | 702 | -702 |
| 13752 | defender | last_player_standing | 0 | 932 | -932 |
| 1472 | defender | last_player_standing | 0 | 944 | -944 |
| 11754 | defender | last_player_standing | 0 | 1000 | -1000 |
| 7212 | defender | last_player_standing | 0 | 1273 | -1273 |
| 399 | defender | last_player_standing | 0 | 1315 | -1315 |
| 306 | attacker | last_player_standing | 1343 | 0 | +1343 |
| 3701 | defender | last_player_standing | 0 | 1344 | -1344 |
| 11012 | defender | last_player_standing | 0 | 1347 | -1347 |
| 3701 | defender | last_player_standing | 0 | 1348 | -1348 |
@@ -1,41 +1,41 @@
action_seq,attacker_factions,attacker_player,defender_factions,defender_player,ai_vs_ai,real_winner,sim_winner,winner_match,real_round,sim_round,real_attacker_troops,real_defender_troops,sim_attacker_troops,sim_defender_troops
306,1,AI,2,AI,true,attacker,attacker,true,0,17,1454,0,1343,0
306,1,AI,2,AI,true,attacker,attacker,true,0,19,1454,0,1388,0
307,2,AI,1,AI,true,defender,defender,true,0,16,0,699,0,648
399,2,AI,1,AI,true,defender,defender,true,0,6,0,1310,0,1315
631,1,AI,2,AI,true,defender,defender,true,0,5,0,3766,0,3654
1326,9,AI,6,AI,true,attacker,attacker,true,0,11,1715,0,1701,0
1472,8,AI,3,human,false,defender,defender,true,0,31,0,1593,0,944
2437,2,AI,1,AI,true,attacker,attacker,true,0,15,3146,0,3219,0
2584,1,AI,2,AI,true,defender,defender,true,0,30,0,2028,0,174
2437,2,AI,1,AI,true,attacker,attacker,true,0,16,3146,0,3247,0
2584,1,AI,2,AI,true,defender,defender,true,0,31,0,2028,0,168
2654,2,AI,1,AI,true,defender,defender,true,0,28,0,335,0,467
3536,1,AI,2,AI,true,attacker,attacker,true,0,16,2172,0,1596,0
3701,8,AI,2,AI,true,defender,defender,true,0,30,0,2819,0,1344
3536,1,AI,2,AI,true,attacker,attacker,true,0,12,2172,0,2528,0
3701,8,AI,2,AI,true,defender,defender,true,0,30,0,2819,0,1348
4298,2,AI,1,AI,true,defender,defender,true,0,3,0,3514,0,1977
4576,2,AI,1,AI,true,defender,defender,true,0,5,0,2028,0,1920
6014,2,AI,8,AI,true,defender,defender,true,0,12,0,1942,0,1474
4576,2,AI,1,AI,true,defender,defender,true,0,4,0,2028,0,1986
6014,2,AI,8,AI,true,defender,defender,true,0,6,0,1942,0,2252
6248,7,AI,9,AI,true,attacker,attacker,true,0,25,520,0,676,0
6408,4,human,7,AI,false,attacker,attacker,true,0,10,4156,0,4022,0
6705,1,AI,2,AI,true,defender,defender,true,0,31,0,3316,0,702
6408,4,human,7,AI,false,attacker,attacker,true,0,11,4156,0,4020,0
6705,1,AI,2,AI,true,defender,defender,true,0,31,0,3316,0,535
7212,2,AI,8,AI,true,defender,defender,true,0,8,0,1253,0,1273
8217,8,AI,2,AI,true,attacker,attacker,true,0,29,920,0,1892,0
8217,8,AI,2,AI,true,attacker,attacker,true,0,22,920,0,2167,0
8394,2,AI,8,AI,true,attacker,attacker,true,0,9,2449,0,3186,0
8628,3,human,8,AI,false,attacker,attacker,true,0,5,2874,0,2760,0
9975,2,AI,1,AI,true,defender,defender,true,0,5,0,7420,0,7437
9975,2,AI,1,AI,true,defender,defender,true,0,4,0,7420,0,7840
10229,5,AI,7,AI,true,attacker,attacker,true,0,23,2135,0,1816,0
10631,1,AI,2,AI,true,attacker,attacker,true,0,1,4000,0,4000,0
11011,2,AI,1,AI,true,defender,defender,true,0,4,0,3532,0,3593
11011,2,AI,1,AI,true,defender,defender,true,0,5,0,3532,0,3658
11012,9,AI,6,AI,true,defender,defender,true,0,30,0,846,0,1347
11112,5,AI,4,human,false,defender,defender,true,0,3,0,7595,0,7595
11297,1,AI,2,AI,true,attacker,attacker,true,0,1,2400,0,2400,0
11469,5,AI,9,AI,true,defender,defender,true,0,8,0,787,0,1489
11470,7,AI,5,AI,true,attacker,attacker,true,0,12,3220,0,3400,0
11470,7,AI,5,AI,true,attacker,attacker,true,0,17,3220,0,3425,0
11565,5,AI,7,AI,true,defender,defender,true,0,31,190,3420,78,3600
11662,4,human,9,AI,false,attacker,attacker,true,0,9,7326,0,6627,0
11662,4,human,9,AI,false,attacker,attacker,true,0,8,7326,0,6624,0
11754,7,AI,4,human,false,attacker,defender,false,0,30,2119,0,0,1000
11941,3,human,5,AI,false,attacker,attacker,true,0,6,4901,0,4777,0
11942,4,human,7,AI,false,attacker,attacker,true,0,5,8840,0,8572,0
12136,9,AI,7,AI,true,attacker,attacker,true,0,21,1829,0,1581,0
12137,2,AI,1,AI,true,defender,defender,true,0,8,0,1894,0,1892
12138,3,human,5,AI,false,attacker,attacker,true,0,13,2960,0,1758,0
12765,6,AI,9,AI,true,defender,defender,true,0,9,0,1773,0,1747
12138,3,human,5,AI,false,attacker,attacker,true,0,14,2960,0,2512,0
12765,6,AI,9,AI,true,defender,defender,true,0,11,0,1773,0,1763
13752,2,AI,1,AI,true,defender,defender,true,0,7,0,943,0,932
1 action_seq attacker_factions attacker_player defender_factions defender_player ai_vs_ai real_winner sim_winner winner_match real_round sim_round real_attacker_troops real_defender_troops sim_attacker_troops sim_defender_troops
2 306 1 AI 2 AI true attacker attacker true 0 17 19 1454 0 1343 1388 0
3 307 2 AI 1 AI true defender defender true 0 16 0 699 0 648
4 399 2 AI 1 AI true defender defender true 0 6 0 1310 0 1315
5 631 1 AI 2 AI true defender defender true 0 5 0 3766 0 3654
6 1326 9 AI 6 AI true attacker attacker true 0 11 1715 0 1701 0
7 1472 8 AI 3 human false defender defender true 0 31 0 1593 0 944
8 2437 2 AI 1 AI true attacker attacker true 0 15 16 3146 0 3219 3247 0
9 2584 1 AI 2 AI true defender defender true 0 30 31 0 2028 0 174 168
10 2654 2 AI 1 AI true defender defender true 0 28 0 335 0 467
11 3536 1 AI 2 AI true attacker attacker true 0 16 12 2172 0 1596 2528 0
12 3701 8 AI 2 AI true defender defender true 0 30 0 2819 0 1344 1348
13 4298 2 AI 1 AI true defender defender true 0 3 0 3514 0 1977
14 4576 2 AI 1 AI true defender defender true 0 5 4 0 2028 0 1920 1986
15 6014 2 AI 8 AI true defender defender true 0 12 6 0 1942 0 1474 2252
16 6248 7 AI 9 AI true attacker attacker true 0 25 520 0 676 0
17 6408 4 human 7 AI false attacker attacker true 0 10 11 4156 0 4022 4020 0
18 6705 1 AI 2 AI true defender defender true 0 31 0 3316 0 702 535
19 7212 2 AI 8 AI true defender defender true 0 8 0 1253 0 1273
20 8217 8 AI 2 AI true attacker attacker true 0 29 22 920 0 1892 2167 0
21 8394 2 AI 8 AI true attacker attacker true 0 9 2449 0 3186 0
22 8628 3 human 8 AI false attacker attacker true 0 5 2874 0 2760 0
23 9975 2 AI 1 AI true defender defender true 0 5 4 0 7420 0 7437 7840
24 10229 5 AI 7 AI true attacker attacker true 0 23 2135 0 1816 0
25 10631 1 AI 2 AI true attacker attacker true 0 1 4000 0 4000 0
26 11011 2 AI 1 AI true defender defender true 0 4 5 0 3532 0 3593 3658
27 11012 9 AI 6 AI true defender defender true 0 30 0 846 0 1347
28 11112 5 AI 4 human false defender defender true 0 3 0 7595 0 7595
29 11297 1 AI 2 AI true attacker attacker true 0 1 2400 0 2400 0
30 11469 5 AI 9 AI true defender defender true 0 8 0 787 0 1489
31 11470 7 AI 5 AI true attacker attacker true 0 12 17 3220 0 3400 3425 0
32 11565 5 AI 7 AI true defender defender true 0 31 190 3420 78 3600
33 11662 4 human 9 AI false attacker attacker true 0 9 8 7326 0 6627 6624 0
34 11754 7 AI 4 human false attacker defender false 0 30 2119 0 0 1000
35 11941 3 human 5 AI false attacker attacker true 0 6 4901 0 4777 0
36 11942 4 human 7 AI false attacker attacker true 0 5 8840 0 8572 0
37 12136 9 AI 7 AI true attacker attacker true 0 21 1829 0 1581 0
38 12137 2 AI 1 AI true defender defender true 0 8 0 1894 0 1892
39 12138 3 human 5 AI false attacker attacker true 0 13 14 2960 0 1758 2512 0
40 12765 6 AI 9 AI true defender defender true 0 9 11 0 1773 0 1747 1763
41 13752 2 AI 1 AI true defender defender true 0 7 0 943 0 932
@@ -14,43 +14,43 @@ factions are marked as AI players.
| Action seq | Attacker | Defender | Real winner | Sim winner | Match | Real troops | Sim troops |
| ---: | --- | --- | --- | --- | --- | ---: | ---: |
| 306 | 1 (AI) | 2 (AI) | attacker | attacker | yes | 1454 vs 0 | 1343 vs 0 |
| 306 | 1 (AI) | 2 (AI) | attacker | attacker | yes | 1454 vs 0 | 1388 vs 0 |
| 307 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 699 | 0 vs 648 |
| 399 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 1310 | 0 vs 1315 |
| 631 | 1 (AI) | 2 (AI) | defender | defender | yes | 0 vs 3766 | 0 vs 3654 |
| 1326 | 9 (AI) | 6 (AI) | attacker | attacker | yes | 1715 vs 0 | 1701 vs 0 |
| 1472 | 8 (AI) | 3 (human) | defender | defender | yes | 0 vs 1593 | 0 vs 944 |
| 2437 | 2 (AI) | 1 (AI) | attacker | attacker | yes | 3146 vs 0 | 3219 vs 0 |
| 2584 | 1 (AI) | 2 (AI) | defender | defender | yes | 0 vs 2028 | 0 vs 174 |
| 2437 | 2 (AI) | 1 (AI) | attacker | attacker | yes | 3146 vs 0 | 3247 vs 0 |
| 2584 | 1 (AI) | 2 (AI) | defender | defender | yes | 0 vs 2028 | 0 vs 168 |
| 2654 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 335 | 0 vs 467 |
| 3536 | 1 (AI) | 2 (AI) | attacker | attacker | yes | 2172 vs 0 | 1596 vs 0 |
| 3701 | 8 (AI) | 2 (AI) | defender | defender | yes | 0 vs 2819 | 0 vs 1344 |
| 3536 | 1 (AI) | 2 (AI) | attacker | attacker | yes | 2172 vs 0 | 2528 vs 0 |
| 3701 | 8 (AI) | 2 (AI) | defender | defender | yes | 0 vs 2819 | 0 vs 1348 |
| 4298 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 3514 | 0 vs 1977 |
| 4576 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 2028 | 0 vs 1920 |
| 6014 | 2 (AI) | 8 (AI) | defender | defender | yes | 0 vs 1942 | 0 vs 1474 |
| 4576 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 2028 | 0 vs 1986 |
| 6014 | 2 (AI) | 8 (AI) | defender | defender | yes | 0 vs 1942 | 0 vs 2252 |
| 6248 | 7 (AI) | 9 (AI) | attacker | attacker | yes | 520 vs 0 | 676 vs 0 |
| 6408 | 4 (human) | 7 (AI) | attacker | attacker | yes | 4156 vs 0 | 4022 vs 0 |
| 6705 | 1 (AI) | 2 (AI) | defender | defender | yes | 0 vs 3316 | 0 vs 702 |
| 6408 | 4 (human) | 7 (AI) | attacker | attacker | yes | 4156 vs 0 | 4020 vs 0 |
| 6705 | 1 (AI) | 2 (AI) | defender | defender | yes | 0 vs 3316 | 0 vs 535 |
| 7212 | 2 (AI) | 8 (AI) | defender | defender | yes | 0 vs 1253 | 0 vs 1273 |
| 8217 | 8 (AI) | 2 (AI) | attacker | attacker | yes | 920 vs 0 | 1892 vs 0 |
| 8217 | 8 (AI) | 2 (AI) | attacker | attacker | yes | 920 vs 0 | 2167 vs 0 |
| 8394 | 2 (AI) | 8 (AI) | attacker | attacker | yes | 2449 vs 0 | 3186 vs 0 |
| 8628 | 3 (human) | 8 (AI) | attacker | attacker | yes | 2874 vs 0 | 2760 vs 0 |
| 9975 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 7420 | 0 vs 7437 |
| 9975 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 7420 | 0 vs 7840 |
| 10229 | 5 (AI) | 7 (AI) | attacker | attacker | yes | 2135 vs 0 | 1816 vs 0 |
| 10631 | 1 (AI) | 2 (AI) | attacker | attacker | yes | 4000 vs 0 | 4000 vs 0 |
| 11011 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 3532 | 0 vs 3593 |
| 11011 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 3532 | 0 vs 3658 |
| 11012 | 9 (AI) | 6 (AI) | defender | defender | yes | 0 vs 846 | 0 vs 1347 |
| 11112 | 5 (AI) | 4 (human) | defender | defender | yes | 0 vs 7595 | 0 vs 7595 |
| 11297 | 1 (AI) | 2 (AI) | attacker | attacker | yes | 2400 vs 0 | 2400 vs 0 |
| 11469 | 5 (AI) | 9 (AI) | defender | defender | yes | 0 vs 787 | 0 vs 1489 |
| 11470 | 7 (AI) | 5 (AI) | attacker | attacker | yes | 3220 vs 0 | 3400 vs 0 |
| 11470 | 7 (AI) | 5 (AI) | attacker | attacker | yes | 3220 vs 0 | 3425 vs 0 |
| 11565 | 5 (AI) | 7 (AI) | defender | defender | yes | 190 vs 3420 | 78 vs 3600 |
| 11662 | 4 (human) | 9 (AI) | attacker | attacker | yes | 7326 vs 0 | 6627 vs 0 |
| 11662 | 4 (human) | 9 (AI) | attacker | attacker | yes | 7326 vs 0 | 6624 vs 0 |
| 11754 | 7 (AI) | 4 (human) | attacker | defender | no | 2119 vs 0 | 0 vs 1000 |
| 11941 | 3 (human) | 5 (AI) | attacker | attacker | yes | 4901 vs 0 | 4777 vs 0 |
| 11942 | 4 (human) | 7 (AI) | attacker | attacker | yes | 8840 vs 0 | 8572 vs 0 |
| 12136 | 9 (AI) | 7 (AI) | attacker | attacker | yes | 1829 vs 0 | 1581 vs 0 |
| 12137 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 1894 | 0 vs 1892 |
| 12138 | 3 (human) | 5 (AI) | attacker | attacker | yes | 2960 vs 0 | 1758 vs 0 |
| 12765 | 6 (AI) | 9 (AI) | defender | defender | yes | 0 vs 1773 | 0 vs 1747 |
| 12138 | 3 (human) | 5 (AI) | attacker | attacker | yes | 2960 vs 0 | 2512 vs 0 |
| 12765 | 6 (AI) | 9 (AI) | defender | defender | yes | 0 vs 1773 | 0 vs 1763 |
| 13752 | 2 (AI) | 1 (AI) | defender | defender | yes | 0 vs 943 | 0 vs 932 |
@@ -1143,32 +1143,36 @@ TEST_F(AIIntegrationTest, StandardScorer_ArcheryEvaluationBeatsEndTurnWithSingle
const double currentScore = scorer->GuessedStateScore(false, gameState, strategy, castleCoords);
AICommandEvaluator evaluator(*scorer, apdCache, battalionTypeGetter);
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10);
const double archeryScore = evaluator
.EvaluateCommand(
0,
false,
0,
0,
engine,
strategy,
currentScore,
castleCoords,
std::distance(commands->begin(), archeryCommand),
deadline)
.get();
const double endTurnScore = evaluator
.EvaluateCommand(
0,
false,
0,
0,
engine,
strategy,
currentScore,
castleCoords,
std::distance(commands->begin(), endTurnCommand),
deadline)
.get();
const auto archeryResult = evaluator
.EvaluateCommand(
0,
false,
0,
0,
engine,
strategy,
currentScore,
castleCoords,
std::distance(commands->begin(), archeryCommand),
deadline)
.get();
const auto endTurnResult = evaluator
.EvaluateCommand(
0,
false,
0,
0,
engine,
strategy,
currentScore,
castleCoords,
std::distance(commands->begin(), endTurnCommand),
deadline)
.get();
ASSERT_TRUE(archeryResult.completed);
ASSERT_TRUE(endTurnResult.completed);
const double archeryScore = archeryResult.score;
const double endTurnScore = endTurnResult.score;
EXPECT_TRUE(std::isfinite(archeryScore));
EXPECT_GT(archeryScore, endTurnScore)
@@ -215,3 +215,30 @@ cc_test(
"@googletest//:gtest_main",
],
)
cc_test(
name = "real_battle_decision_regression_test",
size = "large",
srcs = ["RealBattleDecisionRegressionTest.cpp"],
copts = TEST_COPTS,
data = [
"testdata/kings_loyalists_before_mage_fire.fb",
"//src/main/resources/net/eagle0/shardok:battalion_types",
"//src/main/resources/net/eagle0/shardok:settings",
],
linkstatic = True,
tags = ["exclusive"],
deps = [
"//src/main/cpp/net/eagle0/common:filesystem_utils",
"//src/main/cpp/net/eagle0/common:random_generator",
"//src/main/cpp/net/eagle0/shardok/ai:shardok_ai_client",
"//src/main/cpp/net/eagle0/shardok/ai:transposition_table",
"//src/main/cpp/net/eagle0/shardok/ai_testing_common:ai_client_factory",
"//src/main/cpp/net/eagle0/shardok/ai_testing_common:game_settings_factory",
"//src/main/cpp/net/eagle0/shardok/library:engine",
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
"//src/main/cpp/net/eagle0/shardok/library/action_point_distances:action_point_distances_cache",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
)
@@ -0,0 +1,78 @@
#include <gtest/gtest.h>
#include <filesystem>
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIConfig.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/ShardokAIClient.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/TranspositionTable.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai_testing_common/AIClientFactory.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai_testing_common/GameSettingsFactory.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/ShardokEngine.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
namespace shardok {
extern TranspositionTable g_transpositionTable;
namespace {
using net::eagle0::shardok::common::START_FIRE_COMMAND;
constexpr PlayerId kAttackerPlayerId = 0;
class RealBattleDecisionRegressionTest : public testing::Test {
protected:
void SetUp() override {
auto path = std::filesystem::current_path();
path.append(
"bazel-bin/src/test/cpp/net/eagle0/shardok/ai/"
"real_battle_decision_regression_test");
FilesystemUtils::SetExecPath(path);
g_transpositionTable.clear();
ActionPointDistancesCache::ClearThreadLocalCache();
settings = ai_testing_common::GameSettingsFactory::CreateDefault();
auto setter = settings->GetSetter();
setter.SetInt("maxRounds", 31);
setter.SetInt("minLookaheadTurns", 1);
setter.SetDouble("allAiBattleTimeBudgetMaximum", 0.2);
setter.SetRandomGenerator(std::make_shared<StdLibraryGenerator>(1));
}
GameSettingsSPtr settings;
};
TEST_F(RealBattleDecisionRegressionTest, DoesNotChooseKingsLoyalistsMageDistantFire) {
// Real battle 68f300179888acd7_52_2add2586 immediately before recorded action 70.
// The live stream chose START_FIRE with this mage at (5, 0); the current deterministic
// test budget reproduces the same bad class of choice at a nearby distant tile.
const auto fixturePath =
std::filesystem::current_path() /
"src/test/cpp/net/eagle0/shardok/ai/testdata/kings_loyalists_before_mage_fire.fb";
const auto state = GameStateW::LoadFrom(fixturePath.string());
const ShardokEngine engine(settings, state);
const auto commands = engine.GetAvailableCommandsForAIPlayer(kAttackerPlayerId);
const auto ai = ai_testing_common::AIClientFactory::Create(
kAttackerPlayerId,
false,
state->hex_map(),
settings->GetGetter(),
AIAlgorithmType::ITERATIVE_DEEPENING,
ScoringCalculatorType::STANDARD,
true);
const auto choice = ai->ChooseCommandIndex(engine);
ASSERT_LT(choice.chosenIndex, commands->size());
const auto& chosenCommand = commands->at(choice.chosenIndex);
EXPECT_FALSE(
chosenCommand->GetCommandType() == START_FIRE_COMMAND &&
chosenCommand->GetActorUnitId() == 3);
}
} // namespace
} // namespace shardok