RPSSL resolution logic#

πŸ€” Question to ponder

How do we determine which player has won?

In other words, how do we resolve different situations into win, lose, and tie?

Analyzing an easier game: Rock paper scissors#

If we have only rock πŸͺ¨ paper πŸ—’οΈ and scissors βœ‚οΈ, then we have 9 combinations for player 1 and player 2 named as p1 and p2, respectively.

p1

p2

p1 …

πŸͺ¨

πŸͺ¨

tie

πŸͺ¨

πŸ—’οΈ

loses

πŸͺ¨

βœ‚οΈ

wins

πŸ—’οΈ

πŸͺ¨

wins

πŸ—’οΈ

πŸ—’οΈ

tie

πŸ—’οΈ

βœ‚οΈ

loses

βœ‚οΈ

πŸͺ¨

loses

βœ‚οΈ

πŸ—’οΈ

wins

βœ‚οΈ

βœ‚οΈ

tie

πŸ€” Question to ponder
  1. How would you implement this in C?

  2. Do you see any simplifications on the table that leads to less rows?

After simplification we end up with:

p1

p2

p1 …

πŸͺ¨

πŸ—’οΈ

loses

πŸͺ¨

βœ‚οΈ

wins

πŸ—’οΈ

πŸͺ¨

wins

πŸ—’οΈ

βœ‚οΈ

loses

βœ‚οΈ

πŸͺ¨

loses

βœ‚οΈ

πŸ—’οΈ

wins

else

tie

As a result, we have 3 * 3 - 3 = 6 if-else if lines with Boolean expressions and one else line.

Activity 29 (RPS resolution logic)

  1. 3 min. Draw a flowchart for the resolution table above.

  2. Implement it using the template below:

    • 3 min. using if-else

    • 3 min. using switch

  3. Which solution do you prefer?

    • if-else

    • switch

#include <stdio.h>
enum { ROCK, PAPER, SCISSORS, SHAPE_COUNT } p1, p2;
const char *SHAPE_STRINGS[] = {"πŸͺ¨", "οΈπŸ—’οΈ", "βœ‚οΈ"};

int main() {
  for (size_t p1 = 0; p1 < SHAPE_COUNT; ++p1) {
    for (size_t p2 = 0; p2 < SHAPE_COUNT; ++p2) {
      printf("If p1: %s  and p2: %s  => ", SHAPE_STRINGS[p1],
             SHAPE_STRINGS[p2]);
      // YOUR CODE HERE
    }
    puts("");
  }
}

The output should be:

If p1: πŸͺ¨  and p2: πŸͺ¨  => Tie.
If p1: πŸͺ¨  and p2: οΈπŸ—’οΈ  => p1 looses.
If p1: πŸͺ¨  and p2: βœ‚οΈ  => p1 wins.

If p1: οΈπŸ—’οΈ  and p2: πŸͺ¨  => p1 wins.
If p1: οΈπŸ—’οΈ  and p2: οΈπŸ—’οΈ  => Tie.
If p1: οΈπŸ—’οΈ  and p2: βœ‚οΈ  => p1 looses.

If p1: βœ‚οΈ  and p2: πŸͺ¨  => p1 looses.
If p1: βœ‚οΈ  and p2: οΈπŸ—’οΈ  => p1 wins.
If p1: βœ‚οΈ  and p2: βœ‚οΈ  => Tie.

Important

switch only works with integers. You cannot use it with arrays like:

switch (player_tuple) {
  case {ROCK, SCISSORS}:
    ... 
}

Moving on to πŸͺ¨βœ‚οΈπŸ—’οΈπŸ¦ŽπŸ––#

In our problem we have five different items. This corresponds to 5 * 5 - 5 + 1 = 21 if-else if-else lines. This is too long. A better idea is to analytically describe the win-lose table:

https://upload.wikimedia.org/wikipedia/commons/a/ad/Pierre_ciseaux_feuille_l%C3%A9zard_spock_aligned.svg

Fig. 14 rock paper scissors lizard Spock resolution diagram
CC BY-SA 3.0. By DMacks (talk). Source: Wikimedia Commons
#

Let us try to extract a pattern from the resolution diagram in Fig. 14. Assume that clock-wise rotation is a forwards direction.

In the diagram we see that Spock wins against scissors and stone, which are +1 and +3 forward in the circle. Spock loses against lizard and paper, which are +2 and +4 forward in the circle. In other words, if the second shape is +1 and +3 away, Spock wins, and if the second shape is +2 and +4 shapes away, then Spock loses.

πŸ€” Question to ponder

