Compare commits

...
Author SHA1 Message Date
admin 478ad98081 Simplify MCTS scoring to always use root player's perspective
Replaced complex playerId-to-role mapping logic with simpler approach that
always uses the root player's perspective (isDefender_).

The previous implementation tried to determine the requested player's role
from the game state and score from their perspective. However, MCTS scoring
should always be from the root player's perspective - the adversarial logic
(negating scores for opponent moves) happens in the MCTS selection algorithm,
not in the state scoring function.

This simplification:
- Makes the scoring behavior more predictable
- Aligns with MCTS best practices (fixed evaluation perspective)
- Removes unnecessary game state lookups
- Clarifies the separation of concerns between scoring and selection
2025-11-09 07:14:18 -08:00
@@ -42,31 +42,12 @@ uint64_t ShardokGameState::hash() const {
}
double ShardokGameState::score(MCTSPlayerId playerId) const {
// Honor the interface contract: score() should return evaluation from playerId's perspective.
// Map the requested playerId to defender/attacker role to determine scoring perspective.
// Look up which player ID is the defender from game state
bool foundDefender = false;
bool requestedPlayerIsDefender = false;
if (state_->player_infos()) {
for (const auto* pi : *state_->player_infos()) {
if (pi && pi->is_defender()) {
foundDefender = true;
requestedPlayerIsDefender = (static_cast<PlayerId>(playerId) == pi->player_id());
break;
}
}
}
// Fallback: if we can't determine from game state, use isDefender_ which represents
// the root player's role (and playerId is always the root player in practice)
const bool scoreFromDefenderPerspective =
foundDefender ? requestedPlayerIsDefender : isDefender_;
// Call score calculator with correct perspective for the requested player
return scoreCalculator_
->GuessedStateScore(scoreFromDefenderPerspective, state_, strategy_, castleCoords_);
(void)playerId; // Intentionally unused - we use isDefender_ (fixed at root)
// MCTS scoring: Always use root player's perspective (isDefender_) for evaluation.
// The playerId parameter represents the root player making decisions in the MCTS tree.
// All immediate scores and leaf evaluations must be from this fixed perspective.
// Adversarial logic (negation for opponent) happens in MCTS selection, not here.
return scoreCalculator_->GuessedStateScore(isDefender_, state_, strategy_, castleCoords_);
}
MCTSPlayerId ShardokGameState::currentPlayerId() const { return state_->current_player(); }