Write a simple rock-paper-scissors (aka, jan-ken-pon) game that plays a single round. Place the game logic into its own class that is separate from the user interface details.
Textbook: 4.2
Concepts: Constants. Review of: OOP, encapsulation, conditionals, random numbers, methods, etc.
RockPaperScissors
Write a class named RockPaperScissors
. It should have the following contents.
First, declare the following public static final int
constants: ROCK, PAPER, SCISSORS, WIN, LOSE, DRAW, and NO_MOVE. Assign unique int
values to each of them. (If you want to, you can reuse some of the same values you assign to the first three constants when you assign values to the second three. However, NO_MOVE should not share a value with any of the other constants.)
Then you will need to write the bodies of the following constructor and three methods. The required behavior of each is defined in the javadocs given before the method. You should document your methods like this; you are welcome to just copy the /** javadocs */ provided here.
/** * A rock-paper-scissors game. * <p> * The rules are simple: the two players each choose one of the three * possible moves. Then, paper beats rock, rock beats scissors, * and scissors beats paper. If the two moves are the same *(for example, both rocks) then the game is a draw. * <p> * This class requires only that the current (human) player specify * a move. The other (computer) player's move will be generated * randomly. * * @author ??? */ public class RockPaperScissors { /** * Constructs a new rock-paper-scissors object that can * play multiple games. */ public RockPaperScissors() /** * Given a valid player's move, plays a single game, * updating game state accordingly. * <p> * Playing a game involves the following steps: * <ul> * <li>randomly generating a computer move and storing it * <li>comparing the computer move to the player's move passed as an * argument to this method and updating the stored game outcome * accordingly. The game outcome is whether the player experienced * a WIN, LOSE, or DRAW. * </ul> * <p> * If the passed player move is not valid (ie, not ROCK, PAPER, or * SCISSORS), this method does nothing (that is, no state is changed) * and returns false. If the move is valid and a game is played, this * method returns true. */ public boolean play(int playerMove) /** * Returns the computer move used in the last game. * That is, returns the computer move generated and stored by the * last call to the play method. * <p> * This method does not generate a new move, so repeated * calls to this method will continue to return the same value until * the play method is called again. * <p> * Returns the value of one of the ROCK, PAPER, or SCISSORS constants. * If this method is called on a new game object before play has been * called for the first time, returns NO_MOVE instead. */ public int getComputerMove() /** * Returns the outcome for the player in the last game played. * That is, returns the outcome that was stored by the last call * to the play method. * <p> * This method does not generate a new outcome, so repeated * calls to this method will continue to return the same value until * the play method is called again. * <p> * Returns either WIN, LOSS, or DRAW. If this method is * called on a new game object before play has been called for * the first time, returns NO_MOVE instead. */ public int getOutcome() }
Declare any instance variables you need to produce the required behavior of these methods. Try to limit your instance variables to the smallest number possible, and remember to make them all private.
Hint: All of the game logic should be in the play method. All the get methods can be implemented in one line that simply return an expression or the value from an instance variable.
Optional: You may write additional methods if you like. For example, a method like this:
public String getComputerMoveAsString()
might be useful, especially once you start writing your main method in Step 3. This method would work like getComputerMove()
, but it would return a String (such as "rock", "paper", or "scissors") rather than an int
(such as one of the ROCK, PAPER, or SCISSORS constants).
Test that your class meets the required specification. To help you do that, here is a tester class:
As always, your class should compile with this tester program. Have a look through the tester class to see what it is doing. As mentioned within the tester class, note that it does not test everything. You may want to write some additional tests or at least visually verify that your code meets these untested specifications.
UsernameA09
Now write a text-based user interface that uses your RockPaperScissors class so that someone can play a single game.
This involves the following steps:
• Ask the user to enter a move, either as a string or as an int. (You only have to support one of these two input modes, though it is possible to support both.)
You can have them enter one of the words "rock", "paper", or "scissors". Sample prompt:
Enter your move (rock, paper, or scissors):
Or you can instead print a short menu explaining how the numbers 1, 2 and 3 map to "rock", "paper", or "scissors", and then ask them to enter the corresponding number. Something like this would work (though you could possibly do it clearly in a single line instead). Sample menu prompt:
1) Rock 2) Paper 3) Scissors Enter the number of your move:
• Once you have the player's move, play a game of RockPaperScissors.
• Print the computer's move as a string (not as a number!): either "rock", "paper", or "scissors" on its own labeled line. Sample:
Computer's move: paper
• Then determine whether the human player experienced a win, loss, or draw.
When displaying output, you may refer to the computer as "computer", "PC", "I", or "my". You may refer to the player as "player", "user", "human", or "you". (Case does not matter.) Game outcomes can be "win", "won" or "wins"; "lose" or "lost"; or "tie" or "draw". Three sample outcomes:
You won! The human loses. Tie game.
The game should only play one round and then quit.
Upload your UsernameA09.java
file (including your working non-public RockPaperScissors
class) to Tamarin.
When you submit, Tamarin will only perform a basic evaluation of your code. In lab next week (07 Oct), you will be assigned to groups and will evaluate each other's code in a code review session. Attendence is required for this lab. Also, to ensure we have some code to look at, you must turn in your A09 on time!
After that review session, you will have a chance to resubmit this assignment as UsernameA09r.java
(note the r
, for revised). You may only resubmit if you turned in A09 on-time. If you resubmit, your final grade for A09 will be your A09r grade. If you cannot or choose not to resubmit, your A09 grade will be your final grade.
After the A09r deadline, Tamarin will perform a more thorough grading of both A09 and A09r.
RockPaperScissors
play(int)
method: getOutcome()
is correct given the passed move for the human player and the result of getComputerMove()
(3.5). getComputerMove()
method:play(int)
is called for the first time; then returns either ROCK, PAPER, or SCISSORS values (0.5). play(int)
is called again (affects grading of play method). getOutcome()
method:play(int)
is called for the first time; then returns either WIN, LOSE, or DRAW values (0.5). play(int)
is called again (affects grading of play method).
UsernameA09
.equals
or .equalsIgnoreCase
to compare Strings (if you find you need to compare any Strings).
If the outcome was only win or lose, we could use a boolean
variable to track it. However, there are 3 possible outcomes (4 including NO_MOVE). And there's also more than 2 possible moves. So we can't use boolean
values to represent them.
Using String
s can get messy because comparing them tends to be error-prone. For example, "rock", " rock", "Rock", "ROCK" are not equivalent Strings. It's easy for small typos like this to cause bugs. Also, it's easy for beginning programmers to forget that you should always use .equals
(not ==
) to compare Strings, which can cause more bugs.
So the best choice here is to represent moves and outcomes using int
values.
However, using int
s quickly gets confusing. For example, within your RockPaperScissor
class, you might have logic like this (shown here in pseudocode):
if computer has 1: if player has 1: outcome is 2 else if player has 2: outcome is 3 else...
It's hard to spot any logic errors when using int
literals like this. For example, do I have 2 and 3 in the right place when assigning an outcome here? Or did I get them backwards? It's hard to tell which values are supposed to correspond with which outcomes.
Similarly, what about when I go to use the RockPaperScissors
class later? Suppose I do this in my main/user interface class:
int result = rps.getOutcome(); if (result == 2) { System.out.println("You won!"); }
Did I do this correctly? Does 2 mean win? I don't know. I'd have to define somewhere in the documentation of RockPaperScissor
or getOutcome()
which int
values correspond to which outcomes so that the programmer that later (re)uses the RockPaperScissors
class will know what the returned values mean.
Constants solve both of these problems. First, it makes your own code within RockPaperScissors
easier to read (and so easier to understand and write correctly). For example:
if computer has ROCK: if player has ROCK: outcome is LOSE else if player has PAPER: outcome is DRAW else...
Now behind the scenes (ie, when this code compiles), all these constant variables just turn into int
values, equivalent to the first example above. But using the constants instead of the literals makes it very easy to see any logic errors here: these outcomes are both wrong! They should be DRAW and WIN instead.
And now I don't have to tell the programmer that uses the RockPaperScissors
class what the returned int
values mean in terms of rocks or paper or winning or losing. I can just refer them to the constants I defined, and they can just use them too:
int result = rps.getOutcome(); if (result == RockPaperScissors.WIN) { System.out.println("You won!"); }
(Since this code is outside the RockPaperScissors class, I do have to include the class name before the constant.)
Hopefully this shows you both the usefulness of constants and how to use them in your code.
public boolean play(int playerMove)
, I don't really
get how to use boolean
. Am I supposed to use an if
here somehow?
The boolean here is your return value. You are supposed to return true
if the playerMove
was valid and you played a game. But you should return false
if the playerMove
was not valid. So you'll need an if
statement to check if the playerMove
value was illegal. But then, based on that, you'll return one of the two boolean literals: either true
or false
.
To illustrate, one way to do this is something like:
public boolean play(int playerMove) {
if (playerMove is not equal to ROCK, PAPER or SCISSORS) {
return false; //stops execution of the method right here
}
// The rest of your code goes here, which will only be reached if
// the above if statement failed.
// After playing the game, end the method with:
return true;
}
Of course, you'll have to turn the condition given in this if
statement into proper Java code first. And there are other valid ways to structure this:
public boolean play(int playerMove) {
if (playerMove is valid) {
//play the game (most of the code in this method is here)
return true;
}else {
return false; //playerMove was invalid
}
}
When you write methods, you always want their behavior to be well-defined for any possible values that might get passed to them. So the error-checking of playerMove
should happen within the play
method. On the bright side, that means you don't need to repeat that same testing code in your main/user interface class:
//ask user for their move int move = keybd.nextInt(); boolean worked = rps.play(move); if (worked) { //print computer's move and game outcome }else { //play returned false, so move entered by player must have been invalid. System.out.println("Sorry, but you did not enter a legal move."); }
(Note that this example assumes you're asking the user to enter ints rather than Strings and that the values of your ROCK, PAPER, and SCISSORS constants correspond to 1, 2, and 3.)