16
\$\begingroup\$

Create a function that will take two strings as input and return a single output for the result. Most popular answer wins.

The rules of Rock-paper-scissors-lizard-Spock are:

  • Scissors cut paper
  • Paper covers rock
  • Rock crushes lizard
  • Lizard poisons Spock
  • Spock smashes scissors
  • Scissors decapitate lizard
  • Lizard eats paper
  • Paper disproves Spock
  • Spock vaporizes rock
  • Rock breaks scissors

The output for every possible input case is:

winner('Scissors', 'Paper') -> 'Scissors cut Paper'
winner('Scissors', 'Rock') -> 'Rock breaks Scissors'
winner('Scissors', 'Spock') -> 'Spock smashes Scissors'
winner('Scissors', 'Lizard') -> 'Scissors decapitate Lizard'
winner('Scissors', 'Scissors') -> 'Scissors tie Scissors'
winner('Paper', 'Rock') -> 'Paper covers Rock'
winner('Paper', 'Spock') -> 'Paper disproves Spock'
winner('Paper', 'Lizard') -> 'Lizard eats Paper'
winner('Paper', 'Scissors') -> 'Scissors cut Paper'
winner('Paper', 'Paper') -> 'Paper ties Paper'
winner('Rock', 'Spock') -> 'Spock vaporizes Rock'
winner('Rock', 'Lizard') -> 'Rock crushes Lizard'
winner('Rock', 'Scissors') -> 'Rock breaks Scissors'
winner('Rock', 'Paper') -> 'Paper covers Rock'
winner('Rock', 'Rock') -> 'Rock ties Rock'
winner('Lizard', 'Rock') -> 'Rock crushes Lizard'
winner('Lizard', 'Spock') -> 'Lizard poisons Spock'
winner('Lizard', 'Scissors') -> 'Scissors decapitate Lizard'
winner('Lizard', 'Paper') -> 'Lizard eats Paper'
winner('Lizard', 'Lizard') -> 'Lizard ties Lizard'
winner('Spock', 'Rock') -> 'Spock vaporizes Rock'
winner('Spock', 'Lizard') -> 'Lizard poisons Spock'
winner('Spock', 'Scissors') -> 'Spock smashes Scissors'
winner('Spock', 'Paper') -> 'Paper disproves Spock'
winner('Spock', 'Spock') -> 'Spock ties Spock'

Extra challenge suggested by @Sean Cheshire: Allow custom lists, such as those from this site. With the n-item list, the item loses to the (n-1)/2 previous, and wins over the (n-1)/2 following

\$\endgroup\$

closed as off-topic by Sriotchilism O'Zaic, DJMcMayhem, Taylor Scott, HyperNeutrino, Gryphon Aug 2 '17 at 21:06

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions without an objective primary winning criterion are off-topic, as they make it impossible to indisputably decide which entry should win." – Sriotchilism O'Zaic, DJMcMayhem, Taylor Scott, HyperNeutrino, Gryphon
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 7
    \$\begingroup\$ Creating a 25-element lookup table isn't a challenge, and being popular isn't a code-challenge. \$\endgroup\$ – Peter Taylor Jul 21 '13 at 7:51
  • 6
    \$\begingroup\$ And when I say that being popular isn't a code-challenge: the explanation of that tag begins A code challenge is a competition for creative ways to solve a programming puzzle for an objective criterion other than code size. "Most popular answer wins" is not an objective criterion: you couldn't give the text of two answers to someone and ask them which is the most popular. \$\endgroup\$ – Peter Taylor Jul 21 '13 at 17:16
  • 1
    \$\begingroup\$ @PeterTaylor, dansalmo is right, so long as that lookup table is in a loop: this is a famous theorem of Conway: en.wikipedia.org/wiki/FRACTRAN \$\endgroup\$ – boothby Jul 21 '13 at 19:00
  • 1
    \$\begingroup\$ @dansalmo The challenge you link to was created before the existence of the popularity-contest tag. \$\endgroup\$ – primo Jul 22 '13 at 14:13
  • 1
    \$\begingroup\$ A suggestion to add to the challenge - Allow custom lists, such as those from this site that go up to 101 items. With the n-item list, the item loses to the (n-1)/2 previous, and wins over the (n-1)/2 follwing \$\endgroup\$ – SeanC Jul 22 '13 at 14:36

14 Answers 14

13
\$\begingroup\$

APL

