DEV Community

Eduardo
Eduardo

Posted on • Edited on • Originally published at eduherminio.github.io

Rock, Paper, Scissors, Lizard, Spock

Introduction

In my very first post we went through solving Rock, Paper, Scissors game using a 'mathematical' approach, and that entry ended with an open question:

Can you find a similar, mathematical approach for Rock, Paper, Scissors, Lizard, Spock?

I invested no time at all in thinking about this variant until a few days ago, when @jotagarciaz asked that same question to me and sparked my curiosity.

Game analysis: "Lizard, Spock" or "Spock, Lizard?"

Rock, Paper, Scissors, Lizard, Spock is an expansion of Rock, Paper, Scissors created by Sam Kass and Karen Bryla.
It was popularized by The Big Bang Theory TV series.

Let's start from the original game:

  • Scissors cuts Paper
  • Paper covers Rock
  • (and as it always has) Rock crushes Scissors

'Rock, Paper, Scissors' arrow diagram

Those traditional Rock, Paper, Scissors rules still apply, but two new options are added to the game: Lizard and Spock.

Let's introduce the simplest rules related to those new options:

  • Rock crushes Lizard
  • Lizard poisons Spock
  • Spock smashes Scissors

'Rock, Paper, Scissors, Lizard, Spock' incomplete arrow diagram with Rock → Lizard, Lizard → Spock and Spock → Scissors arrows (besides traditional Rock, Paper, Scissors arrows)

No huge surprises for now. Let's add another rule:

  • Lizard eats Paper

'Rock, Paper, Scissors, Lizard, Spock' incomplete arrow diagram with Rock → Lizard, Lizard → Spock, Spock → Scissors and Lizard → Paper arrows, with the latter one highlighted (besides traditional Rock, Paper, Scissors arrows)

We can already imagine a possible pattern in the arrow diagram, which gets confirmed when adding the next one:

  • Spock vaporizes Rock

'Rock, Paper, Scissors, Lizard, Spock' incomplete arrow diagram with Rock → Lizard, Lizard → Spock, Spock → Scissors, Lizard → Paper and Spock → Rock arrows, with the latter one highlighted (besides traditional Rock, Paper, Scissors arrows)

  • Scissors decapitates Lizard

'Rock, Paper, Scissors, Lizard, Spock' incomplete arrow diagram with Rock → Lizard, Lizard → Spock, Spock → Scissors and Lizard → Paper, Spock → Rock and Scissors → Lizard arrows, with the latter one highlighted (besides traditional Rock, Paper, Scissors arrows)

  • Paper disproves Spock

'Rock, Paper, Scissors, Lizard, Spock' complete arrow diagram

After going through all the new rules one by one, we can conclude that there's definitely a pattern.

