Rock, Paper Scissors: How do I get it to replay more than once?

This is my first post to the forum with any code so my apologies if my format is wrong. Still trying to learn how the forums work but I am stuck. I have tried to Google some answers as well as search for some help on the forums but no luck. I may have missed an article or I may not have searched with the correct wording.

I finished the Rock, Paper, Scissors game and decided I wanted to try and add a couple features to it. The first was to take a users input. This all works fine but if I enter a word that is not expected I get “undefined” instead of the error message. It also shows me the computers answer. Not sure what I am missing there. It looks as though my if/else statement is correct to me but something is not working the way I expected it to.

Also, I can’t seem to get the program to ask to play the game more than once. It works fine for one round through and then it just ends on the second try. This has me stumped.

Any input would be greatly appreciated including code formatting if that is way out of line.

Thank you.

const readline = require(‘readline-sync’);

console.log(‘Welcome to Rock, Paper, Scissors’);

function start() {
console.log(‘Please choose, Rock, Paper, or Scissors’);
const getUserChoice = userInput => {
userInput = readline.question();
userInput = userInput.toLowerCase();
if (userInput === ‘rock’ || userInput === ‘paper’ ||
userInput === ‘scissors’ || userInput === ‘bomb’) {
return userInput;
} else {
console.log(‘Error, please select rock, paper or scissors’);
}
};
const getComputerChoice = () => {
const randomNumber = Math.floor(Math.random() * 3);
switch (randomNumber) {
case 0:
return (‘rock’);
case 1:
return (‘paper’);
case 2:
return (‘scissors’);
}
};

const determineWinner = (userChoice, computerChoice) => {
if (userChoice === computerChoice) {
return ‘This game is a tie’;
}
if (userChoice === ‘rock’) {
if (computerChoice === ‘paper’) {
return ‘Sorry, the computer has won!’;
} else {
return ‘Congratulations, you won!’;
}
}

if (userChoice === 'paper') {
  if (computerChoice === 'scissors') {
    return 'Sorry, the computer has won!';
  } else {
    return 'Congratulations, you won!';
  }
}

if (userChoice === 'scissors') {
  if (computerChoice === 'rock') {
    return 'Sorry, the computer has won!';
  } else {
    return 'Congratulations, you won!';
  }
}

if (userChoice === 'bomb') {
  return 'Congratulations, you won!';
}

};

const playGame = () => {
const userChoice = getUserChoice();
const computerChoice = getComputerChoice();
console.log('You threw: ’ + userChoice);
console.log('The computer threw: ’ + computerChoice);
console.log(determineWinner(userChoice, computerChoice));
};

playGame();
}
start();

console.log(‘Would you like to play again? y/n’);
let response = readline.question();

if (response !== ‘n’) {
start();
}

Think about what the actual flow of the script is when called. Javascript is a single-threaded language and runs a script sequentially by default. Therefore we’re just working from top to bottom, and the script does not jump around. Therefore when we think about what’s going on here we need to think about how Javascript will run this code.

  1. Declare a function called start that encapsulates the entire game within it. The steps within here are not important, all we need to know is that start is created.
  2. Call the start function and run it in its entirety i.e. play one full game of RPS.
  3. Log the string “Would you like to play again? y/n” to the console.
  4. Set up a variable named response and read the user input into that variable.
  5. Run an if statement to check if user response is anything other than “n”. If so, call the start function again.

Then that’s the end of our script! So we run the game, ask the user if they want to play again, and if so they can play again. However after the end of that second start() call we reach the end of the script and there’s no more code to be run. Therefore you’ll never ask more than once.

So how should we actually do this? Well the common way is to use a loop, hence why in video game terms you get the phrase “game loop”. Essentially what you do is loop through the same code for as long as the user wants to play, and then break the loop when they decide they’re done. A good way to do this is with either a while loop or a do…while loop.

While loop

The while loop would essentially be host to your break condition, and by default endlessly run your game loop until your user decides to break. So the game loop would be this.

let response = "";
while (response !== "n") {
	start();
	console.log("Would you like to play again? y/n");
	response = readline.question();
}

We move our response declaration to be outside the while loop as it needs to be declared for the loop to recognise it. Then we run the game, ask the user if they want to play again, and if they don’t enter exactly “n” then it’ll go back to the start of the loop and run the game again. This is fine and works but in this case I think a do…while loop is a bit better.

Do…while loop

A do…while loop always runs the block of code first no matter what. The reason this can matter is because it guarantees that your game will be run once. Imagine in the last chunk of code if you initialised response to be “n”. Then the game would never fully run, as soon as the first line of code is run, the while loop would see that the condition is met and break the loop, thus ending the game. However with a do…while loop, it will always run your block exactly once all the way through before then checking the loop condition from then on. The way this would be done is below.

let response = "n";
do {
	start();
	console.log("Would you like to play again? y/n");
	response = readline.question();
} while (response !== "n");

Note that even though I’ve initialised response = "n" here, the full block still runs and the user gets a chance to make their decision before the loop breaks. That’s not what happens if you try the same thing with the regular while loop.

Tl;DR

Essentially all this to say that JS always runs from top to bottom unless using async coding (Promises, async await etc). This means that we need to use loops to ensure code is run over and over again, without us having to pre-determine the number of runs to do by repeating function calls. In this case, a do…while loop is the best use-case for looping through your game code, ensuring that the user always gets the choice to continue playing as long as they want before quitting the game.

1 Like

Thank you so much for that explanation. I think I had a little bit of tunnel vision and forgot all about those loops. Definitely need to open my mind up to some better problem solving.

Question on my if statement at the end though; you mentioned JS being a single-threaded language and works top to bottom. What causes the if statement I had at the bottom to only be getting used once? Once it gets processed the first time does the program simply ignore it because it was already used? It appears to me that if we say “y” and it goes to start() again it should simply start the program over from the start() line and continue down from top to bottom. Just trying to wrap my head around it so I don’t make that mistake again.

One other question that didn’t get addressed. In the first portion where we ask the user for Rock, Paper, Scissors or Bomb, if the user enters any of those four options it will return the userInput, if not , it should return an “Error” message. Instead it states "You threw: undefined. I thought my code looked pretty straight forward but I am obviously missing something.

Again, thank you for your time with the very thorough answer. Great explanation that will stick with me. Greatly appreciated.

That’s no problem! The way you need to think about it is that when the start() function is called, the code isn’t actually jumping back up in the script to where that function is defined. When the function is defined you can think of that being packaged and stored in memory. When the function is called, Javascript isn’t looking back in the script to place you in the right position, it’s calling that pre-packaged function from memory and running it. The way I find easiest to think about it is that when JS calls the function, it basically just inserts the function body wherever the function call is. So for a shorter example, if I have

function increment_by_one(num) {
    return num + 1;
}

let num = 0;

num = increment_by_one(num);
num = increment_by_one(num);
num = increment_by_one(num);

Rather than jumping back up to the declaration, Javascript would read this code as

let num = 0;

num = num + 1;
num = num + 1;
num = num + 1;

So thinking about it this way, we can see that the if statement would only be run once, as it’s not actually jumping back up above in the script to the start declaration, but just inserting the function body wherever it is called.

1 Like

As for your second question, I apologise as I did miss that in my original answer, and I’m breaking this out into it’s own answer for clarity! This is a result of function behaviour in Javascript. In other languages you need to define a return type i.e. if you are returning a string from the function, you need to tell the script that in advance, if you want to return nothing, you need to state that in advance also. This means that in those languages, a function can only return one type, and if you try to return something that’s not that type it’ll throw an error.

Javascript however doesn’t have this, what’s called strict typing, and instead allows you to return multiple different types from a function, for example;

function return_multiple_types(num = 10) {
    if (num > 0 && num <= 3) {
        return "hello";
    }
    else if (num > 3 && num <= 6) {
        return true;
    }
    else if (num > 6 && num <= 9) {
        return 7;
    }
}

This is a valid function in JS, and depending on the input here we could get back a string, a boolean or a number. However you may notice that the default argument is 10 for this function, which currently isn’t handled by our if statement at all. This begs the question, what is returned if nothing is stated explicitly? Well the issue you’re getting is the answer, the default return value for a function is undefined, meaning that if you don’t explicitly return something from a function, then the value returned from that function will be undefined.

Knowing this we can go back to userChoice, as this is what’s giving the unexpected output. userChoice gets assigned whatever value is returned from the function call getUserChoice(). This is the userInput variable whenever the user has entered a valid input, however what happens when it’s not one of the 4 valid inputs? Well it goes to the else and prints the error message fine, however the program doesn’t actually stop, instead it continues on with the execution as it has no reason to actually stop. Therefore when the getUserChoice() function call finishes, it realises it hasn’t returned anything yet and so it returns undefined as we discussed in the last paragraph. Hence this is what is assigned to userChoice, and this is what is printed out with console.log("You threw: " + userChoice);.

Hopefully that all makes sense! I apologise for the very long messages, but hopefully it can cover not just what is happening, but also why it is happening and so you can know for future!

1 Like

Thank you for the great explanation on why the code would only ask to replay once. After reading your explanation I was able to look at my code and make complete sense of it. Thank you again for that.

The next part I have to digest a little more. Your explanation is very well written but my limited knowledge is going to require just a little more thinking on it. It makes sense but since I am not near as proficient at this as many other developers I know I need to take a moment to process it and walk myself through it. Thank you for giving me some tools to work with. I am going to see what I can do and if I get completely stuck I may reach back out to you. But once again, thank you so much for your time. I greatly appreciate it and you have been very helpful.

1 Like

It’s a tricky topic and likely more advanced than where you are in the course currently! As you continue through the course you should definitely come back to this when you’re more confident and you can hopefully make a bit more sense of it. Much of the quirks of Javascript can only be really learned by actively working with it.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.