/** * This models a Myopic Col position on a Binary Tree. Myopic Col is just like Col, except that it's played on a directed graph and only outgoing edges "count" towards whether a vertex can't be painted. * * @author Kyle Burke <paithanq@gmail.com> * */ import java.lang.*; import java.io.*; import java.util.*; import java.awt.*; import javax.swing.*; public class TreeMyopicCol extends CombinatorialGame implements SwingDisplayable { //constants /** * Integer constant representing the "uncoloredness" of a vertex. */ public static final int UNCOLORED = 2; //instance variables //Priority Queue of heap sizes private PureBinaryTree<Color> tree; //Map of integers to appropriate colors //initialize this outside of the constructor because it's static. private final Map<Integer, Color> intsToColors = new TreeMap<Integer, Color>(); //Map of Colors to Integers //initialize this outside of the constructor because it's static private final Map<Color, Integer> colorsToInts = new HashMap<Color, Integer>(); //Constructors //private constructor that sets the maps private TreeMyopicCol() { //set up the ints->Colors map this.intsToColors.put(CombinatorialGame.LEFT, Color.BLUE); this.intsToColors.put(CombinatorialGame.RIGHT, Color.RED); this.intsToColors.put(UNCOLORED, Color.WHITE); //set up the Colors -> ints Map this.colorsToInts.put(Color.BLUE, CombinatorialGame.LEFT); this.colorsToInts.put(Color.RED, CombinatorialGame.RIGHT); this.colorsToInts.put(Color.WHITE, UNCOLORED); } /** * Class constructor for a game that starts uncolored. * * @param levels The number of levels * @throws IllegalArgumentException if one of the heap sizes is negative. */ public TreeMyopicCol(int levels) throws IllegalArgumentException { this(); if (levels < 1) { throw new IllegalArgumentException("Tried to use a non-positive number for the number of levels."); } else { this.tree = this.createEmptyTree(levels); } } /** * Class constructor. * * @param tree A PureBinaryTree<Integer> to build our tree from. */ public TreeMyopicCol(PureBinaryTree<Integer> tree) { this(); this.tree = getColorTree(tree); } /** * Returns a string version of this. * * @return String version of this. */ public String toString() { return this.tree.toString(); } /** * Returns the underlying tree. * * @return A copy of the PureBinaryTree that holds the appropriate Integers. */ public PureBinaryTree<Integer> getTree() { return this.getIntTree(this.tree); } //@override public CombinatorialGame clone() { return new TreeMyopicCol(this.getIntTree(this.tree)); } //@override public boolean equals(CombinatorialGame object) { if (object == null) { return false; } else if (this.getClass() == object.getClass()) { TreeMyopicCol other = (TreeMyopicCol) object; return this.tree.equals(other.tree); } else { return false; } } //@override public Collection<CombinatorialGame> getOptions(int playerId) { PureBinaryTree<Integer> tree = this.getTree(); return this.getOptions(playerId, tree, tree); } //private methods //gets a Color from the appropriate int public Color getColorFromInt(int colorAsInt) { return this.intsToColors.get(colorAsInt); } //gets an int from the appropriate Color public int getColorInteger(Color color) { return this.colorsToInts.get(color); } //Creates an empty tree with a given number of levels private PureBinaryTree<Color> createEmptyTree(int levels) { if (levels <= 0) { return null; } else { PureBinaryTree<Color> tree = new PureBinaryTree<Color>(this.getColorFromInt(UNCOLORED)); tree.setLeftChild(this.createEmptyTree(levels - 1)); tree.setRightChild(this.createEmptyTree(levels - 1)); } return tree; } //copies a tree of ints to a tree using the appropriate Colors private PureBinaryTree<Color> getColorTree(PureBinaryTree<Integer> tree) { if (tree == null) { return null; } else { PureBinaryTree<Color> copy = new PureBinaryTree<Color>(this.getColorFromInt(tree.getValue())); copy.setLeftChild(this.getColorTree(tree.getLeftChild())); copy.setRightChild(this.getColorTree(tree.getRightChild())); return copy; } } //copies a tree of Colors to a tree using the appropriate Integers private PureBinaryTree<Integer> getIntTree(PureBinaryTree<Color> tree) { if (tree == null) { return null; } else { PureBinaryTree<Integer> copy = new PureBinaryTree<Integer>(this.getColorInteger(tree.getValue())); copy.setLeftChild(this.getIntTree(tree.getLeftChild())); copy.setRightChild(this.getIntTree(tree.getRightChild())); return copy; } } //copies a tree of integers private PureBinaryTree<Integer> copyIntTree(PureBinaryTree<Integer> tree) { return this.getIntTree(this.getColorTree(tree)); } //recursive helper method for getOptions private Collection<CombinatorialGame> getOptions(int playerId, PureBinaryTree<Integer> root, PureBinaryTree<Integer> currentNode) { Collection<CombinatorialGame> options = new ArrayList<CombinatorialGame>(); if (currentNode != null) { //see if we can make a move at the current node if (currentNode.getValue().equals(UNCOLORED)) { if ((currentNode.getRightChild() == null || !currentNode.getRightChild().getValue().equals(playerId)) && (currentNode.getLeftChild() == null || !currentNode.getLeftChild().getValue().equals(playerId))) { //set the current node's color to the playerId currentNode.setValue(playerId); //copy the ENTIRE tree and add it PureBinaryTree<Integer> copy = copyIntTree(root); TreeMyopicCol option = new TreeMyopicCol(copy); options.add(option); //reset the current node to have no color currentNode.setValue(UNCOLORED); } } //add the options from the left subtree options.addAll(this.getOptions(playerId, root, currentNode.getLeftChild())); //ad all the options from the right subtree options.addAll(this.getOptions(playerId, root, currentNode.getRightChild())); } return options; } /** * Gets a Swing representation of this. * * @return A JComponent containing a graphical display of the current position. */ public JComponent toSwingComponent() { return new TreeMyopicColPanel(this); } /** * Represents a factory that creates instances of TreeMyopicCol. */ public static class PositionBuilder implements PositionFactory<TreeMyopicCol> { //minimum depth of the tree private int minDepth; //maximum depth of the tree private int maxDepth; //probability a vertex will have each of the two children private double childProbability; //probability a vertex will be colored private double colorDensity; /** * Class constructor. * * @param minDepth Minimum depth of a full tree. * @param childProbability For each potential child of a node, probability that child will exist (past the minimum depth) * @param colorDensity The probability a vertex will be colored. (Color chosen uniformly at random.) */ public PositionBuilder(int minDepth, int maxDepth, double childProbability, double colorDensity) { this.minDepth = minDepth; this.maxDepth = maxDepth; this.childProbability = childProbability; this.colorDensity = colorDensity; } //@override public TreeMyopicCol getPosition() { PureBinaryTree<Integer> tree = this.buildTree(0); return new TreeMyopicCol(tree); } //Helper function for getPosition. Builds a tree, starting at the given depth private PureBinaryTree<Integer> buildTree(int depth) { Random randomGenerator = new Random(); //choose the color of this node int color; double nextDouble = randomGenerator.nextDouble(); if (nextDouble < (this.colorDensity/2)) { color = CombinatorialGame.LEFT; } else if (nextDouble < this.colorDensity) { color = CombinatorialGame.RIGHT; } else { color = UNCOLORED; } PureBinaryTree<Integer> tree = new PureBinaryTree<Integer>(color); if (depth < this.maxDepth) { //put together the left child, if it should exist if (depth < this.minDepth || randomGenerator.nextDouble() < this.childProbability) { tree.setLeftChild(this.buildTree(depth + 1)); } //put together the right child, if it should exist if (depth < this.minDepth || randomGenerator.nextDouble() < this.childProbability) { tree.setRightChild(this.buildTree(depth + 1)); } } return tree; } } //end of TreeMyopicColFactory //main method with the unit test public static void main(String[] args) { //test TreeMyopicColFactory PositionBuilder gameGenerator = new PositionBuilder(3, 6, .3, .2); System.out.println("A randomly-generated game: \n" + gameGenerator.getPosition()); System.out.println("Another randomly-generated game: \n" + gameGenerator.getPosition()); TreeMyopicCol position = gameGenerator.getPosition(); System.out.println("Yet another randomly-generated game: \n" + position); JFrame frame = new JFrame("TreeMyopicCol"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setPreferredSize(new Dimension(1000, 1000)); frame.setContentPane(position.toSwingComponent()); frame.pack(); frame.setVisible(true); } } //end of TreeMyopicCol.java