/**
* 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.nodevisitor;
import java.util.ArrayDeque;
import java.util.Deque;
import bots.mctsbot.ai.bots.bot.gametree.action.ActionWrapper;
import bots.mctsbot.ai.bots.bot.gametree.search.Distribution;
import bots.mctsbot.ai.bots.bot.gametree.search.GameTreeNode;
import bots.mctsbot.common.util.Pair;
public abstract class TextOutputVisitor implements NodeVisitor {
Deque<String> stack = new ArrayDeque<String>();
private final int maxDepth;
private int depth = 0;
public TextOutputVisitor() {
this(Integer.MAX_VALUE);
}
public TextOutputVisitor(int maxDepth) {
stack.push("");
stack.push(getPrefixElement());
this.maxDepth = maxDepth;
}
@Override
public void enterNode(Pair<ActionWrapper, GameTreeNode> pair, double lowerBound) {
depth++;
if (depth <= maxDepth) {
String prefix = stack.peek();
output(prefix + getNewNodePrefix() + getNodeDescription(pair, pair.getRight().getNbTokens()));
stack.push(prefix + getPrefixElement());
}
}
@Override
public void leaveNode(Pair<ActionWrapper, GameTreeNode> node, Distribution distr) {
if (depth <= maxDepth) {
stack.pop();
String prefix = stack.peek();
output(prefix + getNodeEndPrefix() + getEndNodeDescription(node, distr));
}
depth--;
}
@Override
public void pruneSubTree(Pair<ActionWrapper, GameTreeNode> node, Distribution distribution, double lowerBound) {
enterNode(node, lowerBound);
leaveNode(node, distribution);
}
@Override
public void visitLeafNode(int winnings, double probability, int minWinnable, int maxWinnable) {
if (depth + 1 <= maxDepth) {
String prefix = stack.peek();
output(prefix + getNewNodePrefix() + getNodeDescription(winnings, probability, minWinnable, maxWinnable));
}
}
@Override
public void callOpponentModel() {
// no op
}
protected String getNodeDescription(int winnings, double probability, int minWinnable, int maxWinnable) {
if (winnings == maxWinnable) {
return "Win, " + winnings + " (" + Math.round(100 * probability) + "%)";
} else if (winnings == minWinnable) {
return "Lose, " + winnings + " (" + Math.round(100 * probability) + "%)";
} else
return "Draw, " + winnings + " (" + Math.round(100 * probability) + "%)";
}
protected String getNodeDescription(Pair<ActionWrapper, GameTreeNode> node, int tokens) {
return node.getLeft() + " in " + node.getRight() + " with " + tokens + " token" + (tokens > 1 ? "s" : "") + " in "
+ node.getRight().getGameState().getRound();
}
protected String getEndNodeDescription(Pair<ActionWrapper, GameTreeNode> node, Distribution value) {
String bound = value.isUpperBound() ? "<" : "";
return "EV is " + bound + Math.round(value.getMean()) + " for " + node.getRight().toString() + " s=" + Math.round(Math.sqrt(value.getVariance())) + ")";
}
protected String getPrefixElement() {
return " |";
}
protected String getNewNodePrefix() {
return "---o ";
}
protected String getNodeEndPrefix() {
return " `";
}
protected abstract void output(String line);
}