Assignment 11

Task

Review OOP concepts by simulating a slot machine.

Concepts: OOP; random number generation.
Textbook: 4.1 - 4.5; 3.4 - 3.5

Steps

You are going to model a simple slot machine. This slot machine will have three reels, each with six symbols. The symbols include a red cherry, a green watermelon, a yellow lemon, a grey bell, a blue number 7, and a silver coin. The center reel's coin will be gold, and if this coin ever comes up, you will always win at least your original token back. (Your slot machine will be text-based, though; so you'll just have to imagine the colors.)

You will write 3 classes to do this.

Step 1: SlotMachineReel

This class represents a single reel in a slot machine. It has 6 symbols, represented by Strings (see below). It should contain the following constructor and two instance methods. You may also copy the /** javadocs */ that describe what each method should do.

  /**
   * Constructs a new reel with its current face randomly set to
   * one of the 6 symbols on this reel.
   */
  public SlotMachineReel() {
   //...
  }

  /**
   * Spins this reel.  This randomly sets this reel's face to
   * one of its 6 symbols: "CHERRY", "MELON", "LEMON", "COIN",
   * "BELL", or "SEVEN".
   *
   * Returns the new symbol showing on this reel after the spin.
   */
  public String spin() {
   //...
  }

  /**
   * Returns the current face of this reel.  This is the same
   * value as returned by the last call to spin().
   */
  public String getFace() {
    //...
  }

You will probably need one or more private instance variables to help you keep track of which symbol/face is currently showing on the reel. You may write additional methods if you want to, but you must have these at least. You may add spaces to the 6 Strings (so they are all the same length), but they must each contain only the specific capital letters given in the spin() method documentation.

Step 2: SlotMachine

A slot machine will contain three SlotMachineReel objects. You will probably need 3 instance variables--one to hold each reel. Then you need the following methods:

  /**
   * Constructs a new slot machine with three reels.
   */
  public SlotMachine() {
    //...
  }

  /**
   * Spins all three reels in this slot machine.
   */
  public void pullHandle() {
    //...
  }

  /**
   * Returns a String containing the three faces currently
   * shown on the front of the slot machine.  The String
   * lists the faces in left-center-right order, with a |
   * character between faces and no line-breaks.  
   * 
   * @see SlotMachineReel.getFace()
   */
  public String toString() {
    //...
  }

  /**
   * Returns the payout multiplier for the combination of
   * faces currently on the front of this slot machine.
   * <p>
   * The payout table is given below.  In this table,
   * "fruit" means CHERRY, MELON, or LEMON;
   * # means a symbol that matches one of the other 3 symbols;
   * $ means a unique symbol (does not match any other shown); and
   * ? means any symbol).
   *
   * <pre>
   *         REEL FACES       PAYOUT  FREQUENCY  DESCRIPTION
   * ------------------------ ------ ----------- -----------------------------
   *    COIN    COIN    COIN  = 70x  (* 1 = 70) = Coin JACKPOT
   *   SEVEN   SEVEN   SEVEN  = 21x  (* 1 = 21) = Triple 7s
   *     #       #       #    =  7x  (* 4 = 28) = Three of a Kind
   *  $fruit  $fruit  $fruit  =  5x  (* 6 = 30) = Fruit Medley (1 of each fruit)
   *  #fruit   COIN   #fruit  =  3x  (* 3 =  9) = Coin w/ Matching Fruit Pair
   *     #     COIN      #    =  2x  (* 2 =  4) = Coin w/ Matching non-fruit Pair
   *     ?     COIN      ?    =  1x  (*30 = 30) = Coin
   * </pre>
   * <p>
   * Note that, for any payouts involving a single coin, the COIN must be
   * showing on the center reel (just as shown above).
   * <p>
   * Since this slot machine earns 216 tokens per cycle through all
   * permutations, but pays out only 192 tokens for the same cycle,
   * it has a ~89% payout rate.
   * <p>
   * Returns a number between 0 and 70 as given in the PAYOUT column above.
   * Returns 0 if no payout match is found, meaning the player lost his
   * initial bet.
   */
  public int getPayout() {
    //...
  }

