mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 01:35:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1094976824 | ||
|
|
383f770a2d |
@@ -301,14 +301,22 @@ auto AbstractMCTSAI::MCTSSimulation(
|
||||
const int startingPlayerFlips) const -> double {
|
||||
if (state.isTerminal()) { return state.score(startingPlayer); }
|
||||
|
||||
// If we've already exceeded the simulation horizon, don't simulate - just return immediate
|
||||
// score This ensures fair comparison: all leaves are evaluated at the same game phase Example:
|
||||
// maxSimulationFlips=1 means simulate THROUGH opponent's first response (i.e., allow one action
|
||||
// at playerFlips=1, then stop)
|
||||
if (startingPlayerFlips > config_.maxSimulationFlips) { return state.score(startingPlayer); }
|
||||
|
||||
// Create a mutable copy for simulation
|
||||
auto currentState = state.clone();
|
||||
int depth = 0;
|
||||
int playerFlips = startingPlayerFlips; // Start from the expanded node's flip count
|
||||
MCTSPlayerId previousPlayer = currentState->currentPlayerId();
|
||||
|
||||
// Simulate until terminal or max depth
|
||||
while (!currentState->isTerminal() && depth < config_.maxSimulationDepth) {
|
||||
// Simulate until we exceed the horizon, hit terminal state, or max depth
|
||||
// Note: We allow one action AT maxSimulationFlips before stopping
|
||||
while (!currentState->isTerminal() && depth < config_.maxSimulationDepth &&
|
||||
playerFlips <= config_.maxSimulationFlips) {
|
||||
// Track player changes
|
||||
const MCTSPlayerId currentPlayer = currentState->currentPlayerId();
|
||||
if (currentPlayer != previousPlayer) {
|
||||
@@ -321,7 +329,7 @@ auto AbstractMCTSAI::MCTSSimulation(
|
||||
*currentState,
|
||||
playerId_,
|
||||
playerFlips,
|
||||
config_.maxPlayerFlips);
|
||||
config_.maxSimulationFlips);
|
||||
if (actions.empty()) { break; }
|
||||
|
||||
// Determine if current player is maximizing or minimizing
|
||||
|
||||
@@ -44,8 +44,13 @@ struct MCTSConfig {
|
||||
int numThreads = 16; // Number of threads for parallel MCTS
|
||||
MCTSSimulationPolicy simulationPolicy = MCTSSimulationPolicy::BEST_IMMEDIATE;
|
||||
MCTSBackpropagationPolicy backpropagationPolicy = MCTSBackpropagationPolicy::AVERAGING;
|
||||
int maxPlayerFlips = 0; // Maximum number of player changes to explore (0 = stop at first
|
||||
// flip, 1 = explore through opponent's response, etc.)
|
||||
int maxPlayerFlips = 0; // Maximum number of player changes for tree expansion
|
||||
// (0 = expand through current player's turn only,
|
||||
// 1 = expand through opponent's first response, etc.)
|
||||
int maxSimulationFlips = 0; // Maximum player flips for leaf evaluation
|
||||
// When evaluating a leaf at playerFlips < maxSimulationFlips,
|
||||
// simulate forward to this phase for fair comparison
|
||||
// (default 0 = evaluate leaves as-is, backward compatible)
|
||||
std::string debugDumpPath = ""; // If non-empty, dump MCTS tree to this file path
|
||||
};
|
||||
|
||||
|
||||
@@ -135,20 +135,31 @@ auto ShardokAIClient::StandardChooseCommandIndex(
|
||||
// - MINIMAX correctly models opponent choosing best response
|
||||
// - Explores through one opponent turn for tactical accuracy
|
||||
auto adjustedMCTSConfig = mctsConfig;
|
||||
// adjustedMCTSConfig.maxPlayerFlips = 0;
|
||||
// if (timeBudget.isCloseToEnemy) {
|
||||
// adjustedMCTSConfig.maxPlayerFlips = 1;
|
||||
// adjustedMCTSConfig.backpropagationPolicy = mcts::MCTSBackpropagationPolicy::MINIMAX;
|
||||
// if constexpr (kPerformanceLogging) {
|
||||
// printf("MCTS Config: Close to enemy - using maxPlayerFlips=1, MINIMAX backprop\n");
|
||||
// }
|
||||
// } else {
|
||||
// adjustedMCTSConfig.maxPlayerFlips = 0;
|
||||
// adjustedMCTSConfig.backpropagationPolicy = mcts::MCTSBackpropagationPolicy::AVERAGING;
|
||||
// if constexpr (kPerformanceLogging) {
|
||||
// printf("MCTS Config: Far from enemy - using maxPlayerFlips=0, AVERAGING backprop\n");
|
||||
// }
|
||||
// }
|
||||
|
||||
// Enable adaptive MCTS strategy based on tactical situation
|
||||
if (timeBudget.isCloseToEnemy) {
|
||||
// Close to enemy: use adversarial search with MINIMAX backpropagation
|
||||
adjustedMCTSConfig.maxPlayerFlips = 1;
|
||||
adjustedMCTSConfig.backpropagationPolicy = mcts::MCTSBackpropagationPolicy::MINIMAX;
|
||||
// For fair evaluation: simulate to next player flip (opponent's second action)
|
||||
// This ensures leaves at playerFlips=0 and playerFlips=1 are compared fairly
|
||||
adjustedMCTSConfig.maxSimulationFlips = 2;
|
||||
if constexpr (kPerformanceLogging) {
|
||||
printf("MCTS Config: Close to enemy - maxPlayerFlips=1, maxSimulationFlips=2, MINIMAX "
|
||||
"backprop\n");
|
||||
}
|
||||
} else {
|
||||
// Far from enemy: use single-player search with AVERAGING backpropagation
|
||||
adjustedMCTSConfig.maxPlayerFlips = 0;
|
||||
adjustedMCTSConfig.backpropagationPolicy = mcts::MCTSBackpropagationPolicy::AVERAGING;
|
||||
// For fair evaluation: simulate to first player flip (opponent's first action)
|
||||
// This ensures leaves in middle of our turn are compared at consistent phase
|
||||
adjustedMCTSConfig.maxSimulationFlips = 1;
|
||||
if constexpr (kPerformanceLogging) {
|
||||
printf("MCTS Config: Far from enemy - maxPlayerFlips=0, maxSimulationFlips=1, "
|
||||
"AVERAGING backprop\n");
|
||||
}
|
||||
}
|
||||
|
||||
assert(commandCount == realAvailableCommands->size());
|
||||
// Verify that the AI's guessed state produces the same available commands as reality
|
||||
|
||||
@@ -287,6 +287,7 @@ TEST_F(MCTSIntegrationTest, AdaptiveExplorationExploitation) {
|
||||
TEST_F(MCTSIntegrationTest, EarlyTerminationOnWin_MaxPlayerFlips0) {
|
||||
// Explicitly set maxPlayerFlips=0 for single-player MCTS
|
||||
config_.maxPlayerFlips = 0;
|
||||
config_.maxSimulationFlips = 1; // Simulate to opponent's turn for fair evaluation
|
||||
|
||||
// If MCTS finds a guaranteed win, it should recognize it quickly
|
||||
// X X . <- Position 2 wins
|
||||
@@ -313,6 +314,7 @@ TEST_F(MCTSIntegrationTest, EarlyTerminationOnWin_MaxPlayerFlips0) {
|
||||
TEST_F(MCTSIntegrationTest, EarlyTerminationOnWin_MaxPlayerFlips1) {
|
||||
// Explicitly set maxPlayerFlips=1 for two-player adversarial MCTS
|
||||
config_.maxPlayerFlips = 1;
|
||||
config_.maxSimulationFlips = 2; // Simulate to next player flip for deeper evaluation
|
||||
|
||||
// If MCTS finds a guaranteed win, it should recognize it quickly
|
||||
// X X . <- Position 2 wins
|
||||
@@ -342,6 +344,7 @@ TEST_F(MCTSIntegrationTest, EarlyTerminationOnWin_MaxPlayerFlips8) {
|
||||
// Explicitly set maxPlayerFlips=8 for full game tree exploration
|
||||
// (TicTacToe has max 9 moves = 8 player flips)
|
||||
config_.maxPlayerFlips = 8;
|
||||
config_.maxSimulationFlips = 9; // Simulate to game completion
|
||||
|
||||
// If MCTS finds a guaranteed win, it should recognize it quickly
|
||||
// X X . <- Position 2 wins
|
||||
|
||||
Reference in New Issue
Block a user