vs←{
    n←'Scissors' 'Paper' 'Rock' 'Lizard' 'Spock'
    x←n⍳⊂⍺ ⋄ y←n⍳⊂⍵ ⋄ X←⍺ ⋄ Y←⍵ ⋄ r←{X,⍵,⊂Y}
    x=y:     r (-x=0)↓'ties'
    y=5|1+x: r x⌷'cut' 'covers' 'crushes' 'poisons' 'smashes'
    y=5|3+x: r x⌷'decapitate' 'disproves' 'breaks' 'eats' 'vaporizes'
    ⍵∇⍺
}

Output exactly as required in all cases, including tie/ties. No lookup table, except for the actual words.

You can try it on http://ngn.github.io/apl/web/

'Spock' vs 'Paper'
Paper  disproves  Spock

APL just knows!

\$\endgroup\$
  • \$\begingroup\$ +1, Never noticed APL until now. Mesmerizing. Your structure is also cool. I like the last line the best. \$\endgroup\$ – dansalmo Jul 25 '13 at 15:33
  • \$\begingroup\$ @dansalmo Thanks :) I like it a lot. And now thanks to github.com/ngn/apl we have an open source and web-ready interpreter (for decades there were only commercial interpreters) \$\endgroup\$ – Tobia Jul 25 '13 at 16:25
  • \$\begingroup\$ @dansalmo btw, APL is a perfect fit for the kind of functional coding you seem to be doing in Python (which I like to do as well) \$\endgroup\$ – Tobia Jul 25 '13 at 20:55
10
\$\begingroup\$

SED

#!/bin/sed
#expects input as 2 words, eg: scissors paper

s/^.*$/\L&/
s/$/;scissors cut paper covers rock crushes lizard poisons spock smashes scissors decapitates lizard eats paper disproves spock vaporizes rock breaks scissors/
t a
:a
s/^\(\w\+\)\s\+\(\w\+\);.*\1 \(\w\+\) \2.*$/\u\1 \3 \u\2/
s/^\(\w\+\)\s\+\(\w\+\);.*\2 \(\w\+\) \1.*$/\u\2 \3 \u\1/
t b
s/^\(\w\+\)\s\+\1;\(\1\?\(s\?\)\).*$/\u\1 tie\3 \u\1/
:b
\$\endgroup\$
  • 1
    \$\begingroup\$ This is... diabolical. \$\endgroup\$ – Wayne Conrad Jan 3 '14 at 19:16
4
\$\begingroup\$

Here is a general solution based on a rule string of any size. It performs correct capitalization for the proper name "Spock" and also allows rules for specifying 'tie' instead of 'ties' for plural objects.

def winner(p1, p2):
    rules = ('scissors cut paper covers rock crushes lizard poisons Spock'
    ' smashes scissors decapitate lizard eats paper disproves Spock vaporizes'
    ' rock breaks scissors tie scissors'.split())

    idxs = sorted(set(i for i, x in enumerate(rules) 
                      if x.lower() in (p1.lower(), p2.lower())))
    idx = [i for i, j in zip(idxs, idxs[1:]) if j-i == 2]
    s=' '.join(rules[idx[0]:idx[0]+3] if idx 
          else (rules[idxs[0]], 'ties', rules[idxs[0]]))
    return s[0].upper()+s[1:]

Results:

>>> winner('spock', 'paper')
'Paper disproves Spock'
>>> winner('spock', 'lizard')
'Lizard poisons Spock'
>>> winner('Paper', 'lizard')
'Lizard eats paper'
>>> winner('Paper', 'Paper')
'Paper ties paper'
>>> winner('scissors',  'scissors')
'Scissors tie scissors'    
\$\endgroup\$
  • \$\begingroup\$ When defining rules you can use a multiline string instead of literal concatenation. This allow you to remove the redundant parentheses. \$\endgroup\$ – Bakuriu Jul 27 '13 at 7:07
3
\$\begingroup\$

Python

class Participant (object):
    def __str__(self): return str(type(self)).split(".")[-1].split("'")[0]
    def is_a(self, cls): return (type(self) is cls)
    def do(self, method, victim): return "%s %ss %s" % (self, method, victim)

class Rock (Participant):
        def fight(self, opponent):
                return (self.do("break", opponent)  if opponent.is_a(Scissors) else
                        self.do("crushe", opponent) if opponent.is_a(Lizard)   else
                        None)

class Paper (Participant):
        def fight(self, opponent):
                return (self.do("cover", opponent)    if opponent.is_a(Rock)  else
                        self.do("disprove", opponent) if opponent.is_a(Spock) else
                        None)

class Scissors (Participant):
        def fight(self, opponent):
                return (self.do("cut", opponent)       if opponent.is_a(Paper)  else
                        self.do("decaitate", opponent) if opponent.is_a(Lizard) else
                        None)

