Pages

Rounders

Guys around here will tell ya, you play for a living, it's like any other job. You don't gamble, you grind it out.
— Mike McDermott, Rounders (1998)

Basic Exercise

Study, compile, execute, and modify the Java program shown below. Add or modify statements per the instructions that appear in the form of comments in the source code. Check your work using the utilities provided in the Sample Input/Output section that follows the source code.

Challenge Exercises

  1. Create a class called RiffleShuffle with a shuffle() method that simulates riffle shuffling and could be used instead of KnuthShuffle.shuffle().
  2. Write a program that tries to determine whether Dealer.shuffle() uses KnuthShuffle.shuffle() or RiffleShuffle.shuffle(). Your program should call Dealer.shuffle(). Pretend that you and your program cannot see how Dealer.shuffle() is implemented; in other words, you can only observe the effects of calling the method.
  3. Repeat one or both of the previous two exercises for other shuffling techniques. Analyze the algorithms you implement.

Java Program

public class Rounders
{  
  private static String nextRound(String[] players, int numCardsEach)
  {
    final int maxPlayers = numCardsEach == 0 ? players.length : Dealer.deckSize / numCardsEach;
    final int numPlayers = players.length > maxPlayers ? maxPlayers : players.length;
    final int[][] hands = Dealer.deal(numPlayers, numCardsEach);
    String s = "";
    for (int i = 0; i < numPlayers; i++)
    {
      s += Cards.parseCards(players[i], hands[i]);
    }    
    return s + "\n";
  }
  public static String grind(String[] players, int numCardsEach, int numRounds)
  {
    Dealer.newDeck();
    String s = "";
    while(numRounds-- > 0)
    {
      s += nextRound(players, numCardsEach);
      Dealer.shuffleDeck();
    }
    return s;
  }
  public static void main(String[] args)
  {
    String s = Rounders.grind(args, 13, 1);
    s += Rounders.grind(args, 5, 2);
    s += Rounders.grind(args, 1, 1);
    s += Rounders.grind(args, 0, 1);
    System.out.print(s);
  }
}

class Dealer
{
  public final static int deckSize = 52;
  private static int[] deck = null;
  
  public static int[] newDeck()
  {
    deck = new int[deckSize];
    for (int i = 0; i < deckSize; i++)
    {
      deck[i] = i;
    }
    return deck;
  }
  public static int[][] deal(int numPlayers, int numCardsEach)
  {
    final int[][] hands = new int[numPlayers][numCardsEach];

    // Add code here.

    return hands;
  }
  public static void shuffleDeck()
  {
    // Implement this method. Use KnuthShuffle.shuffle().
  }
  public static void main(String[] args)
  {
    String s = Cards.parseCards("new deck", Dealer.newDeck());
    System.out.print(s);
  }
}

class Cards
{
  private static String parseSuit(int card)
  {
    int suit = card / 13;
    switch (suit)
    {
      case 0:
        return "\u2663"; // clubs
      case 1:
        return "\u2666"; // diamonds
      case 2:
        return "\u2665"; // hearts
      case 3:
        return "\u2660"; // spades
      default:
        return "-";
    }
  }
  private static String parseRank(int card)
  {
      return "?"; // Replace this line of code. Use the % operator and a switch statement.
  }
  public static String parseCards(String label, int[] cards)
  {
    String s = label + ":";
    for (int card : cards)
    {
      s += " " + parseRank(card) + parseSuit(card);
    }
    s += "\n";
    return s;
  }
}

class KnuthShuffle
{
  private static void swap(int[] a, int i, int j)
  {
    // Implement this method.
  }
  private static int random(int i, int j)
  {
    double r = Math.random();

    // Add code here.

    return (int) r;
  }
  public static void shuffle(int[] a)
  {
    // Implement this method.
  }
}

Sample Input/Output

Note: Output is also written to the console. (Try pressing F12.)

args:

Q & A

Ask away! (Your questions and my answers go here.)

  1. What is the Dealer.deal() method supposed to do?
    The Dealer.deal() method should use the static variable Dealer.deck to assign values to the variable hands, a two-dimensional array of values of type int. It’s possible, by studying the Rounders class and the values displayed when you press the buttons in the Sample Input/Output section, to determine how values from Dealer.deck should be dealt. (Spoiler alert: if you would like to figure this out on your own, then stop reading now; otherwise, continue.) The element hands[i] is an array of integers representing cards that have been dealt to the ith player. The element hands[i][j] is an integer representing the jth card dealt to the ith player. Let’s assume there are, say, three players: in that case, Dealer.deal() should assign the first value from Dealer.deck (deck[0]) to hands[0][0] (in other words, the first card should be dealt to the first player); the second value (deck[1]) should be assigned to hands[1][0] (the second card should be dealt to the second player); the third value (deck[2]) should be assigned to to hands[2][0] (the third card should be dealt to the third player); the fourth value (deck[3]) should be assigned to hands[0][1] (the fourth card should be dealt to the first player); and so on, until each player has been dealt the specified number of cards. Exactly how the cards should be dealt when the program runs will depend on the values of the parameters numPlayers and numCardsEach. If you’re not sure how to implement this algorithm using Java, consider using two loops (for loops, for example), one nested inside the other. Note that you will probably need to use three variables of type int: one to keep track of which value/card in Dealer.deck will be dealt next; another to keep track of which player will receive the next card; and a third to keep track of how many cards have been dealt to a given player so far.

