CMSI 185
Homework #4
Partial Answers
  1. The statistics class, with the mode method:
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * A version of David Eck's Statistics Caclulator object, with
     * the addition of a method to compute the mode.
     */
    public class StatCalc {
    
        // Number of values entered so far.
        private int count;
    
        // The sum of the values entered so far.
        private double sum;
    
        // The sum of the squares of all values entered so far.
        private double squareSum;
    
        // The largest value seen so far (or -inifinity if none seen).
        private double max = Double.NEGATIVE_INFINITY;
    
        // The smallest value seen so far (or inifinity if none seen).
        private double min = Double.POSITIVE_INFINITY;
    
        // Counts of all the items seen so far.
        private Map<Double, Integer> entries = new HashMap<Double, Integer>();
    
        /**
         * Adds a number to the dataset. The statistics will be computed for all the
         * numbers that haved been added to the dataset using this method.
         */
        public void enter(double num) {
            count++;
            if (!entries.containsKey(num)) {
                entries.put(num, 1);
            } else {
                entries.put(num, entries.get(num) + 1);
            }
            sum += num;
            squareSum += num * num;
            if (num > max) max = num;
            if (num < min) min = num;
        }
    
        /**
         * Returns the number of items entered into the dataset.
         */
        public int getCount() {
            return count;
        }
    
        /**
         * Returns the sum of all the numbers that have been entered.
         */
        public double getSum() {
            return sum;
        }
    
        /**
         * Returns the average of all the items that have been entered. The
         * return value is Double.NaN if no numbers have been entered.
         */
        public double getMean() {
            return sum / count;
        }
    
        /**
         * Returns the standard deviation of all the items that have been entered.
         * The return value is Double.NaN if no numbers have been entered.
         */
        public double getStandardDeviation() {
            double mean = getMean();
            return Math.sqrt(squareSum / count - mean * mean);
        }
    
        /**
         * Returns the smallest item that has been entered. The return value will be
         * infinity if no numbers have been entered.
         */
        public double getMin() {
            return min;
        }
    
        /**
         * Returns the largest item that has been entered. The return value will be
         * -infinity if no numbers have been entered.
         */
        public double getMax() {
            return max;
        }
    
        /**
         * Returns a mode of the dataset.  If more than one value qualifies as
         * a mode, an arbitrary one is returned.
         */
        public double getMode() {
            double mode = Double.NaN;
            int highestCount = 0;
            for (Double key: entries.keySet()) {
                int thisCount = entries.get(key);
                if (thisCount > highestCount) {
                    mode = key;
                    highestCount = thisCount;
                }
            }
            return mode;
        }
    }
    
  2. The FiveDice class:
    /**
     * A collection of five dice.
     */
    public class FiveDice {
    
        // Note: This class should be implemented with arrays, but since this
        // class is a solution to a homework problem assigned before arrays
        // were introduced, we're doing things the hard way.
    
        private int die1;
        private int die2;
        private int die3;
        private int die4;
        private int die5;
    
        /**
         * Rolls each of the five dice.
         */
        public void roll() {
            die1 = (int)(Math.random() * 6 + 1);
            die2 = (int)(Math.random() * 6 + 1);
            die3 = (int)(Math.random() * 6 + 1);
            die4 = (int)(Math.random() * 6 + 1);
            die5 = (int)(Math.random() * 6 + 1);
        }
    
        /**
         * Returns whether all five dice are showing the same value.
         */
        public boolean isYahtzee() {
            return die1==die2 && die2==die3 && die3==die4 && die4==die5;
        }
    
        /**
         * Returns whether at least four dice are showing the same value.
         */
        public boolean isFourOfAKind() {
            return die1==die2 && die2==die3 && die3==die4 ||
                   die1==die2 && die2==die3 && die3==die5 ||
                   die1==die2 && die2==die4 && die4==die5 ||
                   die1==die3 && die3==die4 && die4==die5 ||
                   die2==die3 && die3==die4 && die4==die5;
        }
    
        /**
         * Returns whether at least three dice are showing the same value.
         */
        public boolean isThreeOfAKind() {
            return die1==die2 && die2==die3 ||
                   die1==die2 && die2==die4 ||
                   die1==die2 && die2==die5 ||
                   die1==die3 && die3==die4 ||
                   die1==die3 && die3==die5 ||
                   die1==die4 && die4==die5 ||
                   die2==die3 && die3==die4 ||
                   die2==die3 && die3==die5 ||
                   die2==die4 && die4==die5 ||
                   die3==die4 && die4==die5;
        }
    
        /**
         * Returns whether the dice are showing a full house.
         */
        public boolean isFullHouse() {
            return die1==die2 && die2==die3 && die4==die5 ||
                   die1==die2 && die2==die4 && die3==die5 ||
                   die1==die2 && die2==die5 && die3==die4 ||
                   die1==die3 && die3==die4 && die2==die5 ||
                   die1==die3 && die3==die5 && die2==die4 ||
                   die1==die4 && die4==die5 && die2==die3 ||
                   die2==die3 && die3==die4 && die1==die5 ||
                   die2==die3 && die3==die5 && die1==die4 ||
                   die2==die4 && die4==die5 && die1==die3 ||
                   die3==die4 && die4==die5 && die1==die2;
        }
    }
    

    The Yahtzee roll simulator:

    /**
     * A little application that simulates rolling five dice many times, counting
     * the number of times, on average, the number of rolls required to make some
     * Yahtzee hands.
     */
    public class YahtzeeStats {
    
        // Run 10,000 experiments for each hand.
        private static final int NUMBER_OF_EXPERIMENTS = 10000;
    
        // The various kinds of hands to roll for.
        private enum Type {THREE_OF_A_KIND, FULL_HOUSE, FOUR_OF_A_KIND, YAHTZEE};
    
        // A single dice object is sufficient.
        private static FiveDice dice = new FiveDice();
    
        public static void main(String[] args) {
    
            System.out.println("Type            Avg #Rolls     StdDev Max #Rolls");
            System.out.println("--------------- ---------- ---------- ----------");
            for (Type type: Type.values()) {
                StatCalc stats = new StatCalc();
                for (int i = 0; i < NUMBER_OF_EXPERIMENTS; i++) {
                    stats.enter(rollFor(type));
                }
                displayTableRow(type.name(), stats.getMean(),
                        stats.getStandardDeviation(), stats.getMax());
            }
        }
    
        // A helper to count the number of rolls until a particular type
        // of hand comes up.
        private static int rollFor(Type type) {
            int rolls = 0;
            while (true) {
                dice.roll();
                rolls++;
                if (type == Type.THREE_OF_A_KIND && dice.isThreeOfAKind()) break;
                if (type == Type.FULL_HOUSE && dice.isFullHouse()) break;
                if (type == Type.FOUR_OF_A_KIND && dice.isFourOfAKind()) break;
                if (type == Type.YAHTZEE && dice.isYahtzee()) break;
            }
            return rolls;
        }
    
        // Helper to display one row of the table.
        private static void displayTableRow(String name, double average,
                double standardDeviation, double max) {
            System.out.printf("%15s%11.2f%11.2f%11.2f\n", name, average,
                    standardDeviation, max);
        }
    
    }
    
  3. The suit class:
    /**
     * An ordinary playing card suit.
     */
    public enum Suit {
        SPADES, HEARTS, DIAMONDS, CLUBS;
    
        @Override
        public String toString() {
           return "\u2660\u2665\u2666\u2663".substring(ordinal(), ordinal() + 1);
        }
    }
    

    The rank class:

    /**
     * A card value for an ordinary playing card.
     */
    public enum CardValue {
        ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT,
        NINE, TEN, JACK, QUEEN, KING;
    
        @Override
        public String toString() {
            return " A 2 3 4 5 6 7 8 910 J Q K".substring(ordinal() * 2,
                    ordinal() * 2 + 2).trim();
        }
    }
    

    The card class. The one from the book wasn't very good, so I can to clean it up. For instance, the book's author explicitly threw a NullPointerException. That's just nasty. I changed it to an IllegalArgumentException, which is proper.

    /**
     * An immutable playing card class.
     */
    public class Card {
    
        private final Suit suit;
        private final CardValue value;
    
        /**
         * Creates a card with a specified suit and value.
         *
         * @param value the non-null value of the new card.
         * @param suit the non-null suit of the new card.
         * @throws IllegalArgumentException either parameter is null.
         */
        public Card(CardValue value, Suit suit) {
            if (value == null || suit == null) {
                throw new IllegalArgumentException("Suit and value cannot be null");
            }
            this.value = value;
            this.suit = suit;
        }
    
        /**
         * Returns the suit of this card.
         */
        public Suit getSuit() {
            return suit;
        }
    
        /**
         * Returns the value of this card.
         */
        public CardValue getValue() {
            return value;
        }
    
        /**
         * Returns a string representation of this card.
         */
        public String toString() {
            return value + "" + suit;
        }
    }