Compare commits

...
Author SHA1 Message Date
admin 5018f882d8 Fix critical error-hiding fallbacks in AbstractMCTSAI
Two issues fixed in pre-existing code:

1. Line 35: No tree built - Now properly distinguishes between:
   - null root (throws MCTSInternalError)
   - 0 actions available (throws - cannot return valid index)
   - 1 action (returns index 0, legitimate early exit)
   - Multiple actions but no children (throws - BuildMCTSTree bug)

2. Line 404: Empty actions list - Now throws instead of returning 0
   (which would be an invalid index into an empty list)

Defensive fallbacks retained:
- FILTERED_RANDOM falls back to random from all actions (reasonable)
- BEST_IMMEDIATE falls back to first action (reasonable)

These fallbacks are acceptable defensive programming against overly
aggressive filtering and don't hide bugs.
2025-10-26 21:59:17 -07:00
@@ -31,11 +31,31 @@ auto AbstractMCTSAI::Search(
result.searchTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - startTime);
if (!rootNode || rootNode->children.empty()) {
// Fallback to first action if no tree was built
result.bestActionIndex = 0;
result.bestScore = 0.0;
return result;
if (!rootNode) {
throw MCTSInternalError("MCTS search: BuildMCTSTree returned null root node");
}
if (rootNode->children.empty()) {
// This can happen legitimately when:
// 1. No legal actions available (terminal state)
// 2. Only one action and we early-exited without exploring
// In case 1, there's no valid action to return. In case 2, we should have
// expanded that one child. Check totalActions to distinguish.
if (rootNode->totalActions == 0) {
throw MCTSInternalError(
"MCTS search: No legal actions available in initial state - cannot return an "
"action index");
}
// Single action case - should have been expanded in BuildMCTSTree
if (rootNode->totalActions == 1) {
result.bestActionIndex = 0;
result.bestScore = 0.0;
return result;
}
// Multiple actions but no children expanded - this shouldn't happen
throw MCTSInternalError(
"MCTS search: Root has " + std::to_string(rootNode->totalActions) +
" actions but no children expanded - this indicates a bug in BuildMCTSTree");
}
// Find best child
@@ -381,7 +401,11 @@ auto AbstractMCTSAI::SelectSimulationAction(
const MCTSGameState& state,
const std::vector<std::unique_ptr<MCTSAction>>& actions,
const bool isMaximizing) const -> size_t {
if (actions.empty()) { return 0; }
if (actions.empty()) {
throw MCTSInternalError(
"MCTSSimulation called with empty actions list - this indicates a bug in the "
"MCTS tree building or game state");
}
thread_local std::mt19937 gen(std::random_device{}());