52
\$\begingroup\$

When playing Rock-Paper-Scissors-Lizard-Spock, your robot reflexes let you see your opponent's throw before they make it. You just need to choose a throw that beats theirs, in as few bytes as possible of course.

There's always two winning throws, and either one will work.

RPSLS throws diagram

Input    -> Possible outputs
------------------------------
Rock     -> Spock or Paper
Spock    -> Paper or Lizard
Paper    -> Lizard or Scissors
Lizard   -> Scissors or Rock
Scissors -> Rock or Spock

Take a string as input, and output one of the strings that beats it. Strings must be exactly as given, with capitalization. List of characters may substitute for strings.

\$\endgroup\$
  • 13
    \$\begingroup\$ It's a pity that the two hands that start with S have no outputs in common... \$\endgroup\$ – Jo King Oct 31 at 0:38
  • \$\begingroup\$ How about leading or trailing whitespace? \$\endgroup\$ – Bubbler Oct 31 at 5:56
  • \$\begingroup\$ @Bubbler Nope, sorry. \$\endgroup\$ – xnor Oct 31 at 6:00
  • \$\begingroup\$ @JoKing Hardly! Necessity is the mother of invention, and your solution is pretty clever. \$\endgroup\$ – jpaugh Oct 31 at 15:54
  • 2
    \$\begingroup\$ @JoKing: The second letters of the possible inputs are distinct, though. Possibly useful. \$\endgroup\$ – dan04 Nov 1 at 16:45

28 Answers 28

62
\$\begingroup\$

Perl 6, 36 30 bytes

{<Spock Lizard Rock>[.comb%4]}

Try it online!

Gets each output based on the length of the input modulo 4. I think this is probably the optimal strategy.

\$\endgroup\$
19
\$\begingroup\$

Perl 5 -p, 28 bytes

$_=/k/?Paper:/i/?Rock:Lizard

Try it online!

Tried @JoKing's length mod 4 method, but this turned out to be even shorter.

\$\endgroup\$
  • 1
    \$\begingroup\$ At first glance this looked like sed for me. \$\endgroup\$ – val says Reinstate Monica Oct 31 at 19:39
  • 2
    \$\begingroup\$ @val, Perl was inspired by sed and awk \$\endgroup\$ – ikegami Nov 1 at 5:51
  • 1
    \$\begingroup\$ +1, pretty sure this is optimal unless you can combine output words somehow. \$\endgroup\$ – Please stop being evil Nov 2 at 18:17
13
\$\begingroup\$

PHP, 39 bytes

Shorter md5 variants by Christoph and Benoit Esnard.

<?=[Lizard,Spock,Rock][md5(y.$argn)%3];

Try it online!

<?=[Spock,Rock,Lizard][md5($argn.m)%7];

Try it online!


PHP, 40 bytes

<?=[Rock,Spock,Lizard][md5($argn)[5]%3];

Try it online!

Takes sixth character of md5 of input, which gives unique number of 1 for "Rock" and "Scissors", unique number of 2 for "Spock" and "Paper" and unique number of 9 for "Lizard". A mod 3 on those numbers and we have 0 or 1 or 2 indexes.

| Input    | MD5                              | 6th char | %3 | Output |
|----------|----------------------------------|----------|----|--------|
| Rock     | 4cfbb125e9878528bab91d12421134d8 |        1 |  1 | Spock  |
| Spock    | 5769c28299713c949cd59d5469e40ace |        2 |  2 | Lizard |
| Paper    | d0a662a5235ecde30739fe50cf0de830 |        2 |  2 | Lizard |
| Lizard   | 2f7569e00c4d97aef5f2c2b3a4d2213f |        9 |  0 | Rock   |
| Scissors | 28204140cee6e34a9843b64e5a490b08 |        1 |  1 | Spock  |

PHP, 40 bytes

Alternative 40 bytes, port of Jo King's answer.

<?=[Spock,Lizard,Rock][strlen($argn)%4];

Try it online!


PHP, 41 bytes

This is based on 79037662's answer.

<?=[Lizard,Rock,Paper][ord($argn[-1])%3];

Try it online!


PHP (7.4), 42 bytes

Uses fifth and sixth characters of input to get one of 0, 1 or 2.

fn($s)=>[Rock,Lizard,Paper][!$s[5]+!$s[4]]

Try it online!


PHP, 43 bytes

Uses CRC32 of input and mod to get 0, 1 or 2.

<?=[Rock,Lizard,Paper][crc32($argn)%866%3];

Try it online!


PHP, 43 bytes

