Regex, Paper, Scissors, Lizard, Spock

81

10

Warm up: Regex, Paper, Scissors

This is the challenge I originally wanted to post, before realising that some very short solution exist. Nevertheless, it can be an interesting problem to think about in preparation for the actual challenge below.

Write three regexes R, P and S such that they match each other in a cyclic Rock, Paper, Scissors fashion. In particular, R matches S, S matches P and P matches R, but R doesn't match P, S doesn't match R and P doesn't match S. Here's a handy table:

Regex   Matches   Doesn't match
R       S         P
P       R         S
S       P         R

It doesn't matter what R, P and S do on any other inputs, including themselves.

Here, match just means that some (possibly empty) substring of the input is matched. The match does not need to cover the entire input.

The challenge: Regex, Paper, Scissors, Lizard, Spock

For this challenge, you'll solve a tougher version of the above problem, based on the RPS variant Rock, Paper, Scissors, Lizard, Spock (as popularised by The Big Bang Theory). In RPSLV, there are five different symbols, that beat each other in two cycles:

  • Rock → Scissors → Lizard → Paper → Spock → Rock
  • Rock → Lizard → Spock → Scissors → Paper → Rock

You should write five regexes R, P, S, L and V which mimic this structure when given to each other as input. Here is the corresponding table:

Regex   Matches   Doesn't match
R       L, S      V, P
L       V, P      S, R
V       S, R      P, L
S       P, L      R, V
P       R, V      L, S

Just to be clear, you should not match the string R, P, etc, but the other regexes. E.g. if your regex R is ^\w$ for example, then P and V have to match the string ^\w$, whereas S and L shouldn't.

Again, match just means that at least one (possibly empty) substring of the input is matched. The match does not need to cover the entire input. For example \b (word boundary) matches hello (at the beginning and at the end), but it doesn't match (^,^).

You may use any regex flavour, but please state the choice in your answer and, if possible, provide a link to an online tester for the chosen flavour. You may not use any regex features that let you invoke code in the flavour's host language (like the Perl flavour's e modifier).

Delimiters (like /regex/) are not included in the regex when given as input to another, and you cannot use modifiers that are outside of the regex. Some flavours still let you use modifiers with inline syntax like (?s).

Your score is the sum of the lengths of the five regexes in bytes. Lower is better.

It turns out to be a lot simpler to find a working solution to this problem than it may seem at first, but I hope that finding an optimal solution is quite tricky.

Martin Ender

Posted 2017-07-05T12:50:54.073

Reputation: 184 808

Does, say, R have to match the entire regex of S or a substring of S, provided it does not match any substrings of P or V? – Okx – 2017-07-05T13:00:38.583

@Okx "match just means that at least one (possibly empty) substring of the input is matched. The match does not need to cover the entire input. For example \b (word boundary) matches hello (at the beginning and at the end), but it doesn't match (^,^)." – Martin Ender – 2017-07-05T13:01:05.390

1Presumably it doesn't matter if a regex matches itself? – Brilliand – 2017-07-05T18:57:40.460

@Brilliand Correct. – Martin Ender – 2017-07-05T19:07:08.520

For making this more general, could one just write a set of programs which accept (or reject) each other? – Paŭlo Ebermann – 2017-07-06T19:39:34.380

@MartinEnder I didn't want to generalize the graph, but to generalize from regular expressions to any kind of programs which implement a string → boolean function, in order to allow more "programming languages". – Paŭlo Ebermann – 2017-07-06T21:32:41.227

1

Great puzzle. I've created an interactive version here, if anyone is interested: https://shark.fish/rock-paper-scissors/

– shark.dp – 2017-07-30T11:22:47.347

@shark.dp Wow that's really cool, nice work! – Martin Ender – 2017-07-30T12:40:03.483

Answers

45

PCRE .NET, 35 32 bytes

-3 bytes thanks to Martin Ender

Rock:

([*?]$)

Paper:

[)$]$+

Scissors:

[+?]$.*

Lizard:

[+$]$.?

Spock:

[*)]$

The idea here is to match characters at the end of other regexes which are reserved regex characters, but stop being treated as such when inside a character class.

Business Cat

Posted 2017-07-05T12:50:54.073

Reputation: 8 927

1Okay, you win :P – ETHproductions – 2017-07-05T13:33:18.967

3Sneaky use of putting things after the EOL "$" that become ignored when actively used and matchable when passively used – Stilez – 2017-07-05T13:56:40.113

44

PCRE, 15 14 bytes

Rock:
B

Paper:
\b$

Scissors:
b|B.

Lizard:
\B.

Spock:
^\w

Anders Kaseorg

Posted 2017-07-05T12:50:54.073

Reputation: 29 242

4Very impressive. – Eric Duminil – 2017-07-08T21:16:18.460

Unbeatable! I've been fiddling with Rock = Q (one 14b solution exists with Lizard = \Q\\, then the rest similar to yours) but to absolutely no avail. – jaytea – 2017-10-22T09:44:15.923

40

no fancy features, 35 30 bytes

5 bytes saved by Neil's idea which uses that ] needs no \.

This works for example with the python re module.

R='[SLR]]'
P='[RVP]]'
S='[PLS]]'
L='[PVL]]'
V='[SRV]]'

It searches for a ] preceded by a letter that indicates which rule it is.

Previous version used R='\[[RSL]' etc.

An earlier attempt with score 40 was using R='[SL]x|Rx' etc.

Christian Sievers

Posted 2017-07-05T12:50:54.073

Reputation: 6 366

1Save 5 bytes by reversing everything: R='[LSR]]' etc. – Neil – 2017-07-05T15:24:25.603

@Neil That's a great improvement, thanks! – Christian Sievers – 2017-07-05T17:23:06.970

This works with python's re well, Python should probably be the header then – cat – 2017-07-05T17:27:30.380

1@cat I also wrote "for example", the whole sentence is just to say something concrete that I actually tried. I guess I could say POSIX like some others did, but I think my header is quite correct. – Christian Sievers – 2017-07-05T17:48:49.363

26

PCRE, 20 19

Rock

W

Paper

^\w

Scissors

^\W

Spock

w?\x57$

Lizard

[w]W?

TwiNight

Posted 2017-07-05T12:50:54.073

Reputation: 4 187

21

20 bytes

R = 'R|VP'
L = 'L|RS'
V = 'V|LP'
S = 'S|RV'
P = 'P|LS'

emulbreh

Posted 2017-07-05T12:50:54.073

Reputation: 311

Oh wow, it can be so easy! – Christian Sievers – 2017-07-06T22:31:11.553

That's beautiful. – Eric Duminil – 2017-07-08T21:09:32.120

8

JavaScript, 45 bytes

Another trivial solution.

R:
^R|^.[SL]
P:
^P|^.[RV]
S:
^S|^.[PL]
L:
^L|^.[PV]
V:
^V|^.[SR]

dzaima

Posted 2017-07-05T12:50:54.073

Reputation: 19 048

Oh just realised my answer is a longer/similar version of yours, want me to delete it? – TheLethalCoder – 2017-07-05T13:22:43.617

4@TheLethalCoder All the answers currently are just longer/shorter versions of each other :p – dzaima – 2017-07-05T13:25:40.200

1I suppose haha... – TheLethalCoder – 2017-07-05T13:26:00.177

5

POSIX, 50 45 bytes

Rock
.{5}RP?V?
Paper
.{5}PS?L?
Scissors
.{5}SR?V?
Lizard
.{5}LR?S?
Vulcan (Spock)
.{5}VP?L?

Could be done shorter but the (hide matches after $) trick got used, so I'm looking for another way

The first 5 chars of each string are ignored when matching. So the effective target string simplifies to just X?Y?. None of them have any double letters because "?" is an ordinary char, so the last 4 chars when used as a regex have to match (null string). So the patterns collapse down to "contains 5 chars followed by the target letter": meaning chars 6-9 of the target must contain the target letter (the 5th char in each string)

Update: 35 byte version below, now!

Stilez

Posted 2017-07-05T12:50:54.073

Reputation: 201

I always thought the V isn't actually for Vulcan but to represent the shape of the Vulcan salute (which is the hand gesture you use to represent Spock when playing RPSLV in person). – Martin Ender – 2017-07-05T15:36:38.313

Anyway, welcome to PPCG! Nice first answer. I like that you've inverted the logic compared to all existing answers (matching only one letter and putting the letters beating the current regex into the regex). – Martin Ender – 2017-07-05T15:40:34.503

Does POSIX automatically anchor the match to the beginning of the string? Otherwise, couldn't you drop those commas? – Martin Ender – 2017-07-05T15:41:10.757

I think its POSIX. Could be perc. But yes, nicely spotted! 5 chars less! – Stilez – 2017-07-05T15:43:14.987

And thanks for the welcome. Not that there's many golfs I know enough to tangle with or compete with jelly, but this one was nice! – Stilez – 2017-07-05T15:53:03.363

4There's no need to compete with Jelly. Just pick a language you like and enjoy yourself. :) – Martin Ender – 2017-07-05T15:59:36.397

4

Vanilla RE, 40 characters

Not the most concise or elegant solution but has a pleasing quasi-semantic visual structure!

[^r][sl]
[^p][vr]
[^s][lp]
[^l][pv]
[^v][rs]

Rock beats Scissors or Lizard
Paper beats Vulcan or Rock
Scissors beats Lizard or Paper
Lizard beats Paper or Vulcan
Vulcan beats Rock or Scissors

Chris D'Amato

Posted 2017-07-05T12:50:54.073

Reputation: 41

3

PCRE, 65 bytes

This is a really trivial solution - and not very clever at all - but I'll try to golf it.

V:

(?#V).+[SR]\)

L:

(?#L).+[PV]\)

S:

(?#S).+[PL]\)

P:

(?#P).+[RV]\)

R:

(?#R).+[SL]\)

Essentially, each regex has an 'identifier', in the form of a comment, which tells the other regexes whether it should be matched or not.

Okx

Posted 2017-07-05T12:50:54.073

Reputation: 15 025

3

.NET, 50 bytes

In order they are R, P, S, L, V.

[^R]\^[SL]
[^P]\^[RV]
[^S]\^[PL]
[^L]\^[PV]
[^V]\^[SR]

Works by looking for the identifier group (for example, [^R]) in each of the other expressions.

Changing the expressions to ^R|\^[SL], or similar, seems to work but then it's a bit too similar to @dzaima's answer although it would get it to 45 bytes.

TheLethalCoder

Posted 2017-07-05T12:50:54.073

Reputation: 6 930

2

POSIX, 35 bytes

Rock
R?^[LS]
Paper
P?^[RV]
Scissors
S?^[LP]
Lizard
L?^[PV]
Vulcan (Spock)
V?^[RS]

A completely different way to "hide" behind a start/end symbol, so I feel OK about it :) I'm matching to start because "?" would always have to go between letter and end/$ if done the other way.

10 bytes less than my 1st solution, and conceptually simple which is a bonus I like.

Stilez

Posted 2017-07-05T12:50:54.073

Reputation: 201