This is an ongoing school project that I would like to improve. The point is to make the code as efficient (or short) as possible. I would like to reduce it by finding an al
Switch statements goes like that. Try to look at the e.g i gave you and try to understand the flow of the code.
Best way to learn is with your hands! Random switch eg:
switch(expression) {
case x:
code block
break; // after every statement e.g: (console.log) you need to use
"break"
case y:
code block
break;
default:
code block
}
Your code:
switch (weapon) {
case chosenOne:
return console.log(alert("It's a tie! Try again to win!"));
break;
case "Rock":
case "Paper":
alert("You lost! Paper beats the rock.");
break;
case "Paper":
case "Scissors":
console.log(alert("You lost! The scissors cut the paper."))
break;
case "Scissors":
case "Rock":
console.log(alert("You lost! The rock beats the scissors."))
break;
case "Scissors":
case "Paper" :
alert("You won! Scissors cut the paper.");
break;
case "Paper":
case "Rock":
console.log(alert("You won! Paper beats the rock."))
break;
case "Rock":
case "Scissors":
alert("You won! The rock beats the scissors.");
default:
return "somthing went wrong"
break;
Quite late to the party, but here's a slightly different way to the other answers in here. I've included the answer in TypeScript, but you can obviously remove the types if you like.
type Weapon = "Rock" | "Paper" | "Scissors";
type Result = "Player" | "Computer" | "Draw";
type GameLogic = Record<Weapon, Array<Weapon>>;
// Map that tracks what each weapon beats.
const outcomes: GameLogic = {
Rock: ["Scissors"],
Paper: ["Rock"],
Scissors: ["Paper"]
};
const determineWinner = (playerOneWeapon: Weapon, playerTwoWeapon: Weapon): Result => {
if (playerOneWeapon === playerTwoWeapon) {
return "Draw";
}
return outcomes[playerOneWeapon].includes(playerTwoWeapon)
? "Player One"
: "Player Two";
}
This implementation has the ability to scale nicely, too. As you can add extra weapons into the mix and the implementation of determineWinner
doesn't change - by adding to the Weapon
type and the outcomes
map:
type Weapon = "Rock" | "Paper" | "Scissors" | "Lizard" | "Spock";
const outcomes: GameLogic = {
Rock: ["Scissors", "Lizard"],
Paper: ["Rock", "Spock"],
Scissors: ["Paper", "Lizard"],
Lizard: ["Spock", "Paper"],
Spock: ["Rock", "Scissors"]
};
We can now support a game where each weapon beats each weapon exactly twice and each weapon loses to a weapon exactly twice (as opposed to the classic variation where everything beats one weapon and loses to one weapon).
You can also use an array to check the winner. Order the array so that the winner is always on the right side. Then compare if the machine's choise is the one next to user's choise, like so:
var weapons = ['paper', 'scissors', 'rock'],
user = 'scissors',
machine = 'paper',
uIdx = weapons.indexOf(user),
mIdx = weapons.indexOf(machine),
winner;
if (uIdx !== mIdx) {
winner = (mIdx === (uIdx + 1) % 3) ? 'machine' : 'user';
} else {
winner = 'tie';
}
console.log(winner);
A fiddle to play with.
The modulo operator makes the magic at the end of the array. If user has chosen "rock", the next to it would be undefined, but the modulo operator of 3 % 3
returns 0, hence "paper" is compared to "rock".
I removed some of your variables and combined some, just to make it shorter. I also got rid of the bulk of the if/else
since it's not really needed here. For more info on how a switch
works, check out https://javascript.info/switch.
I also changed up your choices so that you can add multiple win or loss conditions for each choice, in case you wanted to upgrade to Rock,Paper,Scissors,Lizard,Spock ( https://www.youtube.com/watch?v=cSLeBKT7-s ).
// Set up our various choices, how they rank, and their action (can also be array if desired).
const choices = {
Rock : { win:["Scissors"] , action:"beats" } ,
Paper : { win:["Rock"] , action:"beats" } ,
Scissors : { win:["Paper"] , action:"cuts" } ,
Spock : { win:["Rock","Scissors"] , action:"beats" }
} ;
// Use the keys in choices as our selectable items.
const weapons = Object.keys(choices) ;
// Our basic intro.
const rps = prompt("Welcome to Rock, Paper, Scissors. Would you like to play?" + '\n' + "If you do, enter number 1." + '\n' + "If you don't, enter number 2.");
// Set the computer choice.
const chosenOne = weapons[Math.floor(Math.random()*3)];
// This is an example of your switch.
switch (rps) {
case "1" : // Since we used text input, we have to evaluate for a text "number".
alert("Remember:" + '\n' + " - Rock beats the scissors" + '\n' + " - Paper beats the rock" + '\n' + " - The scissors cut the paper");
// Make your choice.
let weapon = prompt("Make your choice:" + '\n' + weapons, "");
// Is our choice valid?
if ( !weapons.includes(weapon) ) {
alert("Invalid choice. Closing Game."); break;
} else {
alert("You chose: " + weapon + '\n' + "The computer chose: " + chosenOne);
}
// Did I win?
alert( compareRPS(weapon,chosenOne) ) ;
break ; // This will break out of the switch. Otherwise will fall through to next case.
case "2":
alert("Thanks for visiting! See you later.");
break ;
default :
alert("Invalid option. Closing game.");
// No break needed here since this is the end of the switch.
}
// I broke the check-you-vs-cpu functionality out into its own function.
function compareRPS(youC,cpuC) {
if ( youC === cpuC ) {
return "It's a tie! Try again to win." ;
}
if (choices[youC].win.includes(cpuC)) {
return "You won! " + youC + " " + choices[youC].action + " " + cpuC + "." ;
} else {
return "You lost! " + cpuC + " " + choices[cpuC].action + " " + youC + "." ;
}
}
NOTE: I also switch between const
and let
. See https://codeburst.io/part-2-var-vs-const-vs-let-69ea73fe76c1 for differences. I mostly use const
to indicate a variable I won't change and let
to be one that I can (within its proper scope). There's also var
, but I didn't need it here.
Since more strightforward answers were already provided, I wrote you an alternative using more advance features of javascript. These features are an overkill for the problem at hand, but are useful as a learning exercise.
// Create an array 'battle' as a result from mapping over the given options
const battle = ["Rock", "Paper", "Scissors"].map(
/* The resulting array is conformed of tuples (arrays of 2 values).
The first value is the weapon of choice and the second value is a function.
That function will receive as an argument another weapon and will return the outcome of battling the first option with the second.
Positive number is win, 0 is tie and negative number is loose. */
(weapon, index, array) => [weapon, enemy => {
const res = index - array.findIndex(x => x === enemy)
return !!(res%2)? res : res * -1
}]
/* The reduce transform the array of tuples into an object,
with each choice as a property key and each function as the property value */
).reduce((accumulator, current) => {accumulator[current[0]] = current[1]; return accumulator}, {})
/* Output is just a function that receives your weapon.
It returns a function that receives your enemy's weapon.
Which in turn uses the previous object with functions to get the result.
Finally it uses the result to print one sentence or other, using template strings. */
const output = mine => enemy => {
const result = battle[mine](enemy)
return result > 0 ? `You won! ${mine} beats ${enemy}`
: result < 0 ? `You lost! ${enemy} beats ${mine}`
: "It's a tie! Try again to win"
}
console.log(output("Rock")("Paper"))
You can define an object that define if your move is weak or strong against another. Example:
const myChoice = 'Rock'
const enemyChoice = 'Scissors'
const weapons = {
Rock: {weakTo: 'Paper', strongTo: 'Scissors'},
Paper: {weakTo: 'Scissors', strongTo: 'Rock'},
Scissors: {weakTo: 'Rock', strongTo: 'Paper'}
}
if (weapons[myChoice].strongTo === enemyChoice) {
// I won
return;
}
if (weapons[myChoice].weakTo === enemyChoice) {
// I Lost
return;
}
// tie