class Lizard (Participant):
        def fight(self, opponent):
                return (self.do("poison", opponent) if opponent.is_a(Spock) else
                        self.do("eat", opponent)    if opponent.is_a(Paper) else
                        None)

class Spock (Participant):
        def fight(self, opponent):
                return (self.do("vaporize", opponent) if opponent.is_a(Rock)     else
                        self.do("smashe", opponent)    if opponent.is_a(Scissors) else
                        None)

def winner(a, b):
    a,b = ( eval(x+"()") for x in (a,b))
    return a.fight(b) or b.fight(a) or a.do("tie", b)
\$\endgroup\$
  • \$\begingroup\$ Scissors are plural, so "cuts" paper and "decaitates" lizard" is wrong (the last one misses a P too) . And "Spock smashs" should be "smashes" ;) \$\endgroup\$ – daniero Jul 21 '13 at 21:50
  • \$\begingroup\$ @daniero, thanks. I noticed the scissors problem, but fixing it complicates things. Fixing "smashes" now. \$\endgroup\$ – ugoren Jul 22 '13 at 8:40
  • \$\begingroup\$ @Daniel "Scissors" is plural. "Scissors" is also singular. See en.wiktionary.org/wiki/scissors \$\endgroup\$ – DavidC Jul 24 '13 at 18:19
  • \$\begingroup\$ So subtle. Love it. \$\endgroup\$ – kaoD Jul 31 '13 at 7:48
2
\$\begingroup\$

Python

def winner(p1, p2):
    actors = ['Paper', 'Scissors', 'Spock', 'Lizard', 'Rock']
    verbs = {'RoLi':'crushes', 'RoSc':'breaks', 'LiSp':'poisons',
             'LiPa':'eats', 'SpSc':'smashes', 'SpRo':'vaporizes', 
             'ScPa':'cut', 'ScLi':'decapitate', 'PaRo':'covers', 
             'PaSp':'disproves', 'ScSc':'tie'}
    p1, p2 = actors.index(p1), actors.index(p2)
    winner, loser = ((p1, p2), (p2, p1))[(1,0,1,0,1)[p1 - p2]]
    return ' '.join([actors[winner],
                     verbs.get(actors[winner][0:2] + actors[loser][0:2],
                               'ties'),
                     actors[loser]])
\$\endgroup\$
  • 1
    \$\begingroup\$ By the way, "looser" is the opposite of "tighter". "Loser" is the opposite of "winner". And it'll save you a few characters in your code. \$\endgroup\$ – Joe Jul 22 '13 at 16:21
2
\$\begingroup\$

Ruby, arithmetic approach

The actors can can be arranged in an array in such a way that each actor a[i] wins against the actors a[i+1] and a[i+2], modulo 5, for instance:

%w(Scissors Lizard Paper Spock Rock)

Then, for an actor A with index i we can see how he matches agains actor B with index j by doing result = (j-i)%5: Result 1 and 2 means that actor A won against an actor 1 or 2 places in front of him respectively; 3 and 4 similarly means he lost against an actor behind him in the array. 0 means a tie. (Note that this may be language dependent; in Ruby (j-i)%5 == (5+j-i)%5 also when j>i.)

The most interesting part of my code is the use of this property to find a sorting function of the indices of two actors. The return value will be -1, 0 or 1 as it should:

winner,loser = [i,j].sort { |x,y| ((y-x)%5+1)/2-1 }

Here's the whole thing:

def battle p1,p2
    who = %w(Scissors Lizard Paper Spock Rock)
    how = %w(cut decapitate poisons eats covers disproves smashes vaporizes crushes breaks)
    i,j = [p1,p2].map { |s| who.find_index s }

    winner,loser = [i,j].sort { |x,y| ((y-x)%5+1)/2-1 }

    method = (winner-loser)%5/2
    what = method == 0 && "ties" || how[winner*2 + method-1]

    return "#{who[winner]} #{what} #{who[loser]}"
end
\$\endgroup\$
2
\$\begingroup\$

Python


  def winner(p,q):
        if p==q:
           return(' '.join([p,'tie',q]))
        d = {'ca':'cut','ao':'covers','oi':'crushes','ip':'poisons','pc': 'smashes','ci':'decapitate','ia':'eats', 'ap':'disproves', 'po':'vaporizes','oc': 'breaks'}
        [a,b] = [p[1],q[1]]
        try:
           return(' '.join([p,d[a+b],q]))
        except KeyError:
           return(' '.join([q,d[b+a],p]))

Using a tricky dictionary.

