16
\$\begingroup\$

The "sequential" game

A number of people stand in a line with their hands hidden in a pre-chosen state of either Rock ✊, Paper ✋ or Scissors ✌️. Here's an example line:

Initial Line
✌️ ✋ ✊ ✋ ✊ ✌️ ✌️ ✊ ✌️ ✋ ✊

The game goes like this: at each turn, the leftmost player "challenges" the next one: the one who wins the Rock Paper Scissors challenge stays in the line (and becomes/remains the leftmost player), the other is eliminated. Tying equals to a win for the challenger (or for the challenged, it doesn't really matter).

This repeats until only one player is left, who wins the game.

Let's see how this unfolds, while keeping track of the eliminated players (in order of elimination).

Current challenge Eliminated players Updated line
✌️ ✋ ✊ ✋ ✊ ✌️ ✌️ ✊ ✌️ ✋ ✊ (start)
✌️>✋ +✋ ✌️ ✊ ✋ ✊ ✌️ ✌️ ✊ ✌️ ✋ ✊
✌️<✊ ✋ +✌️ ✊ ✋ ✊ ✌️ ✌️ ✊ ✌️ ✋ ✊
✊<✋ ✋✌️ +✊ ✋ ✊ ✌️ ✌️ ✊ ✌️ ✋ ✊
✋>✊ ✋✌️✊ +✊ ✋ ✌️ ✌️ ✊ ✌️ ✋ ✊
✋<✌️ ✋✌️✊✊ +✋ ✌️ ✌️ ✊ ✌️ ✋ ✊
✌️=✌️ ✋✌️✊✊✋ +✌️ ✌️ ✊ ✌️ ✋ ✊
✌️<✊ ✋✌️✊✊✋✌️ +✌️ ✊ ✌️ ✋ ✊
✊>✌️ ✋✌️✊✊✋✌️✌️ +✌️ ✊ ✋ ✊
✊<✋ ✋✌️✊✊✋✌️✌️✌️ +✊ ✋ ✊
✋>✊ ✋✌️✊✊✋✌️✌️✌️✊ +✊ ✋ (winner!)

Your challenge

Your challenge is to write the shortest code (function or entire program) that

  • Takes as input a line formed of at least two players.

  • Outputs

    • the winner, and
    • the sequence of all other players in order (or reverse order) of elimination
  • Input and output can be in any of the commonly accepted formats (standard input/output, function arguments, return values, printouts, file I/O, ...).

    The two outputs can be in any order, as long as it's consistent. They can also be clumped together (just define upfront if the winner is the first or last element)

  • Choose your own representation of Rock, Paper and Scissors. It can be strings of "R", "P", "S", sequences of these characters, strings of emojis, arrays of integers (0, 1, 2) or (-1, 0, 1), ...

Examples

 PP                    ->  P, P
 RS                    ->  R, S
 RSPPR                 ->  P, SRPR
 PSPPRSSP              ->  P, PPPSSSR
 RSPPRSRPSR            ->  R, SRPRPSRPS
 SPRPRSSRSPR           ->  P, PSRRPSSSRR   # (this is the one from the above breakdown)
 PSSPSRRRRSP           ->  P, PSPSSRRRSR
 RSPRRPPRPRRS          ->  S, SRRRPPRPRRP
 PRPSSRPRRSRPRR        ->  P, RPPSSRRRPSRRR
 RSRRPRSRPRSRPSSR      ->  R, SRRRRPSRRPSRPSS
 SPSSRRSSRSSSRPSSS     ->  S, PSSSRSSRSSSRRPSS
 SPSSPPPRSSPRPSSPSPP   ->  S, PSSPPPSSSRRPPSPSPP

( Reverse order is also acceptable
  as long as it's consistent:
 SPSSPPPRSSPRPSSPSPP   ->  S, PPSPSPPRRSSSPPPSSP
 SPSSPPPRSSPRPSSPSPP   ->  PSSPPPSSSRRPPSPSPP, S )
 SPSSPPPRSSPRPSSPSPP   ->  PPSPSPPRRSSSPPPSSP, S )

