Blog Arolla

RPSLS — Rock Paper Scissors Lizard Spock

Let’s start with a tweet from earlier today :

If you follow the link, you’ll get to a Google Play application that allows you to play the (in-)famous RPSLS (Rock-paper-scissors-lizard-Spock) game. Why that tweet stroke me is that I had already played (with code !) around that game a few months ago…

Although this is just a variant of the classical rock-paper-scissors game, the two additional gestures shift the number of gesture combinations to 25, as shown on this matrix:

(image taken from http://en.wikipedia.org/wiki/File:Normal_form_matrix_of_Rock-paper-scissors-lizard-Spock.jpg)

There could be several ways to test these different combinations, for instance using the previous matrix as a reference, but… come on…. a closed set of values, and a simple behaviour… That’s just a perfect candidate for an enum ! And the best part is that I have actually implemented this as a test two months ago (proof on Github !).

Let’s first define the possible game outcomes :

public class GameOutcome : PolymorphicEnum<GameOutcome>
{
    public static GameOutcome WIN = Register();
    public static GameOutcome LOSE = Register();
    public static GameOutcome DRAW = Register();
}

Then we define the Gesture class and its values :

public class GeekGesture : PolymorphicEnum<GeekGesture>
{
    public static GeekGesture ROCK = Register();
    public static GeekGesture PAPER = Register();
    public static GeekGesture SCISSORS = Register();
    public static GeekGesture SPOCK = Register();
    public static GeekGesture LIZARD = Register();
    [...]

The trick here is that I changed the order of the values int the enum from Rock – paper – scissors – lizard – Spock to Rock – paper – scissors – Spock – lizard. This way, each values beats its predecessor and its second successor, and loses against its successor and second predecessor…

The behaviour can then be simply implemented based on the integer values of the enums :

// we can use the int representation and a modulo trick !
public GameOutcome PlayAgainst(GeekGesture other)
{
    return this == other
        ? GameOutcome.DRAW
        : (GameOutcome)(((other + 5 - this) % 5) % 2);
}

Now we can write the tests in a very expressive way :

[TestMethod]
public void gesture_against_2nd_next_is_a_win()
{
    Assert.AreEqual(GameOutcome.WIN,
        GeekGesture.ROCK.PlayAgainst(GeekGesture.SCISSORS));
    Assert.AreEqual(GameOutcome.WIN,
        GeekGesture.PAPER.PlayAgainst(GeekGesture.SPOCK));
    Assert.AreEqual(GameOutcome.WIN,
        GeekGesture.SCISSORS.PlayAgainst(GeekGesture.LIZARD));
    Assert.AreEqual(GameOutcome.WIN,
        GeekGesture.SPOCK.PlayAgainst(GeekGesture.ROCK));
    Assert.AreEqual(GameOutcome.WIN,
        GeekGesture.LIZARD.PlayAgainst(GeekGesture.PAPER));
}

So… here is my answer to Leonard :

Anyone bored and have an android phone? play.google.com/store/apps/det…

@TheRealLeonardH I have and android phone, but when I’m bored I think about software, and sometimes blog about it : pirrmann.net/rpsls-rock-pap…

Site Web | Plus de publications

Contributeur enthousiaste

Comments are closed.