\$\endgroup\$
  • \$\begingroup\$ Nice one. return(' '.join([p,'tie' + 's'*(p[1]!='c'),q])) will get the verb tense correct. \$\endgroup\$ – dansalmo Nov 25 '13 at 19:27
2
\$\begingroup\$

C#

Assumptions

The opponents are arranged in an n-item array where players beat the (n-1)/2 players ahead of them and lose to the (n-1)/2 players behind them. (With even length lists, the player loses to the ((n-1)/2 + 1) players behind them)

Player actions are arranged in an array where actions within the range of [(indexOfPlayer * (n-1)/2)] to [(indexOfPlayer * (n-1)/2)) + (n-2)/2 - 1].

Additional Info

CircularBuffer<T> is a wrapper around an array to create an "infinitely" addressable array. The IndexOf function returns the index of an item within the actual bounds of the array.

The Class

public class RockPaperScissors<T> where T : IComparable
{
    private CircularBuffer<T> players;
    private CircularBuffer<T> actions;

    private RockPaperScissors() { }

    public RockPaperScissors(T[] opponents, T[] actions)
    {
        this.players = new CircularBuffer<T>(opponents);
        this.actions = new CircularBuffer<T>(actions);
    }

    public string Battle(T a, T b)
    {
        int indexA = players.IndexOf(a);
        int indexB = players.IndexOf(b);

        if (indexA == -1 || indexB == -1)
        {
            return "A dark rift opens in the side of the arena.\n" +
                   "Out of it begins to crawl a creature of such unimaginable\n" +
                   "horror, that the spectators very minds are rendered\n" +
                   "but a mass of gibbering, grey jelly. The horrific creature\n" +
                   "wins the match by virtue of rendering all possible opponents\n" +
                   "completely incapable of conscious thought.";
        }

        int range = (players.Length - 1) / 2;

        if (indexA == indexB)
        {
            return "'Tis a tie!";
        }
        else
        {
            indexB = indexB < indexA ? indexB + players.Length : indexB;
            if (indexA + range < indexB)
            {
                // A Lost
                indexB = indexB >= players.Length ? indexB - players.Length : indexB;
                int actionIndex = indexB * range + (indexA > indexB ? indexA - indexB : (indexA + players.Length) - indexB) - 1;

                return players[indexB] + " " + actions[actionIndex] + " " + players[indexA];
            }
            else
            {
                // A Won
                int actionIndex = indexA * range + (indexB - indexA) - 1;

                return players[indexA] + " " + actions[actionIndex] + " " + players[indexB];
            }
        }
    }
}

Example

string[] players = new string[] { "Scissors", "Lizard", "Paper", "Spock", "Rock" };
string[] actions = new string[] { "decapitates", "cuts", "eats", "poisons", "disproves", "covers", "vaporizes", "smashes", "breaks", "crushes" };

RockPaperScissors<string> rps = new RockPaperScissors<string>(players, actions);

foreach (string player1 in players)
{
    foreach (string player2 in players)
    {
        Console.WriteLine(rps.Battle(player1, player2));
    }
}
Console.ReadKey(true);
\$\endgroup\$
1
\$\begingroup\$

Python, one-liner

winner=lambda a,b:(
    [a+" ties "+b]+
    [x for x in 
        "Scissors cut Paper,Paper covers Rock,Rock crushes Lizard,Lizard poisons Spock,Spock smashes Scissors,Scissors decapitate Lizard,Lizard eats Paper,Paper disproves Spock,Spock vaporizes Rock,Rock break Scissors"
        .split(',') 
     if a in x and b in x])[a!=b]
\$\endgroup\$
  • \$\begingroup\$ Very cool! You can .split(', ') and not have to jam,the,rules,together. \$\endgroup\$ – dansalmo Nov 14 '13 at 2:13
  • \$\begingroup\$ @dansalmo, Thanks, But I see no harm in JammingTheRulesTogether. Though it isn't a golfing contest, I think the shorter the better. \$\endgroup\$ – ugoren Nov 17 '13 at 12:32
1
\$\begingroup\$

Just a small thing I came up with:

echo "winners('Paper', 'Rock')"|sed -r ":a;s/[^ ]*'([[:alpha:]]+)'./\1/;ta;h;s/([[:alpha:]]+) ([[:alpha:]]+)/\2 \1/;G"|awk '{while(getline line<"rules"){split(line,a," ");if(match(a[1],$1)&&match(a[3],$2))print line};close("rules")}' IGNORECASE=1

Here, rules is the file containing all the rules that were given.

\$\endgroup\$
0
\$\begingroup\$

