function computerPlay() { let choices = ['rock', 'paper', 'scissors']; return choices[Math.floor(Math.random() * choices.length)]; }
In the above function, you create the choices array again and again. This is a waste of time and memory. It's better to create the choices once and for all:
const choices = ['rock', 'paper', 'scissors']; function computerPlay() { return choices[Math.floor(Math.random() * choices.length)]; }
On the other hand, the JavaScript compiler may be able to prove that creating this array each time is not necessary. In that case the code might stay as it is. I don't know how advanced the optimization techniques for JavaScript compilers are in 2020. You would have to measure this using a memory profiler.
The playRound function is quite big, and having all these inner functions does not increase the readability. Especially since you are inconsistent: The message for a tie is in the lower half while the two other messages are in the upper half. All these messages belong together, therefore they should be written in the same area of the code.
function playRound(playerSelection, computerSelection) { const capitalize = word => { return word.charAt(0).toUpperCase() + word.slice(1); } const winStatement = (winner, loser) => { return `You Win! ${capitalize(winner)} beats ${capitalize(loser)}`; } const loseStatement = (winner, loser) => { return `You Lose! ${capitalize(winner)} beats ${capitalize(loser)}`; } const winningChoice = {'rock': 'paper', 'paper': 'scissors', 'scissors': 'rock'};
In all these inner functions, you can remove the { return at the beginning and the } at the end. This makes them shorter and easier to understand since they now look more similar to the mathematical concept of a function, which is written as \$f\colon A \to B\$, and not \$f\colon A \to \left\{ \mathop{\text{return}} B \right\}\$.
The winningChoice should be moved outside of this function, just like the choices in computerPlay.
playerSelection = playerSelection.toLowerCase();
Having to deal with uppercase and lowercase words makes this function more complicated than necessary. Normalizing the user input should be done somewhere else. This way, the playRound function can focus on its single purpose, which is deciding the outcome of the game.
if(playerSelection === computerSelection) return "Oh! It's a tie"; else if(playerSelection === winningChoice[computerSelection]) return winStatement(playerSelection, computerSelection); else return loseStatement(computerSelection, playerSelection); }
This code looks quite condensed. The usual style is to write if (, with a space.
A general guideline is that your code should not require horizontal scrollbars. Therefore you should place all the return statements into separate lines, like this:
if (playerSelection === computerSelection) return "Oh! It's a tie"; else if (playerSelection === winningChoice[computerSelection]) return winStatement(playerSelection, computerSelection); else return loseStatement(computerSelection, playerSelection);
This way you can clearly see the structure of the code by reading only the leftmost word of each line: if return else return else return.
When I copied your code duringthe review, I made some other changes, and this is the final result:
const choices = ['rock', 'paper', 'scissors']; const beats = {'rock': 'paper', 'paper': 'scissors', 'scissors': 'rock'}; const upper = {'rock': 'Rock', 'paper': 'Paper', 'scissors': 'Scissors'}; function computerPlay() { return choices[Math.floor(Math.random() * choices.length)]; } function playRound(human, computer) { return human === computer ? `Oh! It's a tie` : human === beats[computer] ? `You win! ${upper[human]} beats ${computer}` : `You lose! ${upper[computer]} beats ${human}`; } const playerSelection = choices[0]; const computerSelection = computerPlay(); console.log(playRound(playerSelection, computerSelection));
As you can see, I decided to only capitalize one of the words, and I also created a fixed translation table instead of capitalizing the words on-the-fly. Again, I did this to avoid unnecessary object creation. This kind of performance optimization is not necessary for a simple Rock Paper Scissors game, but I tried to be consistent in the code. Oh well, except that the You win and You lose strings are still templates that have to be created dynamically.
I also made the variable names a bit shorter: human instead of playerSelection. I removed the selection since that was not an essential part of the name. I renamed the variable from player to human since both the human and the computer are players of the game, which makes the variable name player ambiguous.
In playRound it's a stylistic choice whether to use the ?: operator or an if else chain. At first I used an if (human === computer) return 'tie' combined with a ?: operator for deciding between you win and you lose. I didn't like this mixture because it felt inconsistent. I had to settle on either using the ?: operator throughout or the if else chain. I chose the ?: operator because it is shorter. On the other hand, the if else chain would have probably been clearer.