```
\$\endgroup\$
2
  • \$\begingroup\$ Can we have the input as three distinct floats, as long as it's consistent, say S = 1, P = 0.25, R = 1.75? \$\endgroup\$
    – Tbw
    Commented Apr 20 at 19:27
  • \$\begingroup\$ @Tbw yes, any three distinct constants \$\endgroup\$
    – Nicola Sap
    Commented Apr 20 at 19:34

16 Answers 16

9
\$\begingroup\$

Uiua 0.11.0, 1211 bytes SBCS

/(&p⍥:⁅÷,,)

Try on Uiua Pad!

Takes a list of 1 for rock, 2 for paper, and 3 for scissors, prints the losers sequentially, and outputs the winner on the stack.

Explanation

/(&p⍥:⁅÷,,)
/(        ) # fold the list by...
        ,,  # duplicate a and b on stack
      ⁅÷    # round(a / b)
    ⍥:      # flip the stack that many times
  &p        # print the loser, continue with winner

This works because round(a / b) is even only when (a,b) == (3,2), (2,1), or (1,3)

\$\endgroup\$
5
\$\begingroup\$

Desmos, 102 101 bytes

-1 bytes thanks to @fireflame241!!!

a=l[1]
b=l[2]
k=mod((a-b)^2-a-b,3)
L=l.count
f(l)=\join(\{L>1:f(\join(a+b-k,l[3...])),l\}[1...L-1],k)

0 is rock, 1 is paper, 2 is scissors

Outputs the winner, then the eliminated players in reverse order.

Try It On Desmos!

Try It On Desmos! - Prettified

WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO DESMOS RECURSION IS OUT OMFG!!!!!!!!! FIRST DESMOS ANSWER USING RECURSION

(for reference, the recursion feature just dropped about an hour ago)

This can most definitely be golfed but I just wanted to get an answer out there, this is sooooooo exciting!!!

See relevant article: Recursion

Port of xnor's Haskell answer so make sure to upvote that too!

\$\endgroup\$
3
  • \$\begingroup\$ -1 by .count instead of .length. And you may consider something like [b,a][mod(a-b,3)] for k (not sure if it ends up shorter: desmos.com/calculator/l6m8ya4xz1) \$\endgroup\$ Commented Apr 22 at 21:48
  • \$\begingroup\$ @fireflame241 oh yea count, why i keep forgetting about that bruh \$\endgroup\$
    – Aiden Chow
    Commented Apr 22 at 22:08
  • \$\begingroup\$ @fireflame241 doesn't seem to work for when a=b :( \$\endgroup\$
    – Aiden Chow
    Commented Apr 23 at 19:28
3
\$\begingroup\$

Python 2, 77 bytes

a=lambda s,l=[]:len(s)<2and(s,l)or a(s,l+[s.pop(s[:2]in[[1,0],[2,1],[0,2]])])

Try it Online! Uses Rock = 0, Paper = 1, Scissors = 2 and takes a list of integers as input such as [0, 2, 1, 1, 0].

I can't resist a question with some recursion possibilities. Un-golfed version:

def a(s, l=[]):
    if len(s) < 2:
        return s, l
    else:
        return a(s, l + [seq.pop(s[:2] in [[1,0],[2,1],[0,2]])])

Until the sequence s has only 1 item left, add the loser (seq.pop(s[:2] in [[1,0],[2,1],[0,2])) to the losers l.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 60 bytes: a=lambda s,*t:s[1:]and a(s,s.pop((s[0]-s[1])%3%2),*t)or(s,t), outputting ([winner], (last_elimination ... first_elimination)). \$\endgroup\$ Commented Apr 21 at 23:31
3
\$\begingroup\$

Haskell, 48 bytes

f(a:b:r)|k<-mod((a-b)^2-a-b)3=k:f(a+b-k:r)
f l=l

Try it online!

Outputs the throws eliminated in order with the last element being the winner, since the challenge allows for the two outputs to be be "clumped together (just define upfront if the winner is the first or last element)".

We treat the three throws as \$\{0,1,2\}\$, and working modulo 3, evaluate the loser of two throws (the losing throw, not the index) as the quadratic polynomial:

$$\text{loser} \equiv (a-b)^2-a-b \thinspace$$

We could similarly evaluate

$$\text{winner} \equiv - (a-b)^2-a-b,$$

but it's shorter to compute the winner from the loser using that $$\text{winner} + \text{loser} = a+b$$ since the winner and loser are \$a\$ and \$b\$ in some order.

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

Haskell, 69 bytes

o![a]=(a,o)
o!(a:b:r)|mod(a-b)3>1=(a:o)!(b:r)|1<2=(b:o)!(a:r)
f=([]!)

Attempt This Online!

Uses a list of integers as input and output, with rock=1, paper=2 and scissors=3.

There probably is a fancy way to solve this with combinators, but everything I've tried with fold and scan was longer.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ mod(a-b)3 is shorter \$\endgroup\$
    – xnor
    Commented Apr 21 at 21:36
2
\$\begingroup\$

Charcoal, 22 bytes

FS¿⁼ω§RPS⌕SRιι«ω≔ιω»,ω

Try it online! Link is to verbose version of code. Outputs the updated line and winner (here separated by a comma, although that wasn't strictly necessary, so please don't tell me how to save a byte). Explanation:

FS

Loop through the characters of the input string.

¿⁼ω§RPS⌕SRι

If the previous winner beats the next player (for the first player there is no previous winner so it always loses to the first player), then...

ι

... output the next player, otherwise...

«ω≔ιω»

... output the previous winner and set the next player as the previous winner.

Output the final winner.

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

JavaScript (Node.js), 55 48 bytes

-7b thanks to @Arnauld's math wizardry

f=(a,b,...A)=>b?[q=19/(b^=a)&3||a]+f(q^b,...A):a

Try it online!

Takes input as integer arguments, like f(3,1,2,2,3) for RSPPR, with 1=Scissors, 2=Paper, 3=Rock.

Outputs a string of the same integers, with the winner at the end and losers in order from left to right.

Old Answer, 67 bytes

f=(a,b,...A)=>b?(e=(/RS|SP|PR/.exec(a+b+a)||a)+a)[1]+f(e[0],...A):a

Try it online!

Takes input as one of R, P, or S per argument, like f("R", "S"), which can be done easily in JavaScript with the spread operator: f(..."RS").

Outputs a single string consisting of R/P/Ss where the winner is the last character of the string, and the losers are in order from left to right.

Recursively builds the string of losers while passing on the winner to the next iteration, eventually adding the winner to the end. Determines winner/loser by turning, for example, PS into PSP or RS into RSR, then checking for the occurrence of winner before loser, resulting in SP and RS; for ties, just makes a string like RR since both winner and loser are the same.

\$\endgroup\$
4
  • \$\begingroup\$ You can get rid of i and save a byte by doing b^=a. \$\endgroup\$
    – Arnauld
    Commented Apr 20 at 20:03
  • \$\begingroup\$ 49 bytes \$\endgroup\$
    – Arnauld
    Commented Apr 20 at 20:12
  • 1
    \$\begingroup\$ 48 bytes \$\endgroup\$
    – Arnauld
    Commented Apr 20 at 20:26
  • \$\begingroup\$ @Arnauld Good catch on reusing b. And I figured there'd be a mathematical way to get the winner/loser but didn't think it would just be another XOR, good find! I'm thinking the 48b solution is different enough for its own answer though, but I'm unsure of the norm. \$\endgroup\$ Commented Apr 20 at 20:31
2
\$\begingroup\$

Thue, 184 182 bytes

-2 bytes thanks to @DLosc!!!

I::=:::
|RP::=R|P
|PR::=R|P
|RS::=S|R
|SR::=S|R
|PS::=P|S
|SP::=P|S
|RR::=R|R
|PP::=P|P
|SS::=S|S
|RX::=R
|PX::=P
|SX::=S
pR::=pTR
pP::=pTP
pS::=pTS
TR::=~R
TP::=~P
TS::=~S
::=
p|IX

R is rock, P is paper, and S is scissors.

Input has to have a newline at the end, idk why.

Outputs the eliminated players, then the winner as the last element.

Try it online!

This is my first ever Thue answer so I’m sure there are many bytes to be saved here!

Also idk why, but the code needs to end in a newline as well, removing either the newline in the code or the newline in the input will break it.

Here is the same code as above but modified to add a separation between the winner and the eliminated players:

204 202 bytes

I::=:::
|RP::=R|P
|PR::=R|P
|RS::=S|R
|SR::=S|R
|PS::=P|S
|SP::=P|S
|RR::=R|R
|PP::=P|P
|SS::=S|S
|RX::=-R
|PX::=-P
|SX::=-S
pR::=pTR
pP::=pTP
pS::=pTS
p-::=pT-
TR::=~R
TP::=~P
TS::=~S
T-::=~-
::=
p|IX

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Nice! You can save 2 bytes by outputting in the opposite order. \$\endgroup\$
    – DLosc
    Commented Apr 22 at 4:00
  • \$\begingroup\$ @DLosc Oh damn that's big brain stuff \$\endgroup\$
    – Aiden Chow
    Commented Apr 22 at 7:01
2
\$\begingroup\$

R, 128 123 bytes

Minus 5 bytes thanks to pajonk

\(v,L={},`+`=paste,`/`=c){for(i in 2:sum(v|1)){v=((w=max((a=v[1]),(b=v[2]),na.rm=T)&max(a,b))/v[-1:-2]);L=L/sub(+w,"",a+b)};v/L}

Original output as booleans

Version with a post-conversion

All three states are coded as a boolean value each: PAPER == TRUE, ROCK == FALSE, SCISSORS == NA.

The function max(a,b)&max(a,b,na.rm=T) returns:

INPUT       OUTPUT
--------------------
TRUE, NA    -> NA
FALSE, NA   -> FALSE
FALSE, TRUE -> TRUE

Also, when a==b, the same value is returned.

Finding the loser was tricky because of the notoriously elusive nature of NA (1==NA and NA==NA both give NA!). The solution was to convert the values to the string format and remove the winner.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ Some minor golfs for -5 bytes. \$\endgroup\$
    – pajonk
    Commented Apr 24 at 9:58
  • 2
    \$\begingroup\$ @pajonk thanks for the improvement! \$\endgroup\$ Commented Apr 25 at 8:26
1
\$\begingroup\$

APL+WIN, 95 bytes

Prompts for string consisting of characters R P S:

SP←RS←PR←2⋄PS←SR←RP←RR←PP←SS←1⋄x←0⍴⍬⋄y←⎕⋄⍎∊(¯1+⍴y)⍴⊂'n←⍎m←2↑y⋄x←x,m[n]⋄y←m[1 2~n],2↓y⋄'⋄y,',',x

Try it online! Thanks to Dyalog Classic

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

Retina 0.8.2, 36 bytes

^
,
{O^`(?<=,.?).
,SP
,PS
,(.)\B
$1,

