package net.sf.colossus.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.colossus.variant.CreatureType;
import net.sf.colossus.variant.Variant;
/**
* Predicts splits for one enemy player, and adjusts predictions as
* creatures are revealed.
*
* @author David Ripton
* See docs/SplitPrediction.txt
*/
public final class PredictSplits
{
private static final Logger LOGGER = Logger.getLogger(PredictSplits.class
.getName());
private final PredictSplitNode root; // All contents of root must be known.
private final NodeTurnComparator nodeTurnComparator = new NodeTurnComparator();
PredictSplits(String rootId, List<CreatureType> creatureTypes,
Variant variant)
{
assert variant != null;
CreatureInfoList infoList = new CreatureInfoList();
for (CreatureType type : creatureTypes)
{
CreatureInfo ci = new CreatureInfo(type, true, true);
infoList.add(ci);
}
root = new PredictSplitNode(rootId, 0, infoList, null, variant);
}
/** Return all non-empty childless nodes in subtree starting from node. */
List<PredictSplitNode> getLeaves(PredictSplitNode node)
{
List<PredictSplitNode> leaves = new ArrayList<PredictSplitNode>();
if (node.getChild1() == null)
{
if (!node.getCreatures().isEmpty())
{
leaves.add(node);
}
}
else
{
leaves.addAll(getLeaves(node.getChild1()));
leaves.addAll(getLeaves(node.getChild2()));
}
TreeSet<Integer> prunes = new TreeSet<Integer>(
new ReverseIntegerComparator());
// If duplicate markerIds, prune the older node.
for (int i = 0; i < leaves.size(); i++)
{
for (int j = 0; j < leaves.size(); j++)
{
if (i != j)
{
PredictSplitNode leaf1 = leaves.get(i);
PredictSplitNode leaf2 = leaves.get(j);
if (leaf1.getMarkerId().equals(leaf2.getMarkerId()))
{
assert leaf1.getTurnCreated() != leaf2
.getTurnCreated() : "Leaf nodes have to have different markerId or turn";
if (leaf1.getTurnCreated() < leaf2.getTurnCreated())
{
prunes.add(Integer.valueOf(i));
}
else
{
prunes.add(Integer.valueOf(j));
}
}
}
}
}
// Remove in reverse order to keep indexes consistent.
for (Integer in : prunes)
{
leaves.remove(in.intValue());
}
return leaves;
}
/** Return all non-empty nodes in subtree starting from node. */
List<PredictSplitNode> getNodes(PredictSplitNode node)
{
List<PredictSplitNode> nodes = new ArrayList<PredictSplitNode>();
if (!node.getCreatures().isEmpty())
{
nodes.add(node);
}
if (node.getChild1() != null)
{
nodes.addAll(getNodes(node.getChild1()));
nodes.addAll(getNodes(node.getChild2()));
}
return nodes;
}
class ReverseIntegerComparator implements Comparator<Integer>
{
// Sort in reverse, so we don't disturb array
// indexes when removing.
public int compare(Integer in1, Integer in2)
{
return in2.compareTo(in1);
}
}
/** Print all childless nodes in tree. */
void printLeaves()
{
LOGGER.log(Level.FINEST, "");
List<PredictSplitNode> leaves = getLeaves(root);
Collections.sort(leaves);
for (PredictSplitNode leaf : leaves)
{
LOGGER.log(Level.FINEST, leaf.toString());
}
LOGGER.log(Level.FINEST, "");
}
/** Print all nodes in tree. */
void printNodes()
{
LOGGER.log(Level.FINEST, "");
List<PredictSplitNode> nodes = getNodes(root);
Collections.sort(nodes, nodeTurnComparator);
for (PredictSplitNode node : nodes)
{
LOGGER.log(Level.FINEST, node.toString());
}
LOGGER.log(Level.FINEST, "");
}
/** Return the leaf PredictSplitNode with matching markerId. */
PredictSplitNode getLeaf(String markerId)
{
List<PredictSplitNode> leaves = getLeaves(root);
for (PredictSplitNode leaf : leaves)
{
if (markerId.equals(leaf.getMarkerId()))
{
return leaf;
}
}
return null;
}
public PredictSplitNode getRoot()
{
return root;
}
}
class NodeTurnComparator implements Comparator<PredictSplitNode>
{
public int compare(PredictSplitNode n1, PredictSplitNode n2)
{
int diff = n1.getTurnCreated() - n2.getTurnCreated();
if (diff != 0)
{
return diff;
}
diff = n1.getParent().toString().compareTo(n2.getParent().toString());
if (diff != 0)
{
return diff;
}
diff = n2.getCreatures().size() - n1.getCreatures().size();
if (diff != 0)
{
return diff;
}
return (n1.toString().compareTo(n2.toString()));
}
}