Uses second to the last character of input ("Rock" and "Spock" = "c", "Paper" = "e", "Lizard" and "Scissors" = "r").

<?=[c=>Paper,e=>Lizard,r=>Rock][$argn[-2]];

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ 39 by adding some pepper: <?=[Lizard,Spock,Rock][md5(y.$argn)%3];. \$\endgroup\$ – Christoph Oct 31 at 9:14
  • 1
    \$\begingroup\$ 39 bytes: <?=[Spock,Rock,Lizard][md5($argn.m)%7]; - Try it online! \$\endgroup\$ – Benoit Esnard Oct 31 at 9:14
  • 2
    \$\begingroup\$ @BenoitEsnard ha beat you by 5 seconds! :) \$\endgroup\$ – Christoph Oct 31 at 9:15
7
\$\begingroup\$

APL (Dyalog Unicode), 28 bytes

'Spock' 'Lizard' 'Rock'⊃⍨4|≢

Try it online!

Uses the simple strategy based on input length modulo 4 (idea from Jo King's answer). ⎕IO←0.


APL (Dyalog Unicode), 34 bytes

'Lizard' 'Rock' 'Paper'⊃⍨'Xbj'⍸2∘⊃

Try it online!

Uses the shortest choice for all cases. ⎕IO←1.

How it works

'Lizard' 'Rock' 'Paper'⊃⍨'Xbj'⍸2∘⊃
2∘⊃     Take the second character
'Xbj'⍸  Classify it by intervals:
        'X'≤c<'b': 1
        'b'≤c<'j': 2
        'j'≤c    : 3
...⊃⍨   Give the nth string
\$\endgroup\$
4
\$\begingroup\$

Jelly, 16 14 bytes

Lị“ŀ¡ḋ¤:1KỌ»Ḳ¤

Try it online!

-2 bytes thanks to Jonathan Allan and Nick Kennedy both independently coming up with splitting on spaces instead of using a list literal.

A translation of Jo King's Perl 6 solution, using Jelly's compressed strings and modular indexing.

 ị                The element of
  “ŀ¡ḋ¤:1KỌ»      "Lizard Rock  Spock"
            Ḳ¤    split on spaces
 ị                at (1-based) index
L                 length of the input
  “ŀ¡ḋ¤:1KỌ»Ḳ¤    mod 4.
\$\endgroup\$
4
\$\begingroup\$

JavaScript, 40 bytes

s=>['Spock','Lizard','Rock'][s.length%4]

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Runic Enchantments, 42 bytes

il͍4%:0)9*?~"Spock"@1)9*?"Lizard"@"Rock"@

Try it online!

