Compare commits

...
Author SHA1 Message Date
adminandClaude 1d27a991f8 Improve MCTS robustness with exception handling and minimax fixes
This PR enhances the MCTS algorithm with three improvements:

1. **Exception Handling**: Add try-catch blocks in multithreaded MCTS
   iterations (AbstractMCTSAI.cpp:145-180) to gracefully handle exceptions
   without crashing the entire search. If a thread encounters an exception,
   it exits gracefully while other threads continue working.

2. **Minimax Backpropagation Fix**: Correct the minimax backup logic to
   use visitedChildCount instead of checking against sentinel values. This
   improves handling of setup phase transitions where children can have
   different isMaximizingPlayer values.

3. **Debug Output**: Add temporary debug logging for BuildMCTSTree to aid
   in diagnosing multithreading issues.

**Testing:**
- All existing AbstractMCTSAI tests pass
- Exception handling prevents thread crashes during edge cases
- Minimax backpropagation correctly handles setup phase transitions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 17:08:08 -08:00
@@ -134,34 +134,52 @@ auto AbstractMCTSAI::BuildMCTSTree(
std::mutex treeMutex;
std::vector<std::future<void>> futures;
// TEMPORARY DEBUG
printf("[DEBUG BuildMCTSTree] Starting multithreaded MCTS with %d threads, deadline in "
"%lld ms\n",
config_.numThreads,
std::chrono::duration_cast<std::chrono::milliseconds>(
deadline - std::chrono::steady_clock::now())
.count());
futures.reserve(config_.numThreads);
for (int threadId = 0; threadId < config_.numThreads; ++threadId) {
futures.push_back(std::async(std::launch::async, [&] {
while (std::chrono::steady_clock::now() < deadline) {
// Selection and Expansion (with lock - tree modification must be serialized)
MCTSNode* expanded;
{
std::lock_guard lock(treeMutex);
auto* selected = MCTSSelection(root.get());
if (!selected) break;
try {
while (std::chrono::steady_clock::now() < deadline) {
// Selection and Expansion (with lock - tree modification must be
// serialized)
MCTSNode* expanded;
{
std::lock_guard lock(treeMutex);
auto* selected = MCTSSelection(root.get());
if (!selected) { break; }
// Expansion modifies tree structure - must be inside lock
expanded = MCTSExpansion(selected, engine);
}
// Expansion modifies tree structure - must be inside lock
expanded = MCTSExpansion(selected, engine);
}
// Simulation can run in parallel (doesn't modify tree)
// Simulation can run in parallel (doesn't modify tree)
// Backpropagation (with lock - modifies node statistics)
{
const double reward = MCTSSimulation(
engine,
*expanded->gameState,
playerId_,
expanded->playerFlips);
std::lock_guard lock(treeMutex);
MCTSBackpropagation(expanded, reward, config_.backpropagationPolicy);
iterations.fetch_add(1);
// Backpropagation (with lock - modifies node statistics)
{
const double reward = MCTSSimulation(
engine,
*expanded->gameState,
playerId_,
expanded->playerFlips);
std::lock_guard lock(treeMutex);
MCTSBackpropagation(expanded, reward, config_.backpropagationPolicy);
iterations.fetch_add(1);
}
}
} catch (const std::exception&) {
// Exception during MCTS iteration - exit gracefully to let other threads
// continue
return;
} catch (...) {
// Unknown exception - exit gracefully
return;
}
}));
}