Does the rule described above apply to all shapes? Why?

Describing the resolution using the distance between shapes#

If we generalize our rule for all items, we get:

If the second item is +1 or +3 away, then the first item wins. If the second item is +2 or +4 away, then the first item loses.

How can we determine if the second item is +1 or +3 away, i.e the distance? An idea is to attach numbers to all items, for example:

  1. Spock

  2. scissors

  3. paper

  4. rock

  5. lizard

The numbers 0 to 4 become their ids. Now we can determine the distance by subtracting their ids.

Example: rock vs lizard. lizard’s id is 4, and rock’s id is 3, so we get 4-3=1, which means that rock wins against the lizard. The resolution diagram confirms this.

This approach can be summarized as a table, where id1 and id2 stand for the ids of the items.

p2-p1

p1 …

0

tie

1 or 3

wins

2 or 4

loses

πŸ€” Question to ponder

This was too fast. Do you see any problems with this table?

Expanding the resolution table#

What happens if p1’s id is greater than p2’s id? Then we get negative numbers. For example for p1 = 4 and p2 = 0, we get p2 - p1 = -4. 4 means lizard and 0 Spock; and lizard wins against Spock.

We can look at the negative numbers from another perspective. -4 means a counter-clockwise turn, which is the same as if we moved 1 clockwise. So we can extend the table as follows:

p2-p1

p1 …

0

tie

-4 or -2 or 1 or 3

wins

-3 or -1 or 2 or 4

loses

Activity 30 (RPSSL resolution logic using difference between shapes)

Implement the resolution logic above either with

  1. if-else ... or

  2. switch statement.

Two students with different approaches will demonstrate and we will compare.

Use the following template. Your output should be the same as the output below.

#include <stdio.h>
enum { SPOCK, SCISSORS, PAPER, ROCK, LIZARD, SHAPE_COUNT } player1, player2;

const char *SHAPE_STRINGS[] = {"πŸ––", "βœ‚οΈ", "οΈπŸ—’οΈ", "πŸͺ¨", "🦎"};

int main() {

  for (size_t player1 = SPOCK; player1 < SHAPE_COUNT; ++player1) {
    for (size_t player2 = SPOCK; player2 < SHAPE_COUNT; ++player2) {
      printf("If player1: %s  and player2: %s  => ", SHAPE_STRINGS[player1],
             SHAPE_STRINGS[player2]);
      // YOUR CODE HERE
    }
    puts("");
  }
}

If player1: πŸ––  and player2: πŸ––  => Tie.
If player1: πŸ––  and player2: βœ‚οΈ  => Player1 wins.
If player1: πŸ––  and player2: οΈπŸ—’οΈ  => Player1 looses.
If player1: πŸ––  and player2: πŸͺ¨  => Player1 wins.
If player1: πŸ––  and player2: 🦎  => Player1 looses.

If player1: βœ‚οΈ  and player2: πŸ––  => Player1 looses.
If player1: βœ‚οΈ  and player2: βœ‚οΈ  => Tie.
If player1: βœ‚οΈ  and player2: οΈπŸ—’οΈ  => Player1 wins.
If player1: βœ‚οΈ  and player2: πŸͺ¨  => Player1 looses.
If player1: βœ‚οΈ  and player2: 🦎  => Player1 wins.

If player1: οΈπŸ—’οΈ  and player2: πŸ––  => Player1 wins.
If player1: οΈπŸ—’οΈ  and player2: βœ‚οΈ  => Player1 looses.
If player1: οΈπŸ—’οΈ  and player2: οΈπŸ—’οΈ  => Tie.
If player1: οΈπŸ—’οΈ  and player2: πŸͺ¨  => Player1 wins.
If player1: οΈπŸ—’οΈ  and player2: 🦎  => Player1 looses.

If player1: πŸͺ¨  and player2: πŸ––  => Player1 looses.
If player1: πŸͺ¨  and player2: βœ‚οΈ  => Player1 wins.
If player1: πŸͺ¨  and player2: οΈπŸ—’οΈ  => Player1 looses.
If player1: πŸͺ¨  and player2: πŸͺ¨  => Tie.
If player1: πŸͺ¨  and player2: 🦎  => Player1 wins.

If player1: 🦎  and player2: πŸ––  => Player1 wins.
If player1: 🦎  and player2: βœ‚οΈ  => Player1 looses.
If player1: 🦎  and player2: οΈπŸ—’οΈ  => Player1 wins.
If player1: 🦎  and player2: πŸͺ¨  => Player1 looses.
If player1: 🦎  and player2: 🦎  => Tie.

Appendix#