Step 3: UsernameA11

This is your main user interface class. It will contain a main method that creates a slot machine, pulls its handle, prints the resulting symbols shown (all on one line), and announces the resulting payout.

The only integers that should appear in your output is the payout multiplier. In the case that the player lost, you may either print a "0" somewhere (as in, "You win 0 tokens") or the word "lose" or "lost" instead.

Note that there is no user input.

Sample Output

D:\TA\grading\A11> java ZtomaszeA11
SLOT MACHINE
You drop a token into the slot and pull the handle...
You see: [ COIN  |  SEVEN |  COIN ]
You lost your token.

D:\TA\grading\A11> java ZtomaszeA11
SLOT MACHINE
You drop a token into the slot and pull the handle...
You see: [ MELON |  COIN  |  MELON]
You WIN 3 tokens!

D:\TA\grading\A11> java ZtomaszeA11
SLOT MACHINE
You drop a token into the slot and pull the handle...
You see: [ MELON |  COIN  |  LEMON]
You WIN 1 token.

D:\TA\grading\A11> java ZtomaszeA11
SLOT MACHINE
You drop a token into the slot and pull the handle...
You see: [ COIN  |  COIN  |  COIN ]
You WIN 70 tokens!

D:\TA\grading\A11> java ZtomaszeA11
SLOT MACHINE
You drop a token into the slot and pull the handle...
You see: [CHERRY |  SEVEN |  MELON]
You lost your token.

D:\TA\grading\A11> java ZtomaszeA11
SLOT MACHINE
You drop a token into the slot and pull the handle...
You see: [ SEVEN |  SEVEN |  SEVEN]
You WIN 21 tokens!

You can also run the javadoc on your source code to extract your documentation into an API-like webpage. Here's an example of the output when I ran it on my code: Slot Machine Documentation.

What to Submit

Place all three classes into a single file, just as you did for A10. You'll need to take the public off of SlotMachine and SlotMachineReel. Also, if you used java.util.Random, you may need to move the import statements.

Then upload your complete UsernameA11.java file to Tamarin.

Grading [10 points]

1 - Compiles
Your program compiles successfully (no errors). Your code follows Java coding standards.
2 - SlotMachineReel
Has a constructor that takes no parameters (0.5; must work to get credit for other methods). Has a spin() method that randomly returns one of each of the 6 String specified above (extra spaces allowed) (1.0). Has a getFace() method that returns the same String as returned by the last call to spin() (0.5).
6 - SlotMachine
Has a constructor that takes no parameters (0.5; must work to get credit for other methods). Has a toString() method that returns a String containing the three symbols on each of the reels (2.0). Has a pullHandle() method that spins the reels so as to change the results returned by the next toString() call (1.0). Has a getPayout() method that returns the correct payout modifier in accordance with the current faces shown (as returned by toString()) and the payout table given above (2.5).
1 - UsernameA09
Program takes no user input, but prints the 3 randomized symbols of a slot machine (using the String symbols specified above, all on one line) and then prints the resulting payout for a bet of 1 "token". The payout should be the only integer in the output; in the case of a 0 payout, you may print the words "lose" or "lost" somewhere instead of a 0.

FAQs

Hints
This assignment shouldn't be too hard if you understand Object-Oriented Programming and did well on A10. However, note that 80% of your grade is determined by how your SlotMachine and SlotMachineReel perform! That means it's great if your main program works correctly, but you can still score very poorly if your other two classes fail to follow the specifications given above.

In terms of procedural logic and algorithms, the hardest part is probably the getPayout() method. Some things to consider when writing this:

I'm still not clear what code goes into each method...
Okay, here's a brief overview of how you could implement each class or method to meet the specification given by the /** docs */ above. (Other implementations are possible, and you can use different instance variable names.)

SlotMachineReel should have one private String instance variable named face. This holds the single symbol currently showing on the front of this reel.

SlotMachineReel() - initialize face to a valid value in case someone looks at the front of a reel before spinning it for the first time. (You could call spin() to do this; or you could just initialize each new Reel to show the same symbol.)

spin() - Generate a random symbol and store it into face. (To do this, generate a random number between 0 and 5, then translate each possible value to one of the 6 Strings.) Return the value you just stored into face.

getFace() - return the value currently stored in face.

SlotMachine should have three private SlotMachineReel instance variables--left, center, and right.

SlotMachine() - initializes your three instance variables with three new SlotMachineReel objects.

pullHandle() - spin() each of your three reels. (Note that you'll only ever spin() a reel from within this method.)

toString() - use getFace() to get the String symbol currently on the front of each of your three reels. Combine these into a single String and return it.

getPayout() - use getFace() to see what's currently on the front of each reel, and then use that information to determine what the payout should be according to the payout table. (This can be tricky, and so this will be your longest method; most of your other methods should be pretty short--an average of 3 to 4 lines.)

On a related note, I've noticed that some students are a little too casual matching the method signatures above. The methods must match exactly (including case) or Tamarin will be unable to grade your assignment--even if your main program works flawlessly. So to help make sure you got your method signatures correct, here is a very basic tester that simply calls each of your methods at least once: SlotMachineTester.java

In getPayout(), how do I see what's on a reel? How do I compare that to something else?
It depends on what you named your instance variables in your SlotMachine class. I used left, center, and right for my three reels. In this case, I can compare two reels like this:
  if (left.getFace().equals(right.getFace()) {
    //matching pair...

If you find this confusing, remember that you can save the results of the methods first and break this into smaller steps:

  String leftFace = left.getFace();
  String rightFace = right.getFace();
  if (leftFace.equals(rightFace) {
    //matching pair...

It accomplishes the same thing.

Remember you have to compare the faces (which are Strings), and not the reels (which are SlotMachineReel objects). For example, this is not correct: if (this.left.equals(this.right) {, since it comparing the entire reel, not just the currently displayed face. (Technically, this could be made to work if you wrote an equals method in your SlotMachineReel class that does the work of comparing the String faces; but, at this point, that is probably even more confusing than just using the String comparison code given above.)

Remember too to use the .equals method, not ==, when comparing Strings. If you need to do "not equals", you can use the ! (not) operator:

  if (!leftFace.equals(rightFace) {
    ...
So for getPayout(), can I just list all the possible combinations of 3 reels and the corresponding payout?
There are only 216 combination, so I suppose this is humanly possible. But please don't do it! Use some logic to simplify your code instead.

For example, most combinations result in a payout of 0. So, if you use an if/else-if/else structure, you can test for all the cases that really do pay something and then use the else block to return 0 for everything else. Thus, if one of the earlier cases didn't match a paying combination, your code will reach the else and you'll return 0.

Similarly, if you've already tested the current faces for all the combinations including a COIN in the center slot that payout more than 1x, then you can simply have something like this to match all 30 of the possible 1x payouts:

  }else if (center.getFace().equals("COIN")) {
    return 1;
  }else ...

Note that I'm using the reel object here, so I need to get the face before comparing it to a String. You could declare three local String variables at the start of getPayout() for the faces and so simplify your code a bit:

  String midFace = this.center.getFace();
  ...
  }else if (midFace.equals("COIN")) {
    return 1;
  }...
When I combine my classes in one file, I get a compile-time error about my import statement.
All import statements must be placed at the top of the file before any class definitions. Therefore, if you import something for SlotMachine or SlotMachineReel, you'll need to move that import statement to the top of the file when you copy those classes into UsernameA11.java.