Try it online! Link includes test cases. Outputs the updated line and the winner separated by a comma. Explanation:

^
,

Start at the beginning of the line.

{`

Repeat until all matches have been played.

O^`(?<=,.?).

Assume Paper beats Rock beats Scissors.

,SP
,PS

But actually Scissors beats Paper.

,(.)\B
$1,

Advance the comma past the loser.

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

05AB1E, 15 13 bytes

ćsvDysy/òƒs]ðsJ

Port of @Tbw's Uiua's answer, so make sure to upvote that answer as well!
-2 bytes (thanks to @Tbw) mentioning a rule I missed ("They can also be clumped together (just define upfront if the winner is the first or last element)")

Input as an integer, with digits 1 for Rock; 2 for Paper; 3 for Scissors.
Output as a clumped together string, where the last digit is the winner.

Try it online or verify all test cases.

Explanation:

ć           # Extract the head of the (implicit) input-integer;
            # push its remainder and first digit separately to the stack
 s          # Swap so the remainder is at the top
  v         # Pop and loop over its digits `y`:
   D        #  Duplicate the current top of the stack
    y       #  Push loop-digit `y`
     s      #  Swap so the duplicate it at the top
      y     #  Push another `y`
       /    #  Pop the top two, and divide them
        ò   #  (Banker's) round it to an integer
         ƒ  #  Loop that + 1 amount of times:
          s #   Swap the top two values
  ]         # Close both loops
   J        # Join everything on the stack together
            # (after which this string is output implicitly as result)
\$\endgroup\$
2
  • 1
    \$\begingroup\$ You don't need the ðs. The two outputs are allowed to be clumped together. \$\endgroup\$
    – Tbw
    Commented Apr 22 at 14:08
  • \$\begingroup\$ @Tbw I missed that rule, thanks. :) \$\endgroup\$ Commented Apr 22 at 14:26
0
\$\begingroup\$

Ruby, 44 bytes

->a,*s{[s.map{|x|a+x-a=[a,x,a][(x-a)%3]},a]}

Try it online!

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

Perl 5 -pl, 65 bytes

say$&=~s/$+//r while s/^((.)\2|R(P)|(P)R|S(R)|(R)S|(S)P|P(S))/$+/

Try it online!

Output is in order of elimination with the winner being at the end.

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

Scala 3, 203 bytes

203 bytes, it can be golfed more.


Uses Rock = 0, Paper = 1, Scissors = 2 and takes a list of integers as input such as [0, 2, 1, 1, 0]


Golfed version. Attempt This Online!

def a(s:List[Int],x:List[Int]=Nil):(List[Int],List[Int])={if(s.size<2)(s,x)else{val(z,e)=if(List(List(1,0),List(2,1),List(0,2)).contains(s.take(2))){(s(0)::s.drop(2),s(1))}else{(s.tail,s(0))};a(z,x:+e)}}

