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 apple, 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", "APPLE", "LEMON", "COIN",
   * "BELL", or "SEVEN".
   */
  public void spin() {
   //...
  }

  /**
   * Returns the current face of this reel.  This does not change
   * the current face on the reel.
   */
  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, APPLE, 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 DESCRIPTION                      FREQUENCY 
   * ------------------------ ------ -------------------------------  --------- 
   *    COIN    COIN    COIN  = 70x  Coin JACKPOT                    (* 1 = 70)
   *   SEVEN   SEVEN   SEVEN  = 21x  Triple 7s                       (* 1 = 21)
   *     #       #       #    =  7x  Three of a Kind                 (* 4 = 28)
   *  $fruit  $fruit  $fruit  =  5x  Fruit Medley (1 of each fruit)  (* 6 = 30)
   *  #fruit   COIN   #fruit  =  3x  Coin w/ Matching Fruit Pair     (* 3 =  9)
   *     #     COIN      #    =  2x  Coin w/ Matching non-fruit Pair (* 2 =  4)
   *     ?     COIN      ?    =  1x  Coin                            (*30 = 30)
   * </pre>
   * <p>
   * Note that, for any payouts of 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. It gives some sort of payout on 47
   * of 216 the combinations (~22% chance on each pull of the handle).
   * <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: Test what you have so far

Your method signatures must exactly match those given above 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. It will also make sure your reels are spinning and changing when they should.

Beyond this, you are on your own for testing. There isn't much left to test, though--mostly just the getPayout() method. (See the FAQs below if you want some hints on how you might test this in a controlled way.)

Step 4: 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: [ APPLE |  COIN  |  APPLE]
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: [ APPLE |  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 |  APPLE]
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 getFace() method that returns one of each of the 6 String specified above (extra spaces allowed) (0.5). Multiple calls to getFace() should return the same result until spin() is called. Has a spin() method that randomly changes the result to be returned by the next getFace() call (1.0).
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

Authoring Hints
This assignment shouldn't be too hard if you understand object-oriented programming (OOP) 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:

See below for Testing Hints.

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.)

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--usually 1 to 4 lines.)

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 comparing the results of two method calls to be confusing, remember that you can save the results of the methods first and break this into smaller steps:

  String leftFace = this.left.getFace();
  String rightFace = this.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 is 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. As discussed in the FAQ above, 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;
  }...
Testing Hints
The hardest thing to test is getPayout(). It has 216 different combinations and randomness as well.

0) So the first option is to just manually run your program multiple times and wait to see if each possible payout is printed at some point. This will take a while. (By the end of the semester, you should know how write some code to pull the handle of your slot machine 1000 times and collect the results for you. But at this point you don't know loops or arrays yet, so you'd have to do this yourself.)

Still, 1000 runs would take only about 30 minutes if you can run your program and analyse the results in 2 seconds per run. But that assumes your code is correct! What happens if you finally get a COIN | COIN | COIN combo and the resulting payout is wrong? You'd have to fix your code and then test it all over again to see if the fix really worked!

So, a more controlled approach would be helpful here and probably take less time in the long run. Here are three such alternatives to testing your getPayout() method (and more are possible):

1) Add a second constructor to both SlotMachine and SlotMachineReel that lets you specify which face(s) you want to currently show on your new slot machine or reel. Then you could do something like this in a tester program:

SlotMachine tester;
tester = new SlotMachine("COIN", "COIN", "COIN);
System.out.println(tester.toString() + " pays " + tester.getPayout());
tester = new SlotMachine("SEVEN", "SEVEN", "SEVEN");
System.out.println(tester.toString() + " pays " + tester.getPayout());
...

2) Testing does not always have to be structured as a separate, static testing file, though. Sometimes you can test on-the-fly: tweak your code a bit, compile it and run it to make sure that test works, then you can change your code to test something else.

For example, say you added only a setFace method to your Reel class. You could then temporarily add this at the start of your getPayout() method:

  this.left.setFace("CHERRY");
  this.center.setFace("CHERRY");
  this.right.setFace("CHERRY");
  System.out.println("TESTING payout for: " + this.toString());
Then compile and run your program again for each of your tests, changing what you set the reels to each time. With this approach, you only have to write one simple extra setFace method and don't need a separate tester program. But it involves a lot more compiling and checking.

3) Finally, if you used local String variables in your getPayout() method, you can do a variation of #2 without writing any extra methods at all. Just temporarily change where you get the faces of the 3 reels:

  String leftFace = this.left.getFace();
  String midFace = this.center.getFace();
  String rightFace = this.right.getFace();
to set these variables to certain values:
  String leftFace = "CHERRY";
  String midFace = "CHERRY";
  String rightFace = "CHERRY";
and then you can compile and run you program to test different combinations. Note that your output will be confusing while you do this: If you print your slot machine, the faces shown by your program will not be the ones you are testing in getPayout()! But you can still see if the printed payout is correct for the combo you are currently testing.

Note that, regardless of which of these approaches you choose, you shouldn't have to test every possible combination! Just test each logical branch of your getPayout() code. Testing one example of each payout type is a good start on this. You may then need more tests, depending on the structure of your code.

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.