We can also observe such pattern by reflecting those rules in a double entry table, providing we use Rock, Paper, Scissors, Spock, Lizard order (that is, swapping Lizard and Spock positions in the game's name).

Double entry table comparing 'Rock, Paper, Scissors, Spock, Lizard' 'battles'

Shocking, right? The game's most popular name is Rock, Paper, Scissors, Lizard, Spock, as that's how it appears in The Big Bang Theory; but according to the acronyms used in Sam's page, their creators originally named it 'right' (Rock, Paper, Scissors, Spock, Lizard).

I wonder how Doctor Sheldon Lee Cooper missed that!

Solving the game

Once all rules are applied, we end up with this final diagram:

'Rock, Paper, Scissors, Lizard, Spock' complete arrow diagram

Analyzing this figure and/or the table we saw above, we can write the pattern we've identified as:

  • Any given option alternates between winning and losing against the other ones if you evaluate it following this (cyclic) order: Rock, Paper, Scissors, Spock, Lizard.
  • The sequence starts by either losing against the next option in the list (or the first one clockwise in the diagram) or winning against the previous one (or the first one anti/counterclockwise in the diagram).

That's a really solid start, but still not enough. We need some mathematical relationship to be able to transform that into code.

Let's remember what we concluded for Rock, Paper, Scissors:

  • If both numbers are the same, no one wins
  • If both numbers are consecutive, the bigger one wins
  • If both numbers aren’t consecutive, the smaller one wins

We have to find something similar for our pattern, and that's why we added numbers to the diagram above.

Let's write down some examples and try to induct a general rule that satisfies them all:

  • Spock beats Scissors → 4 beats 3 → > number wins
  • Spock beats Rock → 4 beats 1 → > number wins
  • Paper beats Spock → 2 beats 4 → < number wins
  • Lizard beats Spock → 5 beats 4 → > number wins
  • Lizard beats Paper → 5 beats 2 → > number wins
  • Scissors beats Lizard → 3 beats 5 → < number wins
  • Rock beats Lizard → 1 beats 5 → < number wins

By carefully observing the examples, we can induct that:

  • If the difference between both numbers is odd, the bigger one wins
  • If the difference between both numbers is even:
    • If both numbers are the same, no one wins
    • If both numbers are not the same, the smaller one wins

Feel free to take any other example and verify that it satisfies those statements.

If we implement them using C# 8, we get the following function:

int CalculateWinner(int player1, int player2)
{
    return (Math.Abs(player1 - player2) % 2)
        switch
    {
        0 => player1 == player2
            ? -1
            : new[] { player1, player2 }.Min(),
        1 => new[] { player1, player2 }.Max(),
        _ => throw new Exception(@"¯\_(ツ)_/¯")
    };
}
Enter fullscreen mode Exit fullscreen mode

When we compare it with Rock, Paper, Scissors solution, we see that this algorithm is a generalization of the algorithm we used there, exactly how it's supposed to be, given that we're now solving an expansion of that game.

Console application

Here is an example of an interactive console application that plays Rock, Paper, Scissors, Spock, Lizard with us, and which is based on the previously defined algorithm.

Note that this code can also be used for Rock, Paper, Scissors game if we remove Spock and Lizard from enum Item (line 6).

using System;
using System.Linq;

public static class Program
{
    private enum Item { Rock, Paper, Scissors, Spock, Lizard }

    private static readonly string[] ItemArray = Enum.GetNames(typeof(Item));
    private static readonly Random Rnd = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);

    public static void Main()
    {
        while (true)
        {
            Console.WriteLine($"Let's play! Type '{string.Join("', '", ItemArray[..^1])}' or '{ItemArray.Last()}'");

            if (!Enum.TryParse(typeof(Item), Console.ReadLine().Trim('\''), ignoreCase: true, out var humanInput))
            {
                Console.WriteLine("\tComputer says \"no\": invalid input\n");
                continue;
            }

            var computerInput = Enum.Parse(typeof(Item), ItemArray[Rnd.Next(0, ItemArray.Length)]);
            Console.WriteLine($"\tYou've chosen {humanInput}\n\tComputer has chosen {computerInput}");

            var result = CalculateWinner((int)humanInput, (int)computerInput);

            PrintResultMessage((int)humanInput, result);
        }
    }

    private static int CalculateWinner(int player1, int player2)
    {
        return (Math.Abs(player1 - player2) % 2)
            switch
        {
            0 => player1 == player2
                ? -1
                : new[] { player1, player2 }.Min(),
            1 => new[] { player1, player2 }.Max(),
            _ => throw new Exception(@"¯\_(ツ)_/¯")
        };
    }

    private static void PrintResultMessage(int humanInput, int result)
    {
        var resultMessage = result == -1
            ? "It's a draw!"
            : (result == humanInput) ? "You win!" : "You lose!";

        Console.WriteLine($"\t{resultMessage}\n");
    }
}
Enter fullscreen mode Exit fullscreen mode

You can also find this code here.


I hope you've enjoyed this analysis of Rock, Paper, Scissors, Spock, Lizard and/or that you've found the provided solution interesting!

Top comments (0)