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;
}
}
/**
* 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);
}
}
/**
* 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;
}
}