6
\$\begingroup\$

I am learning JavaScript and I just wrote a Rock, Paper, Scissors game that will first prompt the two players for their choices and validate them. In the event of a tie, each player will choose their answer again and it will once again be validated. Everything works fine, but it feels like I have a lot of repetition going on here. Since I'm new to this, I can't figure out a way to make this more concise if possible.

var compare = function(choice1, choice2){
    while (choice1 != 'rock' && choice1 != 'paper' && choice1 != "scissors"){
        choice1 = prompt('Invalid entry. Player 1, please choose rock, paper, or scissors only');
    }    
    while (choice2 != 'rock' && choice2 != 'paper' && choice2 != "scissors"){
        choice2 = prompt('Invalid entry. Player 2, please choose rock, paper, or scissors only');
    }
    while (choice1 == choice2){
        choice1 = prompt('Tiebreaker!. Player 1, please choose rock, paper, or scissors only');
     while (choice1 != 'rock' && choice1 != 'paper' && choice1 != "scissors"){
        choice1 = prompt('Invalid Entry. Player 1, please choose rock, paper, or scissors only');
        }
        choice2 = prompt('Tiebreaker!. Player 2, please choose rock, paper, or scissors only');
     while (choice2 != 'rock' && choice2 != 'paper' && choice2 != "scissors"){
        choice2 = prompt('Invalid Entry. Player 2, please choose rock, paper, or scissors only');
        }

    }
    if (choice1 == 'rock'){
        if (choice2 == 'scissors'){
            return 'rock wins';
        }
        else{
            return 'paper wins';
        }
    }
    if (choice1 == 'paper'){
        if (choice2 == 'rock'){
            return 'paper wins';
        }
        else{
            return 'scissors wins';
        }
    }  
    if (choice1 == 'scissors' ){
        if (choice2 == 'rock'){
            return 'rock wins';
        }
        else{
            return 'scissors wins';
        }
    }
}
\$\endgroup\$

migrated from stackoverflow.com Jun 10 '13 at 11:53

This question came from our site for professional and enthusiast programmers.

11
\$\begingroup\$

Just build an object with the associativity.

// what wins to what
var winsTo = {
  'rock': 'scissors',
  'paper': 'rock',
  'scissors': 'paper',
};

// invalid selected
if (!(choice1 in winsTo && choice2 in winsTo)) {
  alert("bad choice !");
  return;
}

// same choice => equality
if (choice1 == choice2) {
  alert("Equality!");
} else if (winsTo[choice1] == choice2) {
  alert("choice1 won!");
} else {
  alert("choice2 won!");
}
\$\endgroup\$
6
\$\begingroup\$

While you can get away with this sort of thing in rock paper scissors, any more options and it becomes unmanageable. (For example, rock paper scissors lizard spock).

There is a general-case solution to this problem and it is called an adjacency matrix. An adjacency matrix is basically the computer's representation of a directed graph. This is useful because your problem actually has a directed graph under the hood.

Consider the following graph:

Rock Paper Scissors state machine . . .

In this situation, we begin at the starting state, then follow the transitions for each player's move (not all transitions are shown).

Now it is a mathematical property of state machines such as this that they can be represented using a matrix (which we call an Array in computer science).

So we create an array with a row and column for each state (1 represents victory for player 1, 0 represents tie, -1 represents victory for player 2)

Player 2 ->  Rock    Paper    Scissors
Player 1:
Rock          0        1         1
Paper         1        0         -1
Scissors      -1       1         0

Now if we get the user's input as a number where rock is assigned the value of 0, paper the value 1 and scissors 2, we can look up the result of our operation using:

var result = adjacency_matrix[player_1_value][player_2_value];

switch( result )
{
    case 0:
        break;
        // Tie
    case 1:
        // Victory player 1
        break;

    case 2:
        // Victory player 2
        break;
}

Now there are some nice things that are Javascript specific like dictionaries which make this a little nicer to read in code, but if you understand the fundamentals of this solution you will be able to solve any of this type of problem in any language.

\$\endgroup\$
  • \$\begingroup\$ That certainly works, but you don't need that 2nd depth in this case. Let's do it simply! \$\endgroup\$ – user1737909 Jun 10 '13 at 7:08
  • 1
    \$\begingroup\$ Actually there is no second depth. If you look again at the diagram you will notice that each state in the "second depth" has only one transition to a final state. I included it for the sake of generality. \$\endgroup\$ – ose Jun 10 '13 at 7:16
  • \$\begingroup\$ I mean in the javascript code, actually \$\endgroup\$ – user1737909 Jun 10 '13 at 7:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy