12
\$\begingroup\$

RPS 25 is a version of Rock Paper Scissors which has 25 hand symbols instead of just 3. Each symbol defeats 12 symbols, and is defeated by 12 others. Here's a link to a chart showing which symbols defeat which.

The challenge here is simple: your program should take in two strings representing the symbols thrown by each of the players, and output which player wins. You can do this in multiple ways:

  • Outputting one of three distinct symbols, one indicating the first input wins and one indicating the second input wins, and one if there's a tie
  • Outputting one of 25 distinct symbols indicating which hand symbol wins, outputting either one if there's a tie (since there only is a tie if both players play the same symbol)

The strings can be all lowercase, ALL UPPERCASE, or Title Case.

The following describes all of the possible outcomes; each symbol is followed by a colon, then a list of all of the symbols which it defeats.

GUN: ROCK SUN FIRE SCISSORS AXE SNAKE MONKEY WOMAN MAN TREE COCKROACH WOLF
DRAGON: DEVIL LIGHTNING NUKE DYNAMITE GUN ROCK SUN FIRE SCISSORS AXE SNAKE MONKEY
MOON: AIR BOWL WATER ALIEN DRAGON DEVIL LIGHTNING NUKE DYNAMITE GUN ROCK SUN
TREE: COCKROACH WOLF SPONGE PAPER MOON AIR BOWL WATER ALIEN DRAGON DEVIL LIGHTNING
AXE: SNAKE MONKEY WOMAN MAN TREE COCKROACH WOLF SPONGE PAPER MOON AIR BOWL
DYNAMITE: GUN ROCK SUN FIRE SCISSORS AXE SNAKE MONKEY WOMAN MAN TREE COCKROACH
ALIEN: DRAGON DEVIL LIGHTNING NUKE DYNAMITE GUN ROCK SUN FIRE SCISSORS AXE SNAKE
PAPER: MOON AIR BOWL WATER ALIEN DRAGON DEVIL LIGHTNING NUKE DYNAMITE GUN ROCK
MAN: TREE COCKROACH WOLF SPONGE PAPER MOON AIR BOWL WATER ALIEN DRAGON DEVIL
SCISSORS: AXE SNAKE MONKEY WOMAN MAN TREE COCKROACH WOLF SPONGE PAPER MOON AIR
NUKE: DYNAMITE GUN ROCK SUN FIRE SCISSORS SNAKE AXE MONKEY WOMAN MAN TREE
WATER: ALIEN DRAGON DEVIL LIGHTNING NUKE DYNAMITE GUN ROCK SUN FIRE SCISSORS AXE
SPONGE: PAPER MOON AIR BOWL WATER ALIEN DRAGON DEVIL LIGHTNING NUKE DYNAMITE GUN
WOMAN: MAN TREE COCKROACH WOLF SPONGE PAPER MOON AIR BOWL WATER ALIEN DRAGON
LIGHTNING: NUKE DYNAMITE GUN ROCK SUN FIRE SCISSORS AXE SNAKE MONKEY WOMAN MAN
BOWL: WATER ALIEN DRAGON DEVIL LIGHTNING NUKE DYNAMITE GUN ROCK SUN FIRE SCISSORS
WOLF: SPONGE PAPER MOON AIR BOWL WATER ALIEN DRAGON DEVIL LIGHTNING NUKE DYNAMITE
MONKEY: WOMAN MAN TREE COCKROACH WOLF SPONGE PAPER MOON AIR BOWL WATER ALIEN
SUN: FIRE SCISSORS AXE SNAKE MONKEY WOMAN MAN TREE COCKROACH WOLF SPONGE PAPER
DEVIL: LIGHTNING NUKE DYNAMITE GUN ROCK SUN FIRE SCISSORS AXE SNAKE MONKEY WOMAN
AIR: BOWL WATER ALIEN DRAGON DEVIL LIGHTNING NUKE DYNAMITE GUN ROCK SUN FIRE
COCKROACH: WOLF SPONGE PAPER MOON AIR BOWL WATER ALIEN DRAGON DEVIL LIGHTNING NUKE
SNAKE: MONKEY WOMAN MAN TREE COCKROACH WOLF SPONGE PAPER MOON AIR BOWL WATER
ROCK: SUN FIRE SCISSORS AXE SNAKE MONKEY WOMAN MAN TREE COCKROACH WOLF SPONGE
FIRE: SCISSORS AXE SNAKE MONKEY WOMAN MAN TREE COCKROACH WOLF SPONGE PAPER MOON

Test cases

Player 1 Player 2 Winner Player Winning
Gun Rock Gun 1
Rock Gun Gun 2
Dynamite Gun Dynamite 1
Gun Dynamite Dynamite 2
Nuke Scissors Nuke 1
Paper Sponge Sponge 2
Moon Paper Paper 2
Man Tree Man 1
Gun Gun Gun Neither

Standard loopholes are forbidden. Since this is , the shortest code wins.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ I apologize for the initial errors in the outcome table; it's been fixed now. \$\endgroup\$ Commented Apr 1, 2023 at 18:59

7 Answers 7

12
\$\begingroup\$

Python,  116  94 bytes

lambda a,b:[b,a][(h(b)-h(a))%25%2]
h=lambda s:b"SBOMAK1;R?0=T3%".find(int(s*3,35)%86)

(contains nine unprintable characters).

Try it online!


Previous

lambda a,b:[a,b][(h(b)-h(a))%50<26]
h=lambda s:' DMNNLTDLDOANWRBBAIMMPRSGWWCRTTMAWNMESEAXSSFFSURR'.find((s*2)[:5:4])

An unnamed function that accepts the two throws as uppercase strings and returns the winning throw as a string.

Try it online!

How?

The throws each beat the twelve throws before them in the circular list GUN DYNAMITE NUKE LIGHTNING DEVIL DRAGON ALIEN WATER BOWL AIR MOON PAPER SPONGE WOLF COCKROACH TREE MAN WOMAN MONKEY SNAKE AXE SCISSORS FIRE SUN ROCK.

If we take the first and fifth characters of each of the throws, repeating characters if necessary (e.g. the fifth character of 'GUN' is 'U') we get distinct results: GU DM NN LT DL DO AN WR BB AI MM PR SG WW CR TT MA WN ME SE AX SS FF SU RR and none of these appear more than once in a concatenated version, GUDMNNLTDLDOANWRBBAIMMPRSGWWCRTTMAWNMESEAXSSFFSURR.

The first and "fifth" characters are extracted with (s*2)[:5:4] - that is the throw, s, repeated twice and then sliced starting with (implicit) index 0, stepping by 4 up to, but not including index 5.

The helper function, h, finds the first index at which those characters appear in the concatenated string to identify the location in the circular list. The GU may be replaced with a space as find returns -1 if the input is not found (thus all indices are reduced by two).

The unnamed function calls the helper for each throw, takes the difference, modulos by fifty to deal with the circular nature of the ordering and checks if the second throw won or tied by seeing if that was less than twenty-six.

\$\endgroup\$
8
\$\begingroup\$

JavaScript, 158 bytes

(a,b,v='ngeperoonAirowlteriengonvilingukeiteGunockSunireorsAxeakekeymanManreeacholf',m=v.indexOf(a.slice(-3)),n=v.indexOf(b.slice(-3)))=>n-m+(n>m?0:75)>36?b:a

Try it:

f=(a,b,v='ngeperoonAirowlteriengonvilingukeiteGunockSunireorsAxeakekeymanManreeacholf',m=v.indexOf(a.slice(-3)),n=v.indexOf(b.slice(-3)))=>n-m+(n>m?0:75)>36?b:a

;arr='Sponge Paper Moon Air Bowl Water Alien Dragon Devil Lightning Nuke Dynamite Gun Rock Sun Fire Scissors Axe Snake Monkey Woman Man Tree Cockroach Wolf'.split` `;

for(i=0;i<25;++i)
  for(j=0;j<25;++j)
    document.write(`<p>${arr[i]} vs ${arr[j]} - winner is ${f(arr[i], arr[j])}</p>`)

Explanation

Imagine that we have a round table and all sorts of options are sitting at the table. I have ordered these options so that to the right of each option 12 options are all those options that can be defeated by this option, and to the left 12 options are all those options that can defeat this option:

Sponge Paper Moon Air Bowl Water Alien Dragon Devil Lightning Nuke Dynamite Gun Rock Sun Fire Scissors Axe Snake Monkey Woman Man Tree Cockroach Wolf

And all I need is to check if the second option is to the right of the first option. If yes, then the first one wins; if not, then the second one wins

UPD 252 -> 227

Thanks to Arnauld for the tip to reduce bytes count

UPD 227 -> 195

Based on Neil's tip

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Nice approach! 226 bytes \$\endgroup\$
    – Arnauld
    Commented Apr 1, 2023 at 17:17
  • \$\begingroup\$ @Arnauld Thank you! A very interesting way to create an array of the desired length \$\endgroup\$
    – EzioMercer
    Commented Apr 1, 2023 at 17:57
  • 1
    \$\begingroup\$ I've listed more methods in this post. \$\endgroup\$
    – Arnauld
    Commented Apr 1, 2023 at 18:01
  • 1
    \$\begingroup\$ @Arnauld Actually I used the same approach, but I guess I should have explained it better. \$\endgroup\$
    – Neil
    Commented Apr 1, 2023 at 19:37
6
\$\begingroup\$

Charcoal, 69 67 bytes