Footer runs all five input possibilities (the y sequences make them print in the correct order, no I'm not sure why the last one needed so many) and presets the stack with an input value (the input command then reads no input) and a newline. Remove the footer to use input instead.

\$\endgroup\$
3
\$\begingroup\$

Python 3, 44 bytes

lambda s:["Spock","Lizard","Rock"][len(s)%4]

Try it online!

Uses the input-length-modulo-4 strategy in Jo King's answer.

\$\endgroup\$
  • \$\begingroup\$ And it’s readable! \$\endgroup\$ – Connor McCormick Nov 1 at 16:38
3
\$\begingroup\$

C (gcc), 51 49 bytes

Thanks to Unrelated String for the suggestion.

The second byte of the string has sufficient uniqueness that I can take its two least-significant bits and use them to then index into a string (with the longest-length result at the end if I rotate the result, fortunately!) The separator between the strings should be a literal NUL character, but I put a space in between them below so that you can see the gaps.

f(char*s){s="Spock Paper Scissors"+(s[1]-3)%4*6;}

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Haskell, 43 bytes

(k!!).length
k="Spock":"Lizard":"Rock":"":k

Try it online!

The mod-4 approach. Longer approaches:

f t|t<"Q"="Scissors"|t<"Sd"="Spock"|1<2="Paper"   (47)
f.last;f 'k'="Paper";f 'r'="Lizard";f _="Rock"    (46)
(["Spock","Lizard","Rock"]!!).(`mod`4).length     (45)
(words"Spock Lizard Rock"!!).(`mod`4).length      (44)
(cycle["Spock","Lizard","Rock",""]!!).length      (44)
(cycle(words"Spock Lizard Rock .")!!).length      (44)
\$\endgroup\$
  • 2
    \$\begingroup\$ Welcome back, Lynn! \$\endgroup\$ – xnor Oct 31 at 23:47
  • 1
    \$\begingroup\$ I tried golfing some tweaks to your code, but it looks like your alternatives have pretty much covered everything. I did get two kind-of weird 45's: 1, 2. \$\endgroup\$ – xnor Nov 2 at 3:09
2
\$\begingroup\$

Python 2, 73 69 bytes

lambda i:l[l.index(i)-1]
l="Rock Lizard Spock Scissors Paper".split()

Try it online!

Since there are already plenty of ports of @JoKing's excellent answer, here is a different way of doing it.

\$\endgroup\$
2
\$\begingroup\$

Retina, 30 27 bytes

'R(`R
Sp
'i(K`Rock
K`Lizard

Try it online! Link includes all possible test cases. @BusinessCat's answer works perfectly well in Retina 0.8.2 but for Retina 1 we can do a little better by using a conditional stage. Explanation:

'R(`

If the input contains R...

R
Sp

... then replace the R with Sp, thus turning Rock into Spock...

'i(

... otherwise if the input contains i...

K`Rock

... then overwrite it with the string Rock, which beats Lizard and Scissors...

K`Lizard

... otherwise overwrite the input with Lizard, which beats Spock and Paper.

\$\endgroup\$
  • \$\begingroup\$ Nice. I still need to learn the new features in Retina 1. The .{6,} is clever though and saves bytes for mine too. \$\endgroup\$ – Business Cat Oct 31 at 13:57
  • \$\begingroup\$ @BusinessCat I'm glad it helped you, as I've switched to an even better Retina 1 method now! \$\endgroup\$ – Neil Oct 31 at 20:44
2
\$\begingroup\$

Ruby, 42 36 bytes

->c{%w(Spock Lizard Rock)[c.size%4]}

Try it online!


Ruby, 43 39 bytes

->c{%w(Lizard Rock Paper)[c[-1].ord%3]}

Try it online!

The second one is an alternative I found to Jo's method, using the ASCII value of the last character, but alas is 3 bytes longer in Ruby.

It is conceivable that my method is better in some language where finding the ASCII value of the last character is shorter than finding the string's length, but I don't know of any.

EDIT: Saved several bytes thanks to @IMP1

\$\endgroup\$
  • \$\begingroup\$ Why not .size instead of .length? \$\endgroup\$ – IMP1 Oct 31 at 14:53
  • \$\begingroup\$ Also, have a look at this. You can use %w(Lizard Rock Paper) instead of ["Lizard","Rock","Paper"] \$\endgroup\$ – IMP1 Oct 31 at 14:55
  • \$\begingroup\$ @IMP1 Thanks for the tips, still new to Ruby. \$\endgroup\$ – 79037662 Oct 31 at 14:55
  • \$\begingroup\$ No worries. Ruby often has multiple methods that do the same thing (like .size and .length), so it's worth looking to see if there's one that's a few characters shorter. \$\endgroup\$ – IMP1 Oct 31 at 14:57
1
\$\begingroup\$

05AB1E, 16 bytes

”‰«†ÕâŸard”#I3öè

Try it online!

\$\endgroup\$
1
\$\begingroup\$

V (vim), 28 bytes

2ORock
Lizard
SpockLÝkYHVGp

Try it online!

Hexdump:

00000000: 324f 526f 636b 0d4c 697a 6172 640d 5370  2ORock.Lizard.Sp
00000010: 6f63 6b1b 4cdd 6b59 4856 4770            ock.L.kYHVGp
\$\endgroup\$
1
\$\begingroup\$

F# (.NET Core), 53 49 46 bytes

 ["Spock";"Lizard";"Rock"].[String.length n%4]

Try it online!

I am using the input-length-modulo-4 strategy from Jo King's answer, so please check his answer.
And I know it's not in competition but I wanted to try something new…

\$\endgroup\$
1
\$\begingroup\$

Japt, 24 bytes

`Spock Lizd Rock`¸gUÊu4

Try it

"Spock Lizard Rock" //=>compressed
.q(S) // split on " "
.g(U.l().u(4)) // return element at index ( length of input % 4 )

Thanks to @Shaggy for the help

\$\endgroup\$
1
\$\begingroup\$

Python 3, 69 64 61 57 bytes

lambda s:[["Scissors","Paper"]['k'in s],"Rock"]['i'in s] 

Not as short as the mod 4 solution, but not a bad alternative.

Try it online!

\$\endgroup\$
  • 2
    \$\begingroup\$ 57 bytes by deleting some spaces \$\endgroup\$ – Stephen Nov 1 at 13:59
  • \$\begingroup\$ Haha, thanks! First code golf, my nasty programmer habits of formatting code are still strong! I also didn't realize you could remove spaces between a string and 'in'. TIL \$\endgroup\$ – Ahndwoo Nov 1 at 14:01
  • 1
    \$\begingroup\$ @Ahndwoo Check out my tip on omitting spaces. tldr: you usually need a space only when letters are followed by letters or digits. \$\endgroup\$ – xnor Nov 1 at 22:50
1
\$\begingroup\$

Pyth, 24 bytes

@c."Lzž›^ºéëEL”‡"\`lz

Try it online!

Port of Jo King's mod-4 answer. Without the compressed string, it's 2 bytes longer: @c"Spock`Lizard`Rock`"\`lz

\$\endgroup\$
1
\$\begingroup\$

Excel, 49 bytes

Mod4 approach:

=CHOOSE(MOD(LEN(A1),4)+1,"Spock","Lizard","Rock")

Switching on last character: 57 bytes

=CHOOSE(MOD(CODE(RIGHT(A1)),3)+1,"Lizard","Rock","Paper")
\$\endgroup\$
1
\$\begingroup\$

R, 52 bytes

function(x)c("Spock","Lizard","Rock")[nchar(x)%%4+1]
\$\endgroup\$
1
\$\begingroup\$

C# dotnet core, 44 bytes

(new[]{"Spock","Lizard","Rock"})[h.Length%4]

Try it online

Uses the input-length-modulo-4 strategy in Jo King's answer.

New contributor
Jorel Fermin is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
  • \$\begingroup\$ You seem to be missing the function declaration part of your answer, i.e. the public static string RPSLS(string h){return ... } part. Taking input as a predefined variable is not an allowed input format, plus you're not actually outputting anything \$\endgroup\$ – Jo King yesterday
  • \$\begingroup\$ This is a snippet where h is implicit. However, here we need a complete program or function. Unfortunately, though, dotnet core is too old to have =>, so a boilerplate will be quite long for this. \$\endgroup\$ – Shieru Asakoto yesterday
1
\$\begingroup\$

Python 2, 50 bytes

lambda s:["Lizard","Rock","Paper"][cmp(s[-2],"e")]

Try it online!

New contributor
Colin Beveridge is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
0
\$\begingroup\$

Wren, 47 bytes

Fn.new{|s|["Spock","Lizard","Rock"][s.count%4]}

Try it online!

Uses the input-length-modulo-4 strategy in Jo King's answer.

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

Retina 0.8.2, 36 33 31 bytes

-3 thanks to Neil

R
s
.{6,}
Rock
.{5}
Lizard
s
Sp

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Can the first line be .[ci].*? \$\endgroup\$ – Neil Oct 31 at 13:35
  • \$\begingroup\$ @Neil Yeah, probably. \$\endgroup\$ – Business Cat Oct 31 at 13:36
  • 1
    \$\begingroup\$ Or not. That makes Rock give SpRock. I guess it would need to be ^.[ci].* \$\endgroup\$ – Business Cat Oct 31 at 13:38
0
\$\begingroup\$

Batch, 87 bytes

@set/ps=
@for %%a in (Rock.d Rock.s Paper.k Lizard.r)do @if %%~xa==.%s:~-1% echo %%~na

Takes input on STDIN. Works by switching on the last character of the input, which saves a case.

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

K, 7 bytes

&/"ki"?

I find this a bit unsatisfying though, since like the Jelly example it requires a dictionary (what they call the "footer") to be supplied as one of the arguments. To be comparable with other languages:

K (not cheating), 26 bytes

`Paper`Rock`Lizard@&/"ki"?
\$\endgroup\$
0
\$\begingroup\$

///, 81 70 bytes

/~/\/\/.//.Sp/.~R/.~ock/Paper~Paper/Lizard~Lizard/Rock~Scissors/Rock/.

Because there is no other way to take input in ///, it goes after the . at the end of the code. For an input of Spock:

/~/\/\/.//.Sp/.~R/.~ock/Paper~Paper/Lizard~Lizard/Rock~Scissors/Rock/.Spock

Try it online!

Test suite

Explanation:

/// works by replacing strings with other strings, within the code.

The first chunk of code, /~/\/\/./, works as a macro replacing ~ with //.. After this, the code expands to:

/.Sp/.//.R/.//.ock/Paper//.Paper/Lizard//.Lizard/Rock//.Scissors/Rock/

/.Sp/./ and /.R/./ change spock and rock to ock, allowing me to just replace ock with Paper.

The rest of the cases are just explicitly handled.

The period before the input is to make sure that none of the code itself is changed, just the input.

\$\endgroup\$

Your Answer

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

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