Python

Inspired by @Tobia's APL code.

def winner(p1, p2):
  x,y = map(lambda s:'  scparolisp'.find(s.lower())/2, (p1[:2], p2[:2]))
  v = (' cut covers crushes poisons smashes'.split(' ')[x*(y in (x+1, x-4))] or
       ' decapitate disproves breaks eats vaporizes'.split(' ')[x*(y in (x+3, x-2))])
  return ' '.join((p1.capitalize(), v or 'tie'+'s'*(x!=1), p2)) if v or p1==p2 \
    else winner(p2, p1)

Results:

>>> winner('Spock', 'paper')
'Paper disproves Spock'
>>> winner('Spock', 'lizard')
'Lizard poisons Spock'
>>> winner('paper', 'lizard')
'Lizard eats paper'
>>> winner('paper', 'paper')
'Paper ties paper'
>>> winner('scissors',  'scissors')
'Scissors tie scissors'    
\$\endgroup\$
0
\$\begingroup\$

C++

#include <stdio.h>
#include <string>
#include <map>
using namespace std ;
map<string,int> type = { {"Scissors",0},{"Paper",1},{"Rock",2},{"Lizard",3},{"Spock",4} };
map<pair<int,int>, string> joiner = {
  {{0,1}, " cuts "},{{0,3}, " decapitates "}, {{1,2}, " covers "},{{1,4}, " disproves "},
  {{2,3}, " crushes "},{{2,0}, " crushes "},  {{3,4}, " poisons "},{{3,1}, " eats "},
  {{4,0}, " smashes "},{{4,2}, " vaporizes "},
} ;
// return 0 if first loses, return 1 if 2nd wins
int winner( pair<int,int> p ) {
  return (p.first+1)%5!=p.second && (p.first+3)%5!=p.second ;
}
string winner( string sa, string sb ) {
  pair<int,int> pa = {type[sa],type[sb]};
  int w = winner( pa ) ;
  if( w )  swap(pa.first,pa.second), swap(sa,sb) ;
  return sa+(pa.first==pa.second?" Ties ":joiner[pa])+sb ;
}

A bit of a test

int main(int argc, const char * argv[])
{
  for( pair<const string&, int> a : type )
    for( pair<const string&, int> b : type )
      puts( winner( a.first, b.first ).c_str() ) ;
}
\$\endgroup\$
0
\$\begingroup\$

Javascript

function winner(c1,c2){
    var c = ["Scissors", "Paper", "Rock", "Lizard", "Spock"];
    var method={
        1:["cut", "covers", "crushes", "poisons", "smashes"],
        2:["decapitate", "disproves", "breaks", "eats", "vaporizes"]};
    //Initial hypothesis: first argument wins
    var win = [c.indexOf(c1),c.indexOf(c2)];
    //Check for equality
    var diff = win[0] - win[1];
    if(diff === 0){
        return c1 + ((win[0]===0)?" tie ":" ties ") + c2;
    }
    //If s is -1 we'll swap the order of win[] array
    var s = (diff>0)?1:-1;
    diff = Math.abs(diff);
    if(diff >2){
        diff = 5-diff;
        s= s * -1;
    }
    s=(diff==1)?s*-1:s;
    if(s === -1){
        win = [win[1],win[0]];
    }
    return c[win[0]] + " " + method[diff][win[0]] + " " + c[win[1]];
}
\$\endgroup\$
0
\$\begingroup\$

Javascript

I see this isn't a golfing contest, but I had been fiddling with this puzzle for a while before finding this thread, so here goes.

Here's a (standard) js version in 278 characters:

function winner(a,b){var c={rock:0,paper:1,scissors:2,spock:3,lizard:4},d="crushe,crushe,cover,disprove,cut,decapitate,smashe,vaporize,poison,eat".split(","),i=c[a],j=c[b],I=i==(j+3)%5;return i^j?i==(j+1)%5||I?a+" "+d[i*2+I]+"s "+b:b+" "+d[j*2+(j==(i+3)%5)]+"s "+a:a+" ties "+b}

Or one using E6 features (likely only works in Firefox) in 259 characters:

winner=(a,b,c={rock:0,paper:1,scissors:2,spock:3,lizard:4},d="crushe,crushe,cover,disprove,cut,decapitate,smashe,vaporize,poison,eat".split(","),i=c[a],j=c[b],I=i==(j+3)%5)=>i^j?i==(j+1)%5||I?a+" "+d[i*2+I]+"s "+b:b+" "+d[j*2+(j==(i+3)%5)]+"s "+a:a+" ties "+b
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.