⊕÷⊖﹪↨E²⌕”$⌊↘sM²o⌕J⊘bA″⌕‴‹⪫IïΣ↓|‖P¦?S↷Tσ⊞ωºBh✂νDδWcD\`9;1”…S³±¹¦⁷⁵¦³⁶

Try it online! Link is to verbose version of code. Takes input in upper case and outputs - if the first input wins and -- if the second input does, nothing for a tie. Explanation:

      ²                     Literal integer `2`
     E                      Map over implicit range
        ”...”               Compressed lookup table
       ⌕                    Find index of
              S             Next input
             …              Truncated to length
               ³            Literal integer `3`
    ↨           ±¹          Take the difference (literally base `-1`)
   ﹪                        Modulo
                   ⁷⁵       Literal integer `75`
  ⊖                         Decremented
 ÷                          Integer divided by
                      ³⁶    Literal integer `36`
⊕                           Incremented
                            Implicitly output that many `-`s

The compressed string contains the first three letters of each word. The two inputs are then located within that string, and the second index is considered as a cyclic offset from the first index. If this is zero, then the result is a tie; if it is 3 to 36, then the first input wins, otherwise the second input wins. Alternatively, the second input does not lose if it is above 36 and the first input does not lose if it is 36 or below.

Alternatively the two indexes could be considered to be a signed cyclic offset of between -36 and 36 corresponding to an offset of between -12 and 12 in the original list of symbols, in which case the sign of the offset would indicate which symbol is the winner.

Another approach I tried was to make the offset even or odd depending on which symbol wins. This is easier to show with Rock-Paper-Scissors-Lizard-Spock. By placing the symbols in the order Spock-Lizard-Rock-Paper-Scissors, each symbol beats those symbols an even number of places after it, e.g. Rock beats Scissors and Lizard. However this is slightly less golfy to implement in Charcoal. (Note that the symbols can also be placed in the order Spock-Rock-Scissors-Lizard-Paper whereby each symbol beats the two to its right and loses to the two to its left.)

\$\endgroup\$
6
\$\begingroup\$

Python, 234 230 134 127 bytes

lambda a,b,v='SpoRocPapSunMooFirAirSciBowAxeWatSnaAliMonDraWomDevManLigTreNukCocDynWolGun'.find:(a,b)[(v(b[:3])-v(a[:3]))%75%2]

Attempt This Online!

-96 thanks to @Neil!

\$\endgroup\$
4
  • \$\begingroup\$ 160 bytes \$\endgroup\$
    – Neil
    Commented Apr 1, 2023 at 19:36
  • \$\begingroup\$ 135 bytes \$\endgroup\$
    – Neil
    Commented Apr 1, 2023 at 19:56
  • \$\begingroup\$ 134 bytes \$\endgroup\$
    – Neil
    Commented Apr 1, 2023 at 20:09
  • \$\begingroup\$ @Neil thanks, and wow. Somehow Python has beaten JS by 61 bytes now. \$\endgroup\$
    – The Thonnu
    Commented Apr 1, 2023 at 20:12
4
\$\begingroup\$

JavaScript (ES6), 94 bytes

Expects [x,y], using title case.

a=>a[a.map(s=>p="DWgMlktAwSironeRpufyCNTL".indexOf(s[parseInt(s,36)*75%687%4])-p,p=25),p%25%2]

Try it online!

Credits

  • This is based on the observation first made by Neil that the outcome is based on a circular list.
  • The odd/even interleaving trick which allows the final modulo 2 was borrowed from Jonathan Allan's answer.

How?

We use a hash function to isolate one of the first 4 letters1 of each word. This is enough to get 25 distinct symbols (including an undefined one for 'Man'). We then look for the symbols into a lookup string to get the correct positions in the circular list.

 s         | hash(s) | s[hash(s)] | index in lookup
-----------+---------+------------+-----------------
 Man       |    3    |     n/a    |       -1
 Devil     |    0    |     'D'    |        0
 Woman     |    0    |     'W'    |        1
 Dragon    |    3    |     'g'    |        2
 Monkey    |    0    |     'M'    |        3
 Alien     |    1    |     'l'    |        4
 Snake     |    3    |     'k'    |        5
 Water     |    2    |     't'    |        6
 Axe       |    0    |     'A'    |        7
 Bowl      |    2    |     'w'    |        8
 Scissors  |    0    |     'S'    |        9
 Air       |    1    |     'i'    |       10
 Fire      |    2    |     'r'    |       11
 Moon      |    1    |     'o'    |       12
 Sun       |    2    |     'n'    |       13
 Paper     |    3    |     'e'    |       14
 Rock      |    0    |     'R'    |       15
 Sponge    |    1    |     'p'    |       16
 Gun       |    1    |     'u'    |       17
 Wolf      |    3    |     'f'    |       18
 Dynamite  |    1    |     'y'    |       19
 Cockroach |    0    |     'C'    |       20
 Nuke      |    0    |     'N'    |       21
 Tree      |    0    |     'T'    |       22
 Lightning |    0    |     'L'    |       23

1: Using only 3 letters is also possible, for instance with this expression.

\$\endgroup\$
2
  • \$\begingroup\$ Very nice approach with the buffer. \$\endgroup\$
    – code
    Commented Apr 1, 2023 at 17:03
  • \$\begingroup\$ Sorry about the accidental inclusion of the plural forms :/ \$\endgroup\$ Commented Apr 1, 2023 at 19:01
3
\$\begingroup\$

Wolfram Language (Mathematica), 698 bytes

Module[{t,keys},t=<|"air"->"dvbgfjnhipt`","alien"->"gfjnhipt`qcr","axe"->"rlxkuewsomad","bowl"->"vbgfjnhipt`q","cockroach"->"ws`madvbg`jn","devil"->"jnhipt`qc`lx","dragon"->"fj`hipt`qcrl","dynamite"->"ipt`qcrlxkue","gun"->"pt`qcrlxkuew","lightning"->"nhipt`qcrlxk","man"->"uewsomadvbgf","monkey"->"xkuewsomadvb","moon"->"advbgfjnhipt","nuke"->"hipt`qrclxku","paper"->"madvbgfjnhip","rock"->"t`qcrlxkuews","scissors"->"crlxkuewsoma","snake"->"lxkuewsomadv","sponge"->"omadvbgfjnhi","sun"->"`qcrlxkuewso","tree"->"ewsomadvbgfj","water"->"bgfjnhipt`qc","wolf"->"somadvbgfjnh","woman"->"kuewsomadvbg"|>;keys=Keys@t;If[n==e||StringContainsQ[t[n],FromCharacterCode[97+FirstPosition[keys,e][[1]]-1]],n,e]]

Explicit Mathematica code is as follows:

r[n_, e_]:=
Module[{t, keys},
  t = <|"air" -> "dvbgfjnhipt`", "alien" -> "gfjnhipt`qcr", "axe" -> "rlxkuewsomad", "bowl" -> "vbgfjnhipt`q", "cockroach" -> "ws`madvbg`jn", "devil" -> "jnhipt`qc`lx", "dragon" -> "fj`hipt`qcrl", "dynamite" -> "ipt`qcrlxkue", "gun" -> "pt`qcrlxkuew", "lightning" -> "nhipt`qcrlxk", "man" -> "uewsomadvbgf", "monkey" -> "xkuewsomadvb", "moon" -> "advbgfjnhipt", "nuke" -> "hipt`qrclxku", "paper" -> "madvbgfjnhip", "rock" -> "t`qcrlxkuews", "scissors" -> "crlxkuewsoma", "snake" -> "lxkuewsomadv", "sponge" -> "omadvbgfjnhi", "sun" -> "`qcrlxkuewso", "tree" -> "ewsomadvbgfj", "water" -> "bgfjnhipt`qc", "wolf" -> "somadvbgfjnh", "woman" -> "kuewsomadvbg"|>;
  keys = Keys[t];
  If[n == e || StringContainsQ[t[n], FromCharacterCode[97 + FirstPosition[keys, e][[1]] - 1]],
    n,
    e
  ]
]

Try it online!

How to store the game rule in the code with minimum number of bytes?

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

05AB1E, 48 bytes

.•2y§Á°rÇŽGιRjºU¤ÓqtÕ·¢ŸÝ)≠A÷`d•I04SδèJkÆ50%₂@è

Port of @JonathanAllan's (previous) Python answer.

Input as a lowercase pair; outputs the winner (or either for ties) from the input-pair.

Try it online or verify all test cases.

Explanation:

.•2y§Á°rÇŽGιRjºU¤ÓqtÕ·¢ŸÝ)≠A÷`d•
                   # Push compressed string "a dmnnltdldoanwrbbaimmprsgwwcrttmawnmeseaxssffsurr"
  I                # Push the input-pair
   04S             # Push [0,4]
      δ            # Map over the input-pair:
       è           #  Get their (modular) 0-based 0th/first and 4th/fifth characters
        J          # Join each inner pair of characters together
         k         # Get their 0-based indices in the string
          Æ        # Reduce it by subtracting
           50%     # Modulo-50
              ₂@   # Is larger than or equal to 26
                è  # Use that 0 or 1 to 0-based index into the (implicit) input-pair
                   # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand why .•2y§Á°rÇŽGιRjºU¤ÓqtÕ·¢ŸÝ)≠A÷`d• is "a dmnnltdldoanwrbbaimmprsgwwcrttmawnmeseaxssffsurr" (a could be just gu for the same byte-count).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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