Exercise Objectives

  • Practice working hard and and being creative, especially if you attempt the challenge exercises.
  • Practice comprehending code that someone else wrote.
  • Practice working with a two-dimensional array in Dealer.deal().
  • Practice using a switch statement in Cards.parseRank().
  • Practice swapping values in KnuthShuffle.swap().
  • Practice converting the output of Math.random() to an integer that is between a min and max value in KnuthShuffle.random().
  • Practice implementing the Knuth/Fisher-Yates shuffle algorithm in KnuthShuffle.shuffle().
  • Observe the use of hexadecimal Unicode character codes in Cards.parseSuit().
  • Observe how classes composed of static methods and variables can be designed to solve familiar, interesting problems In this case, the problem is simulating a deck of cards, shuffling a deck of cards, and dealing some number of cards to some number of players.
  • Observe how methods and variables can be artfully partitioned among classes according to the nature of the class, as suggested by the names of the identifiers involved.
  • Observe how a class (Dealer) can contain static variables as well as static methods.
  • Observe how top-down programming would describe the development process used if the class Rounders were implemented first, before the other classes were implemented.
  • Observe how bottom-up programming would describe the development process used if the class KnuthShuffle were implement first, before being used in Dealer.shuffle().
  • Observe how Dealer.shuffleDeck() is an interesting example of an abstraction: shuffleDeck() hides the particular shuffling technique employed from code that calls shuffleDeck(); furthermore, shuffleDeck() could, if desired, be changed to use a different shuffling technique (e.g. a riffle shuffle) without requiring methods that call Dealer.shuffleDeck() to change.
public class Rounders
{  
  private static String nextRound(String[] players, int numCardsEach)
  {
    final int maxPlayers = numCardsEach == 0 ? players.length : Dealer.deckSize / numCardsEach;
    final int numPlayers = players.length > maxPlayers ? maxPlayers : players.length;
    final int[][] hands = Dealer.deal(numPlayers, numCardsEach);
    String s = "";
    for (int i = 0; i < numPlayers; i++)
    {
      s += Cards.parseCards(players[i], hands[i]);
    }    
    return s + "\n";
  }
  public static String grind(String[] players, int numCardsEach, int numRounds)
  {
    Dealer.newDeck();
    String s = "";
    while(numRounds-- > 0)
    {
      s += nextRound(players, numCardsEach);
      Dealer.shuffleDeck();
    }
    return s;
  }
  public static void main(String[] args)
  {
    String s = Rounders.grind(args, 13, 1);
    s += Rounders.grind(args, 5, 2);
    s += Rounders.grind(args, 1, 1);
    s += Rounders.grind(args, 0, 1);
    System.out.print(s);
  }
}

class Dealer
{
  public final static int deckSize = 52;
  private static int[] deck = null;
  
  public static int[] newDeck()
  {
    deck = new int[deckSize];
    for (int i = 0; i < deckSize; i++)
    {
      deck[i] = i;
    }
    return deck;
  }
  public static int[][] deal(int numPlayers, int numCardsEach)
  {
    final int[][] hands = new int[numPlayers][numCardsEach];
    int k = 0;
    for (int j = 0; j < numCardsEach; j++)
    {
      for (int i = 0; i < numPlayers; i++)
      {
        hands[i][j] = deck[k];
        k++;
      }
    }
    return hands;
  }
  public static void shuffleDeck()
  {
    KnuthShuffle.shuffle(Dealer.deck);
  }
  public static void main(String[] args)
  {
    String s = Cards.parseCards("new deck", Dealer.newDeck());
    System.out.print(s);
  }
}

class Cards
{
  private static String parseSuit(int card)
  {
    int suit = card / 13;
    switch (suit)
    {
      case 0:
        return "\u2663"; // clubs
      case 1:
        return "\u2666"; // diamonds
      case 2:
        return "\u2665"; // hearts
      case 3:
        return "\u2660"; // spades
      default:
        return "-";
    }
  }
  private static String parseRank(int card)
  {
    int rank = card % 13;
    switch (rank)
    {
      case 0:
        return "A";
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
        return ((Integer)(rank + 1)).toString();
      case 10:
        return "J";
      case 11:
        return "Q";
      case 12:
        return "K";
      default:
        return "-";
    }
  }
  public static String parseCards(String label, int[] cards)
  {
    String s = label + ":";
    for (int card : cards)
    {
      s += " " + parseRank(card) + parseSuit(card);
    }
    s += "\n";
    return s;
  }
}

class KnuthShuffle
{
  private static void swap(int[] a, int i, int j)
  {
    int tmp = a[i];
    a[i] = a[j];
    a[j] = tmp;
  }
  private static int random(int i, int j)
  {
    double r = Math.random();
    r *= (1 + j - i);
    r += i;
    return (int) r;
  }
  public static void shuffle(int[] a)
  {
    int n = a.length;
    int last = n - 1;
    for (int i = 0; i < n; i++)
    {
      swap(a, i, random(i, last));
    }
  }
}