in csrc/liars_dice/subgame_solving.cc [931:973]
std::vector<double> compute_ev(const Game& game, const TreeStrategy& strategy1,
const TreeStrategy& strategy2) {
auto tree = unroll_tree(game);
assert(tree.size() == strategy1.size());
assert(tree.size() == strategy2.size());
std::vector<std::vector<double>> op_reach_probabilities;
init_nd(tree.size(), game.num_hands(), 0.0, &op_reach_probabilities);
// values[node][hand] :=
// sum_{z, node->z} sum_{op_hand}
// P(op_hand) pi^{-i}(z|op_hand) pi^{i}(node -> z|hand) U_i(hand, op_hand, z)
std::vector<std::vector<double>> values(tree.size());
const int player = 0;
compute_reach_probabilities(tree, strategy2, get_initial_beliefs(game)[0],
1 - player, &op_reach_probabilities);
for (size_t node_id = tree.size(); node_id-- > 0;) {
const auto& node = tree[node_id];
const auto& state = node.state;
if (node.num_children() == 0) {
assert(game.is_terminal(state));
const auto last_bid = tree[node.parent].state.last_bid;
values[node_id] = compute_expected_terminal_values(
game, last_bid, /*inverse=*/state.player_id != player,
op_reach_probabilities[node_id]);
} else if (state.player_id == player) {
values[node_id].resize(game.num_hands());
for (auto [child_node_id, action] : ChildrenActionIt(node, game)) {
for (int hand = 0; hand < game.num_hands(); ++hand) {
values[node_id][hand] +=
strategy1[node_id][hand][action] * values[child_node_id][hand];
}
}
} else {
values[node_id].resize(game.num_hands());
for (auto child_node_id : ChildrenIt(node)) {
for (int hand = 0; hand < game.num_hands(); ++hand) {
values[node_id][hand] += values[child_node_id][hand];
}
}
}
}
return values[0];
}