/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package bots.mctsbot.ai.bots.bot.gametree.search;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import bots.mctsbot.ai.bots.bot.gametree.action.ActionWrapper;
import bots.mctsbot.ai.bots.bot.gametree.search.expander.SamplingExpander;
import bots.mctsbot.ai.bots.bot.gametree.search.expander.WeightedNode;
import bots.mctsbot.ai.bots.bot.gametree.search.expander.sampling.Sampler;
import bots.mctsbot.ai.bots.bot.gametree.search.nodevisitor.NodeVisitor;
import bots.mctsbot.client.common.gamestate.GameState;
import bots.mctsbot.common.elements.player.PlayerId;
import bots.mctsbot.common.util.Pair;
public class OpponentActionNode extends ActionNode {
@SuppressWarnings("unused")
private final static Logger logger = Logger.getLogger(OpponentActionNode.class);
private final SamplingExpander expander;
private Distribution valueDistribution = null;
public OpponentActionNode(PlayerId opponentId, PlayerId botId, GameState gameState, SearchConfiguration config, Sampler sampler, int tokens, int searchId,
NodeVisitor... visitors) {
super(opponentId, botId, gameState, config, searchId, visitors);
expander = new SamplingExpander(this, tokens, sampler);
}
@Override
public Distribution getValueDistribution(double lowerBound) {
if (valueDistribution == null) {
config.getOpponentModel().assumeTemporarily(gameState);
List<Pair<ActionWrapper, WeightedNode>> children = getExpander().getWeightedChildren(config.isUniformBotActionTokens());
double percentageDone = 0;
double valueDone = 0;
double maxToDo = 0;
List<Distribution> valueDistributions = new ArrayList<Distribution>(children.size());
for (Pair<ActionWrapper, WeightedNode> pair : children) {
WeightedNode child = pair.getRight();
double prob = child.getWeight();
double upperWinBound = child.getNode().getUpperWinBound();
maxToDo += prob * upperWinBound;
}
for (int i = 0; i < children.size(); i++) {
Pair<ActionWrapper, WeightedNode> pair = children.get(i);
WeightedNode child = pair.getRight();
double upperWinBound = child.getNode().getUpperWinBound();
double prob = child.getWeight();
maxToDo -= prob * upperWinBound;
int requiredFromSubtree = (int) ((lowerBound - valueDone - maxToDo) / prob);
if (config.isUseAlphaBetaPruning() && requiredFromSubtree > upperWinBound) {
//prune
for (int j = i; j < children.size(); j++) {
for (NodeVisitor visitor : visitors) {
Pair<ActionWrapper, WeightedNode> skipped = children.get(j);
Pair<ActionWrapper, GameTreeNode> node = new Pair<ActionWrapper, GameTreeNode>(skipped.getLeft(), skipped.getRight().getNode());
visitor.pruneSubTree(node, new Distribution(node.getRight().getUpperWinBound(), 0, true), requiredFromSubtree);
}
}
valueDistribution = new Distribution(valueDone + prob * upperWinBound + maxToDo, 0.0, true);
//forget last game state!
config.getOpponentModel().forgetLastAssumption();
return valueDistribution;
}
percentageDone += prob;
for (NodeVisitor visitor : visitors) {
visitor.enterNode(new Pair<ActionWrapper, GameTreeNode>(pair.getLeft(), pair.getRight().getNode()), requiredFromSubtree);
}
Distribution valueDistribution = child.getNode().getValueDistribution(requiredFromSubtree);
for (NodeVisitor visitor : visitors) {
visitor.leaveNode(new Pair<ActionWrapper, GameTreeNode>(pair.getLeft(), pair.getRight().getNode()), valueDistribution);
}
valueDone += prob * valueDistribution.getMean();
valueDistributions.add(valueDistribution);
}
config.getOpponentModel().forgetLastAssumption();
// see Variance Estimation and Ranking of Gaussian Mixture Distributions
// in Target Tracking Applications
// Lidija Trailovi and Lucy Y. Pao
double varEV = 0;
for (int i = 0; i < valueDistributions.size(); i++) {
Distribution valueDistribution = valueDistributions.get(i);
double m = valueDistribution.getMean();
double var = valueDistribution.getVariance();
double w = children.get(i).getRight().getWeight();
varEV += w * (var + m * m);
}
valueDistribution = new Distribution(valueDone, Math.max(0, varEV - valueDone));
}
return valueDistribution;
}
public SamplingExpander getExpander() {
return expander;
}
public int getNbTokens() {
return expander.tokens;
}
@Override
public String toString() {
return "Opponent " + playerId + " Action Node";
}
}