diff --git a/common/StdStats.java b/common/StdStats.java new file mode 100644 index 0000000..81fd6c9 --- /dev/null +++ b/common/StdStats.java @@ -0,0 +1,534 @@ +package common; +/****************************************************************************** + * Compilation: javac StdStats.java + * Execution: java StdStats < input.txt + * Dependencies: StdOut.java + * + * Library of statistical functions. + * + * The test client reads an array of real numbers from standard + * input, and computes the minimum, mean, maximum, and + * standard deviation. + * + * The functions all throw a java.lang.IllegalArgumentException + * if the array passed in as an argument is null. + * + * The floating-point functions all return NaN if any input is NaN. + * + * Unlike Math.min() and Math.max(), the min() and max() functions + * do not differentiate between -0.0 and 0.0. + * + * % more tiny.txt + * 5 + * 3.0 1.0 2.0 5.0 4.0 + * + * % java StdStats < tiny.txt + * min 1.000 + * mean 3.000 + * max 5.000 + * std dev 1.581 + * + * Should these functions use varargs instead of array arguments? + * + ******************************************************************************/ + +/** + * The {@code StdStats} class provides static methods for computing + * statistics such as min, max, mean, sample standard deviation, and + * sample variance. + *
+ * For additional documentation, see
+ * Section 2.2 of
+ * Computer Science: An Interdisciplinary Approach
+ * by Robert Sedgewick and Kevin Wayne.
+ *
+ * @author Robert Sedgewick
+ * @author Kevin Wayne
+ */
+public final class StdStats {
+
+ private StdStats() { }
+
+ /**
+ * Returns the maximum value in the specified array.
+ *
+ * @param a the array
+ * @return the maximum value in the array {@code a[]};
+ * {@code Double.NEGATIVE_INFINITY} if no such value
+ */
+ public static double max(double[] a) {
+ validateNotNull(a);
+
+ double max = Double.NEGATIVE_INFINITY;
+ for (int i = 0; i < a.length; i++) {
+ if (Double.isNaN(a[i])) return Double.NaN;
+ if (a[i] > max) max = a[i];
+ }
+ return max;
+ }
+
+ /**
+ * Returns the maximum value in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the maximum value in the subarray {@code a[lo..hi)};
+ * {@code Double.NEGATIVE_INFINITY} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ public static double max(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ double max = Double.NEGATIVE_INFINITY;
+ for (int i = lo; i < hi; i++) {
+ if (Double.isNaN(a[i])) return Double.NaN;
+ if (a[i] > max) max = a[i];
+ }
+ return max;
+ }
+
+ /**
+ * Returns the maximum value in the specified array.
+ *
+ * @param a the array
+ * @return the maximum value in the array {@code a[]};
+ * {@code Integer.MIN_VALUE} if no such value
+ */
+ public static int max(int[] a) {
+ validateNotNull(a);
+
+ int max = Integer.MIN_VALUE;
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] > max) max = a[i];
+ }
+ return max;
+ }
+
+ /**
+ * Returns the minimum value in the specified array.
+ *
+ * @param a the array
+ * @return the minimum value in the array {@code a[]};
+ * {@code Double.POSITIVE_INFINITY} if no such value
+ */
+ public static double min(double[] a) {
+ validateNotNull(a);
+
+ double min = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < a.length; i++) {
+ if (Double.isNaN(a[i])) return Double.NaN;
+ if (a[i] < min) min = a[i];
+ }
+ return min;
+ }
+
+ /**
+ * Returns the minimum value in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the maximum value in the subarray {@code a[lo..hi)};
+ * {@code Double.POSITIVE_INFINITY} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ public static double min(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ double min = Double.POSITIVE_INFINITY;
+ for (int i = lo; i < hi; i++) {
+ if (Double.isNaN(a[i])) return Double.NaN;
+ if (a[i] < min) min = a[i];
+ }
+ return min;
+ }
+
+ /**
+ * Returns the minimum value in the specified array.
+ *
+ * @param a the array
+ * @return the minimum value in the array {@code a[]};
+ * {@code Integer.MAX_VALUE} if no such value
+ */
+ public static int min(int[] a) {
+ validateNotNull(a);
+
+ int min = Integer.MAX_VALUE;
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] < min) min = a[i];
+ }
+ return min;
+ }
+
+ /**
+ * Returns the average value in the specified array.
+ *
+ * @param a the array
+ * @return the average value in the array {@code a[]};
+ * {@code Double.NaN} if no such value
+ */
+ public static double mean(double[] a) {
+ validateNotNull(a);
+
+ if (a.length == 0) return Double.NaN;
+ double sum = sum(a);
+ return sum / a.length;
+ }
+
+ /**
+ * Returns the average value in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the average value in the subarray {@code a[lo..hi)};
+ * {@code Double.NaN} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ public static double mean(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ int length = hi - lo;
+ if (length == 0) return Double.NaN;
+
+ double sum = sum(a, lo, hi);
+ return sum / length;
+ }
+
+ /**
+ * Returns the average value in the specified array.
+ *
+ * @param a the array
+ * @return the average value in the array {@code a[]};
+ * {@code Double.NaN} if no such value
+ */
+ public static double mean(int[] a) {
+ validateNotNull(a);
+
+ if (a.length == 0) return Double.NaN;
+ int sum = sum(a);
+ return 1.0 * sum / a.length;
+ }
+
+ /**
+ * Returns the sample variance in the specified array.
+ *
+ * @param a the array
+ * @return the sample variance in the array {@code a[]};
+ * {@code Double.NaN} if no such value
+ */
+ public static double var(double[] a) {
+ validateNotNull(a);
+
+ if (a.length == 0) return Double.NaN;
+ double avg = mean(a);
+ double sum = 0.0;
+ for (int i = 0; i < a.length; i++) {
+ sum += (a[i] - avg) * (a[i] - avg);
+ }
+ return sum / (a.length - 1);
+ }
+
+ /**
+ * Returns the sample variance in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the sample variance in the subarray {@code a[lo..hi)};
+ * {@code Double.NaN} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ public static double var(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ int length = hi - lo;
+ if (length == 0) return Double.NaN;
+
+ double avg = mean(a, lo, hi);
+ double sum = 0.0;
+ for (int i = lo; i < hi; i++) {
+ sum += (a[i] - avg) * (a[i] - avg);
+ }
+ return sum / (length - 1);
+ }
+
+ /**
+ * Returns the sample variance in the specified array.
+ *
+ * @param a the array
+ * @return the sample variance in the array {@code a[]};
+ * {@code Double.NaN} if no such value
+ */
+ public static double var(int[] a) {
+ validateNotNull(a);
+ if (a.length == 0) return Double.NaN;
+ double avg = mean(a);
+ double sum = 0.0;
+ for (int i = 0; i < a.length; i++) {
+ sum += (a[i] - avg) * (a[i] - avg);
+ }
+ return sum / (a.length - 1);
+ }
+
+ /**
+ * Returns the population variance in the specified array.
+ *
+ * @param a the array
+ * @return the population variance in the array {@code a[]};
+ * {@code Double.NaN} if no such value
+ */
+ public static double varp(double[] a) {
+ validateNotNull(a);
+ if (a.length == 0) return Double.NaN;
+ double avg = mean(a);
+ double sum = 0.0;
+ for (int i = 0; i < a.length; i++) {
+ sum += (a[i] - avg) * (a[i] - avg);
+ }
+ return sum / a.length;
+ }
+
+ /**
+ * Returns the population variance in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the population variance in the subarray {@code a[lo..hi)};
+ * {@code Double.NaN} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ public static double varp(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ int length = hi - lo;
+ if (length == 0) return Double.NaN;
+
+ double avg = mean(a, lo, hi);
+ double sum = 0.0;
+ for (int i = lo; i < hi; i++) {
+ sum += (a[i] - avg) * (a[i] - avg);
+ }
+ return sum / length;
+ }
+
+ /**
+ * Returns the sample standard deviation in the specified array.
+ *
+ * @param a the array
+ * @return the sample standard deviation in the array {@code a[]};
+ * {@code Double.NaN} if no such value
+ */
+ public static double stddev(double[] a) {
+ validateNotNull(a);
+ return Math.sqrt(var(a));
+ }
+
+ /**
+ * Returns the sample standard deviation in the specified array.
+ *
+ * @param a the array
+ * @return the sample standard deviation in the array {@code a[]};
+ * {@code Double.NaN} if no such value
+ */
+ public static double stddev(int[] a) {
+ validateNotNull(a);
+ return Math.sqrt(var(a));
+ }
+
+ /**
+ * Returns the sample standard deviation in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the sample standard deviation in the subarray {@code a[lo..hi)};
+ * {@code Double.NaN} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ public static double stddev(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ return Math.sqrt(var(a, lo, hi));
+ }
+
+
+ /**
+ * Returns the population standard deviation in the specified array.
+ *
+ * @param a the array
+ * @return the population standard deviation in the array;
+ * {@code Double.NaN} if no such value
+ */
+ public static double stddevp(double[] a) {
+ validateNotNull(a);
+ return Math.sqrt(varp(a));
+ }
+
+ /**
+ * Returns the population standard deviation in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the population standard deviation in the subarray {@code a[lo..hi)};
+ * {@code Double.NaN} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ public static double stddevp(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ return Math.sqrt(varp(a, lo, hi));
+ }
+
+ /**
+ * Returns the sum of all values in the specified array.
+ *
+ * @param a the array
+ * @return the sum of all values in the array {@code a[]};
+ * {@code 0.0} if no such value
+ */
+ private static double sum(double[] a) {
+ validateNotNull(a);
+ double sum = 0.0;
+ for (int i = 0; i < a.length; i++) {
+ sum += a[i];
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the sum of all values in the specified subarray.
+ *
+ * @param a the array
+ * @param lo the left endpoint of the subarray (inclusive)
+ * @param hi the right endpoint of the subarray (exclusive)
+ * @return the sum of all values in the subarray {@code a[lo..hi)};
+ * {@code 0.0} if no such value
+ * @throws IllegalArgumentException if {@code a} is {@code null}
+ * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)}
+ */
+ private static double sum(double[] a, int lo, int hi) {
+ validateNotNull(a);
+ validateSubarrayIndices(lo, hi, a.length);
+
+ double sum = 0.0;
+ for (int i = lo; i < hi; i++) {
+ sum += a[i];
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the sum of all values in the specified array.
+ *
+ * @param a the array
+ * @return the sum of all values in the array {@code a[]};
+ * {@code 0.0} if no such value
+ */
+ private static int sum(int[] a) {
+ validateNotNull(a);
+ int sum = 0;
+ for (int i = 0; i < a.length; i++) {
+ sum += a[i];
+ }
+ return sum;
+ }
+
+ /**
+ * Plots the points (0, a0), (1, a1), ...,
+ * (n-1, an-1) to standard draw.
+ *
+ * @param a the array of values
+ */
+ public static void plotPoints(double[] a) {
+ validateNotNull(a);
+ int n = a.length;
+ StdDraw.setXscale(-1, n);
+ StdDraw.setPenRadius(1.0 / (3.0 * n));
+ for (int i = 0; i < n; i++) {
+ StdDraw.point(i, a[i]);
+ }
+ }
+
+ /**
+ * Plots the line segments connecting
+ * (i, ai) to
+ * (i+1, ai+1) for
+ * each i to standard draw.
+ *
+ * @param a the array of values
+ */
+ public static void plotLines(double[] a) {
+ validateNotNull(a);
+ int n = a.length;
+ StdDraw.setXscale(-1, n);
+ StdDraw.setPenRadius();
+ for (int i = 1; i < n; i++) {
+ StdDraw.line(i-1, a[i-1], i, a[i]);
+ }
+ }
+
+ /**
+ * Plots bars from (0, ai) to
+ * (ai) for each i
+ * to standard draw.
+ *
+ * @param a the array of values
+ */
+ public static void plotBars(double[] a) {
+ validateNotNull(a);
+ int n = a.length;
+ StdDraw.setXscale(-1, n);
+ for (int i = 0; i < n; i++) {
+ StdDraw.filledRectangle(i, a[i]/2, 0.25, a[i]/2);
+ }
+ }
+
+ // throw an IllegalArgumentException if x is null
+ // (x is either of type double[] or int[])
+ private static void validateNotNull(Object x) {
+ if (x == null)
+ throw new IllegalArgumentException("argument is null");
+ }
+
+ // throw an exception unless 0 <= lo <= hi <= length
+ private static void validateSubarrayIndices(int lo, int hi, int length) {
+ if (lo < 0 || hi > length || lo > hi)
+ throw new IllegalArgumentException("subarray indices out of bounds: [" + lo + ", " + hi + ")");
+ }
+
+
+ /**
+ * Unit tests {@code StdStats}.
+ * Convert command-line arguments to array of doubles and call various methods.
+ *
+ * @param args the command-line arguments
+ */
+ public static void main(String[] args) {
+ double[] a = StdArrayIO.readDouble1D();
+ StdOut.printf(" min %10.3f\n", min(a));
+ StdOut.printf(" mean %10.3f\n", mean(a));
+ StdOut.printf(" max %10.3f\n", max(a));
+ StdOut.printf(" stddev %10.3f\n", stddev(a));
+ StdOut.printf(" var %10.3f\n", var(a));
+ StdOut.printf(" stddevp %10.3f\n", stddevp(a));
+ StdOut.printf(" varp %10.3f\n", varp(a));
+ }
+}
diff --git a/main.typ b/main.typ
index 5a107ac..48cc6cf 100644
--- a/main.typ
+++ b/main.typ
@@ -15,7 +15,7 @@ Collection of solutions to programming exercises as part of Introduction to Prog
)
#{
- let count = 5;
+ let count = 6;
for week in range(1, count + 1) {
let a = "./week" + str(week) + "/doc.typ"
include a
diff --git a/week6/Calendar.java b/week6/Calendar.java
new file mode 100644
index 0000000..7e71328
--- /dev/null
+++ b/week6/Calendar.java
@@ -0,0 +1,88 @@
+package week6;
+
+public class Calendar {
+ static final String[] months = {
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December"
+ };
+
+ public static void main(String[] args) {
+ int month = Integer.parseInt(args[0]);
+ int year = Integer.parseInt(args[1]);
+
+ System.out.printf("%s %d\n", months[month - 1], year);
+ System.out.println(" S M Tu W Th F S");
+ int daysToSkip = dayOfWeek(year, month, 1);
+ int monthLength = monthLength(year, month);
+ for(var i = 1; i < monthLength + daysToSkip + 1; i++) {
+ if(i < daysToSkip && i % 7 == 0) {
+ System.out.println();
+ continue;
+ }
+ if(i <= daysToSkip) {
+ System.out.print(" ");
+ continue;
+ }
+ System.out.printf("%2d ", i - daysToSkip);
+ if(i % 7 == 0) {
+ System.out.println();
+ }
+ }
+ }
+
+ static final int[] monthLengths = {
+ 31,
+ 28,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ };
+
+ /**
+ * Length of a given month
+ * @param year
+ * @param month 1=January
+ * @return length of the month in days, leap year adjusted
+ */
+ static int monthLength(int year, int month) {
+ return monthLengths[month - 1] + (month == 2 ? (isLeapYear(year) ? 1 : 0) : 0);
+ }
+
+ static boolean isLeapYear(int year) {
+ boolean isLeapYear;
+ isLeapYear = (year % 4 == 0);
+ isLeapYear = isLeapYear && (year % 100 != 0);
+ isLeapYear = isLeapYear || (year % 400 == 0);
+ return isLeapYear;
+ }
+
+ /**
+ * @param year
+ * @param month 1=January
+ * @param day
+ * @return day of week, 0=Sunday,6=Saturday
+ */
+ static int dayOfWeek(int year, int month, int day) {
+ int y0 = year - (14 - month) / 12;
+ int leapAdjustedYear = y0 + y0 / 4 - y0 / 100 + y0 / 400;
+ int m0 = month + 12 * ((14 - month) / 12) - 2;
+ return (day + leapAdjustedYear + (31 * m0) / 12) % 7;
+ }
+}
diff --git a/week6/Card.java b/week6/Card.java
new file mode 100644
index 0000000..e94a655
--- /dev/null
+++ b/week6/Card.java
@@ -0,0 +1,141 @@
+package week6;
+
+public class Card {
+
+ public enum Suit {
+ Clubs,
+ Diamonds,
+ Hearts,
+ Spades;
+
+ public static final String CLUBS = "♣";
+ public static final String DIAMONDS = "♦";
+ public static final String HEARTS = "♥";
+ public static final String SPADES = "♠";
+
+ public String sprint() {
+ switch(this) {
+ case Clubs: return CLUBS;
+ case Diamonds: return DIAMONDS;
+ case Hearts: return HEARTS;
+ case Spades: return SPADES;
+ }
+ throw new NullPointerException();
+ }
+ }
+
+ public Suit suit;
+ public int value;
+
+ public Card(Suit suit, int value) {
+ this.suit = suit;
+ this.value = value;
+ }
+
+ // Print a single character for the value
+ // Only a single character is returned so that we can format
+ // the card output correctly and easily
+ public char sprintValue() {
+ assert this.value > 0 && this.value < 14;
+ if(this.value == 1) {
+ return 'A';
+ } else if(this.value == 11) {
+ return 'J';
+ } else if(this.value == 12) {
+ return 'Q';
+ } else if(this.value == 13) {
+ return 'K';
+ } else if(this.value == 10) {
+ // UTF-8 character that looks like a 10
+ // but uses only one character width
+ return '⒑';
+ }
+ // asserted 0 < x < 14, we handled 1, 10-13
+ // the only valid values here are 2-9
+ return Integer.toString(this.value).charAt(0);
+ }
+
+ // Print the card to a string to be later processed (or printed).
+ // Approximates 'normal' card deck look
+ // Numbered cards have their suit symbol repeated based on their value.
+ // Face cards are empty.
+ public String sprintCard() {
+ var output = "";
+ var value = this.sprintValue();
+ var suit = this.suit.sprint();
+
+ // Generate the top (and bottom) of a card
+ // this will show the value of the card on each edge
+ var top = "";
+ top += value;
+ if(this.value > 10) {
+ // face cards are empty
+ top += " ";
+ } else {
+ // and for numbered cards, show the suit characters
+ top += this.value >= 4 ? suit : " ";
+ top += this.value < 4 && this.value > 1 ? suit : " ";
+ top += this.value >= 4 ? suit : " ";
+ }
+ top += value;
+ top += "\n";
+
+ output += top;
+
+ if(this.value > 10) {
+ // face cards get suits on the side, right above and under their values
+ // numbered cards are empty on their sides
+ output += suit + " " + suit + "\n";
+ output += suit + " " + suit + "\n";
+ } else {
+ // normal cards have either 3 or 4 rows of suits in 1-3 columns,
+ // with the middle one sometimes floating.
+ // we have to have a set size and can't have floating characters,
+ // so this is a best effort approximation
+ // instead of using 3 rows we have a gap in the 3rd row
+ output += " ";
+ output += this.value >= 6 ? suit : " ";
+ // odd or 10 have a symbol in the middle
+ output += this.value % 2 == 1 || this.value == 10 ? suit : " ";
+ output += this.value >= 6 ? suit : " ";
+ output += " ";
+ output += "\n";
+
+
+ output += " ";
+ output += this.value >= 8 ? suit : " ";
+ output += this.value == 10 ? suit : " ";
+ output += this.value >= 8 ? suit : " ";
+ output += " ";
+ output += "\n";
+ }
+
+ output += top;
+
+ return output;
+ }
+
+ // Shows cards next to each other (left to right)
+ // for visuals, assumes that sprintCard returns the same width for each card (and each row of a card)
+ // for correctness, assumes that sprintCard always returns 4 rows (is asserted)
+ public static String sprintCards(Card[] cards) {
+ String[] output = { "", "", "", "" };
+
+ // split each card into it's 4 rows
+ // save the row into relevant output
+ for(var i = 0; i < cards.length; i++) {
+ var cardstr = cards[i].sprintCard().split("\n");
+ assert output.length == cardstr.length;
+ for(var x = 0; x < cardstr.length; x++) {
+ output[x] += cardstr[x] + " ";
+ }
+ }
+
+ // and join the rows together with a newline
+ var outputstr = "";
+ for(var i = 0; i < output.length; i++) {
+ outputstr += output[i] + "\n";
+ }
+ return outputstr;
+ }
+}
diff --git a/week6/Hand.java b/week6/Hand.java
new file mode 100644
index 0000000..d442649
--- /dev/null
+++ b/week6/Hand.java
@@ -0,0 +1,95 @@
+package week6;
+
+import java.util.Arrays;
+
+public class Hand {
+ enum Type {
+ StraightFlush,
+ FourOfAKind,
+ FullHouse,
+ Flush,
+ Straight,
+ ThreeOfAKind,
+ TwoPair,
+ Pair,
+ HighCard;
+ }
+
+ Card[] cards;
+
+ public Hand(Card[] cards) {
+ assert cards != null;
+ assert cards.length == 5;
+ this.cards = cards;
+ }
+
+ public Type type() {
+ var doubleHistogram = doubleHistogram();
+ var hasFlush = hasFlush();
+ var hasStraight = hasStraight();
+ if(hasFlush && hasStraight) return Type.StraightFlush;
+ if(hasFlush) return Type.Flush;
+ if(hasStraight) return Type.Straight;
+ if(doubleHistogram[4] == 1) return Type.FourOfAKind;
+ if(doubleHistogram[3] == 1 && doubleHistogram[2] == 1) return Type.FullHouse;
+ if(doubleHistogram[3] == 1) return Type.ThreeOfAKind;
+ if(doubleHistogram[2] == 2) return Type.TwoPair;
+ if(doubleHistogram[2] == 1) return Type.Pair;
+ return Type.HighCard;
+ }
+
+ int[] values() {
+ var values = new int[cards.length];
+ for(var i = 0; i < cards.length; i++) {
+ values[i] = cards[i].value;
+ }
+ return values;
+ }
+
+ /**
+ * Generates a histogram of card values.
+ * Each element in an array contains the number of cards with that value.
+ */
+ int[] histogram() {
+ return Histogram.histogram(values(), 14);
+ }
+ /**
+ * Generates a histogram of the histogram of card values.
+ * This shows how many times did any repetitions repeat.
+ * For example (1,1,2,2,2,3,3) first histogram results in (0,2,3,2)
+ * Second histogram (what this function returns) results in (1,0,2,1).
+ * This tells us that there are two pairs and one three of a kind.
+ */
+ int[] doubleHistogram() {
+ // if cards are dealt properly, the max is 5 (cards.length),
+ // as a card can't appear more than 4 times
+ var histogram = histogram();
+ // System.out.println(Arrays.toString(histogram));
+ return Histogram.histogram(histogram, cards.length + 1);
+ }
+
+ boolean hasFlush() {
+ var suit = cards[0].suit;
+ for(var i = 1; i < cards.length; i++) {
+ if(cards[i].suit != suit) return false;
+ }
+ return true;
+ }
+
+ boolean hasStraight() {
+ var values = values();
+ Arrays.sort(values);
+ var isStraight = true;
+ for(var i = 1; i < values.length; i++) {
+ if(values[i] != values[i-1] + 1) {
+ isStraight = false;
+ break;
+ }
+ }
+ if(isStraight) return true;
+ return isStraight || Arrays.equals(values, new int[]{
+ // A 10 J Q K is valid as well
+ 1, 10, 11, 12, 13
+ });
+ }
+}
diff --git a/week6/Histogram.java b/week6/Histogram.java
new file mode 100644
index 0000000..ba852e3
--- /dev/null
+++ b/week6/Histogram.java
@@ -0,0 +1,11 @@
+package week6;
+
+public class Histogram {
+ public static int[] histogram(int[] a, int max) {
+ int[] output = new int[max];
+ for(var x = 0; x < a.length; x++) {
+ output[a[x]]++;
+ }
+ return output;
+ }
+}
\ No newline at end of file
diff --git a/week6/HistogramTests.java b/week6/HistogramTests.java
new file mode 100644
index 0000000..e97527c
--- /dev/null
+++ b/week6/HistogramTests.java
@@ -0,0 +1,16 @@
+package week6;
+
+import java.util.Arrays;
+
+public class HistogramTests {
+ public static void main(String[] args) {
+ assert Arrays.equals(
+ new int[]{ 0,1,1,1 },
+ Histogram.histogram(new int[]{ 1,2,3 }, 4)
+ );
+ assert Arrays.equals(
+ new int[]{ 0,3,0,0 },
+ Histogram.histogram(new int[]{ 1,1,1 }, 4)
+ );
+ }
+}
diff --git a/week6/PokerAnalysis.java b/week6/PokerAnalysis.java
new file mode 100644
index 0000000..f9b81c5
--- /dev/null
+++ b/week6/PokerAnalysis.java
@@ -0,0 +1,81 @@
+package week6;
+
+import common.StdDraw;
+import common.StdRandom;
+import common.StdStats;
+
+public class PokerAnalysis {
+ /**
+ * Example program using PokerAnalysis
+ *
+ * Renders a plot of each type of hand
+ * The bars represent types in Hand.Type, in the same order
+ */
+ public static void main(String[] args) {
+ var decks = 1000;
+ // var hands = decks * 10;
+ var types = analyzeShuffledDecks(decks);
+ var typeFloats = new double[types.length];
+ var max = StdStats.max(types);
+ for(var i = 0; i < types.length; i++) {
+ // max or hands can be used here
+ typeFloats[i] = (double)types[i] / max;
+ }
+
+ StdDraw.setPenColor();
+ StdStats.plotBars(typeFloats);
+ }
+
+ /**
+ * Gets nth (5-card) hand of a given deck.
+ * Up to 10 hands can be dealt from a given deck
+ */
+ public static Hand getHand(Card[] deck, int handOffset) {
+ var offset = handOffset * 5;
+ return new Hand(new Card[]{
+ deck[offset],
+ deck[offset+1],
+ deck[offset+2],
+ deck[offset+3],
+ deck[offset+4]
+ });
+ }
+
+ /**
+ * Gets the first hand of a given deck.
+ */
+ public static Hand getHand(Card[] deck) {
+ return getHand(deck, 0);
+ }
+
+ public static Card[] getRandomDeck() {
+ Card[] deck = new Card[52];
+ for(var i = 0; i < 52; i++) {
+ var value = (i % 13) + 1;
+ var suit = i / 13;
+ deck[i] = new Card(Card.Suit.values()[suit], value);
+ }
+ StdRandom.shuffle(deck);
+ return deck;
+ }
+
+ /**
+ * Shuffles n decks, draws 10 hands from each, and saves the number of hands of each type
+ * Returns an array mapping type (using their ordinals) to number of hands found
+ */
+ public static int[] analyzeShuffledDecks(int decks) {
+ var types = new int[Hand.Type.values().length];
+ for(var i = 0; i < decks; i++) {
+ var deck = getRandomDeck();
+ for(var offset = 0; offset < 10; offset++) {
+ var hand = getHand(deck, offset);
+ types[hand.type().ordinal()]++;
+ }
+ }
+ return types;
+ }
+
+ public static int numberOfHandsOfType(int[] analyzedShuffledDecks, Hand.Type type) {
+ return analyzedShuffledDecks[type.ordinal()];
+ }
+}
diff --git a/week6/common b/week6/common
new file mode 120000
index 0000000..60d3b0a
--- /dev/null
+++ b/week6/common
@@ -0,0 +1 @@
+../common
\ No newline at end of file
diff --git a/week6/doc.typ b/week6/doc.typ
new file mode 100644
index 0000000..5e44628
--- /dev/null
+++ b/week6/doc.typ
@@ -0,0 +1,55 @@
+#import "./common/common.typ" : *
+
+#show: template
+
+= Week 6
+
+== Exercise 2.1.19
+
+Write a static method `histogram()` that takes an `int array a[]` and an
+integer $m$ as arguments and returns an array of length $m$ whose $i$th element is the
+number of times the integer $i$ appeared in `a[]`. Assuming the values in `a[]` are
+all between $0$ and $m-1$, the sum of the values in the returned array should equal
+`a.length`.
+
+#embedClass(name: "Histogram")
+
+== Exercise 2.1.30
+
+_Calendar_. Write a program `Calendar` that takes two integer commandline
+arguments $m$ and $y$ and prints the monthly calendar for month $m$ of year $y$, as
+in this example:
+
+```
+% java Calendar 2 2009
+February 2009
+ S M Tu W Th F S
+ 1 2 3 4 5 6 7
+ 8 9 10 11 12 13 14
+15 16 17 18 19 20 21
+22 23 24 25 26 27 28
+```
+
+#embedClass(name: "Calendar")
+
+== Exercise 2.2.26
+
+_Poker analysis_. Write a `StdRandom` and `StdStats` client (with appropriate
+static methods of its own) to estimate the probabilities of getting one pair, two pair,
+three of a kind, a full house, and a flush in a five-card poker hand via simulation.
+
+Divide your program into appropriate static methods and defend your design decisions.
+_Extra credit_ : Add straight and straight flush to the list of possibilities.
+
+#embedClass(name: "PokerAnalysis")
+
+#embedClass(name: "Hand")
+
+_Note that this reuses #context {
+ let label =