Ungolfed version. Attempt This Online!

object Main {
  def main(args: Array[String]): Unit = {
    println(processList(List(1, 1)))
    println(processList(List(0, 2)))
    println(processList(List(0, 2, 1, 1, 0)))
    println(processList(List(1, 2, 1, 1, 0, 2, 2, 1)))
    println(processList(List(0, 2, 1, 1, 0, 2, 0, 1, 2, 0)))
  }

  def processList(s: List[Int], l: List[Int] = Nil): (List[Int], List[Int]) = {
    if (s.length < 2) (s, l)
    else {
      val (newS, ele) = if (List(List(1, 0), List(2, 1), List(0, 2)).contains(s.take(2))) {
        (s.head :: s.drop(2), s(1))
      } else {
        (s.tail, s.head)
      }
      processList(newS, l :+ ele)
    }
  }
}

Algorithm in Python:

# Rock = 0, Paper = 1, Scissors = 2

def a(s, l=[]):
    if len(s) < 2:
        return s, l
    else:
        if s[:2] in [[1,0],[2,1],[0,2]]:
            ele=s.pop(1)
        else:
            ele=s.pop(0)
        return a(s, l + [ele])
\$\endgroup\$
0
\$\begingroup\$

Python 3.8 (pre-release), 66 bytes

f=lambda a,b,*c:[l:=((a-b)**2-a-b)%3]+(c and f(a+b-l,*c)or[a+b-l])

Try it online!

Port of xnor's Haskell answer

0, 1 and 2 for R, P and S, respectively. Winner is output last.

\$\endgroup\$

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