Rock Paper Scissors
Back to the Program-A-Day homepage
ProblemRock-Paper-Scissors is a game with two players with one in three chance of winning. The outcome of each round will result in the player to either win, lose, or draw. In this example, we're going to make a simulation of the game, Rock-Paper-Scissors, to play against the computer. During the execution of the program, the player may request a summary that prints the current score record. The score record includes the number of wins, loses, and draws, as well as the winning average, (in percentage to one decimal place) and the player's highest winning streak. |
...By Students"Whenever you are having trouble planning or solving a problem, writing a program, debugging a program, or just analyzing code, write/draw it out on paper. Step through each line of code so you know what you want the program to do, or what the code is doing. When you refer back to the code in your program, use print statements and test your variables to make sure you're getting the same results or the results that you should be getting. When you write or draw each step on paper (ex: arrays), it will give you a visual aid of what your program is doing. In the case you are fixing errors, you can compare and match your program with your diagrams to pinpoint where the errors occur." | |
---|---|---|
SolutionBefore we start programming, the first thing to do when solving a problem is understanding the problem, breaking it down, and planning out how to solve the smaller problems (divide and conquer). Let's start off by going over the rules in Rock Paper Scissors. In Rock Paper Scissors, a player plays against an opponent where both of the players select either rock, paper, or scissors as their weapon. At the same time, both of the players draw their selected weapon. The result of each round will result in win, lose, or draw depending on the player and their opponent's selected weapon. Rock beats scissors, scissors beats paper, and paper beats rock. Now that we understand how the game is played, we can start breaking the problem down and figure out how problems should be. In rock paper scissors there are two players, so in our program we need two variables (player and computer) to hold their chosen weapon. The player's choice will be obtained through the user's input and the opponent will be determined by the computer. Because we want the computer to generate a unique choice for each round, we will use a random number generator. Each number represents either rock, paper, or scissors. As you may have noticed, there are only three different choices so our number generator must generate one of three numbers. (int)(Math.random() * 3) Math.random() will generate a number between 0 and less than 1, if we multiply it by 3 we will get either 0, 1, or 2 values before the decimal. We can use 0, 1, and 2 to represent rock, paper, and scissors. We don't need the decimal value so we will cast it as an int and store it in an int variable to cut the decimal values off. This tells us that the variable computer should be an int type variable (as well as the variable player).
final int ROCK = 0; final int PAPER = 1; final int SCISSORS = 2; ROCK will be represented by 0, PAPER will be represented by 1, and SCISSORS will be represented by 2. Note that these constants should never change at any time during our program so set them to be final. Because we want to be able to keep track of the number of wins, loses, draws, average, and win streak, the easiest way to do this is to use an array to store the result of each round. The easiest way to do this is using the same number representation method we used earlier to represent the results of each round, 1 for win, 2 for lose, and 3 for draw. So we'll use an int array rounds[] to store the result of each round. Keep in mind that when we use an array, we need to initialize a maximum size before we can use it. Pick a big random number as the array size (ex: 1000). final int MAX = 1000; int [] rounds = new int [MAX];
In general, you should have a visual idea that looks like, main method { loop (to get input from user) { if the player agrees to play another round { result[current round] = call playRound(); } } } playRound() method { calculate win, lose, or tie; return an int value that represents win, lose, or draw (1,2, or 3 respectively); } Now that we have the basic layout of our program, we can start filling in the details.
input = JOptionPane.showInputDialog(null,"Message for the user goes here."); Since we want the user to play multiple rounds, we'll use a loop to continue getting input from the user. There are two cases that we have to consider for the loop to be true. Case 1: Since we have an array storing the result of each round, we have a limit of the number of rounds there can be during each execution of the program (the size of the array). We can't store results exceeding the size of the array. The easiest way to do this is using a counter to keep track of each round and making sure the count does not exceed the max size of the array. Case 2: If the number of rounds is represented by max size of the array, that means the user MUST play that many times. If the number is too small, the game will end too early when the user wants to keep playing. If the number is too big, the user will get frustrated, bored, and want the game to end. To solve this, we'll give the user the choice to quit the game at any time. Since we are taking input from the user, we will let the user quit the game by entering the command "quit". The easiest way to do this is using a while loop and loop when the expression is true. When the user enters the command "quit", set the boolean expression to false to exit the loop.
final int MAX = 1000; String input; int count = 0; int [] rounds = new int [MAX]; boolean loop = true; : while((count < MAX) && (loop)) { input = JOptionPane.showInputDialog(null,"Message for the user goes here."); if((input == null) || (input.equalsIgnoreCase("quit"))) { loop = false; } : }
Note: JOptionPane's show input dialog function, there is an OK and CANCEL button. If the user clicks CANCEL, allow them to quit the program. We handle this matter similar to if the user enters the "quit" command, (if(input == null)).
If the player enters either "Rock", "Paper", or Scissors", then we want to process the game's round result. If a player enters "Rock", we want to set the variable player to the value that ROCK represents. Remember that the variable player is an int type, and we have ROCK, PAPER, SCISSORS represented by int values. This will allow us to assign player variable with the value representing ROCK, PAPER, or SCISSORS. For example, if player enters "rock", player variable will hold the value 0. final int ROCK = 0; final int PAPER = 1; final int SCISSORS = 2; : : if(input.equalsIgnoreCase("rock")) { player = ROCK; : }
if(input.equalsIgnoreCase("rock")) { player = ROCK; rounds[count] = playRound(player,ROCK,PAPER,SCISSORS); count++; }
Repeat this step for user's input of "paper" or "scissors". Once your done, piece it together with the other statements and you should have something like the following. if((input == null) || (input.equalsIgnoreCase("quit"))) { loop = false; } else if(input.equalsIgnoreCase("rock")) { player = ROCK; rounds[count] = playRound(player,ROCK,PAPER,SCISSORS); count++; } else if (input.equalsIgnoreCase("paper")) { player = PAPER; rounds[count] = playRound(player,ROCK,PAPER,SCISSORS); count++; } else if (input.equalsIgnoreCase("scissors")) { player = SCISSORS; rounds[count] = playRound(player,ROCK,PAPER,SCISSORS); count++; } else if (input.equalsIgnoreCase("summary")) { //do something for summary here. : } else { System.out.println("Invalid input."); }
Side note: Although we have numbers representing rock, paper, and scissors to be used in our algorithm, we should keep the program's communication level with the user at the English language level.
Create your playRound() method. The parameters are int player, int ROCK, int PAPER, and int SCISSORS. The return value will be an int value which will either be 1 = win, 2 = lose, or 3 = draw. We will need an int variable that holds the result of the round to be returned (ex: int result). As you may have noticed, we have all the elements we need to complete a round of rock paper scissors except for one, the computer’s selected weapon. We will generate the computer’s selection in the playRound() method because we are only using it in this method. So far, your method should look something like this, public static int playRound(int player, int ROCK, int PAPER, int SCISSORS) { int computer; int result = 0; computer = (int)(Math.random() * 3); : }
Do this for all the possible outcomes (take some time to think about this and write it down). Once you're done, your if statement should look something like this. if((player == ROCK) && (computer == SCISSORS)) { result = 1; } else if((player == ROCK) && (computer == PAPER)) { result = 2; } else if((player == PAPER) && (computer == ROCK)) { result = 1; } else if ((player == PAPER) && (computer == SCISSORS)) { result = 2; } else if ((player == SCISSORS) && (computer == PAPER)) { result = 1; } else if ((player == SCISSORS) && (computer == ROCK)) { result = 2; } else if (player == computer) { result = 3; }
Once your if statement is complete, we have processed the necessary data that this method is intended to do. All we need to do is returning the value. Add your return statement, and then, your method is complete. Your method should look something like this, public static int playRound(int player, int ROCK, int PAPER, int SCISSORS) { int computer; int result = 0; computer = (int)(Math.random() * 3); if((player == ROCK) && (computer == SCISSORS)) { result = 1; System.out.println("Computer chose scissors. You are victorious!"); } else if((player == ROCK) && (computer == PAPER)) { result = 2; System.out.println("Computer chose paper. You have been defeated."); } else if((player == PAPER) && (computer == ROCK)) { result = 1; System.out.println("Computer chose rock. You are victorious!"); } else if ((player == PAPER) && (computer == SCISSORS)) { result = 2; System.out.println("Computer chose scissors. You have been defeated."); } else if ((player == SCISSORS) && (computer == PAPER)) { result = 1; System.out.println("Computer chose paper. You are victorious!"); } else if ((player == SCISSORS) && (computer == ROCK)) { result = 2; System.out.println("Computer chose rock. You have been defeated."); } else if (player == computer) { result = 3; System.out.println("Computer chose the same. It's a draw!"); } return result; }
Now all we have to do is creating the summary method. If the user enters "summary", then we want to display the user's current score record where we print out the number of wins, loses, draws, winning average, and win streak. This will be based on the array of results stored in the rounds[] array. If you notice, the summary method can be called at any time, and whenever it is called we don’t know how many elements are stored on the array so far. However, the count variable keeps track of how many rounds that have been played. In conclusion, we will need to pass the rounds[] array and the count variable to the summary method. Go back to your main method and finish the statement for if the user enters the “summary” command by adding the method call statement. It should look something like this. : else if (input.equalsIgnoreCase("summary")) { summary(rounds,count); } : Now create the summary method itself. The parameters are int [] rounds and int count, and there are no return values. The first thing we want to do is determining the number of wins, loses, and draws where we will declare three new variables to hold these counts. (ex: int wins, int loses, and int draws). Next, we need to go through the array of results, and increment the count of the appropriate variables depending on the value stored (ex: if the result of the 10th round is 1, then increment the variable wins by 1). The method so far should look something like this. int wins = 0; int loses = 0; int draws = 0; for(int i = 0; i < count; i++) { if(rounds[i] == 1) { wins++; } else if(rounds[i] == 2) { loses++; } else if(rounds[i] == 3) { draws++; } }
The winning average is calculated as the number of wins divided by the number of rounds played multiplied by 100. So the parameters are int wins and int count and the return value will be the calculated average. Note that the calculated average will be in decimals, so the return type variable and the variable that stores winning average should be double type variable. Also, create an average variable inside the getAverage method to hold your calculations. Your method so far should look something like this. public static double getAverage(int wins, int count) { double average; : }
average = (double) wins / count; average = average * 100;
Your average calculation should look something like this (where temp is an int variable). temp = (int) (average * 10); average = (double) temp / 10; For example, if you had wins = 1 and count = 3. int wins = 1; int count = 3; average = (double) wins / count; //average = 0.3333333 average = average * 100; // average = 33.33333 temp = (int) (average * 10); // temp = 333 average = (double) temp / 10; // average = 33.3
public static double getAverage(int wins, int count) { double average; int temp; average = (double) wins / count; average = average * 100; temp = (int) (average * 10); average = (double) temp / 10; return average; }
Create your winningStreak method. To calculate the best winning streak, we need two variables (ex: int streak and int winStreak). The streak variable will keep count of the number of wins in a row. If the result is either a lose or draw then we reset streak to 0. You will want to initialize streak variable to 0 so that we can just increment the count as we go through the array. The winStreak variable will hold the largest number of wins in a row. Going further ahead, you will also want to initialize this variable to 0 (you will see why we do this later). Your method so far should look something like this. public static int winningStreak(int [] rounds, int count) { int streak = 0; int winStreak = 0; : }
Your algorithm should look something like this, for(int i = 0; i < count; i++) { if(rounds[i] == 1) { streak++; } else { streak = 0; } if(streak > winStreak) { winStreak = streak; } }
public static int winningStreak(int [] rounds, int count) { int streak = 0; int winStreak = 0; for(int i = 0; i < count; i++) { if(rounds[i] == 1) { streak++; } else { streak = 0; } if(streak > winStreak) { winStreak = streak; } } return winStreak; }
Your summary method should look something like this. public static void summary(int [] rounds, int count) { int wins = 0; int loses = 0; int draws = 0; double average; int winStreak; for(int i = 0; i < count; i++) { if(rounds[i] == 1) { wins++; } else if(rounds[i] == 2) { loses++; } else if(rounds[i] == 3) { draws++; } } average = getAverage(wins,count); winStreak = winningStreak(rounds,count); System.out.println("===Summary==="); System.out.println(""); System.out.println("Total wins: " + wins); System.out.println("Total losses: " + loses); System.out.println("Total draws: " + draws); System.out.println("Winning Average: " + average + "%"); System.out.println("Win Streak: " + winStreak); }
| ||
Code |