27
\$\begingroup\$

Introduction

In Tennis, the server alternates serving on the left side or the right side of the court every point, starting on the right. It is possible to figure out which side to serve on for that point just based on the score; if there have been an even number of points you serve on the right, and after an odd number of points, on the left. Scoring in tennis works as follows:

Points | Corresponding Call
       |
0      | Love
1      | 15
2      | 30
3      | 40

Once a player scores 4 points, they win the game. If the score is tied at 40-40, the call is "Deuce" rather than "40-40" or "40 all". At Deuce, the subsequent point is called as "Advantage [x]" or "Ad [x]" where x is the player who scored. If the opposite player scores next, the score returns to Deuce, but if the same player scores again, they win.

The Challenge

Your task is to write a function that takes the score and returns a truthy or falsey value. The input may be a string containing the call or a list containing the scores. Love, Deuce, and Ad must be strings but the rest of calls may be any numerical type as well. Deuce may be represented by singleton list containing "Deuce" or a list with "Deuce" and another value of your choice. You may choose which side corresponds to truthy and falsey, but you must specify which corresponds to which side in your answer. The scores will be separated by a hyphen, except in the case of Deuce wherein it will be simply "Deuce". For advantage, the score will be "Ad-40" or "40-Ad".

Winning

This is code golf, so the score is the number of bytes in your function and the answer with the lowest score wins. Standard loopholes are forbidden.

Test Cases

Love-Love | True
Love-15   | False
Love-30   | True
Love-40   | False
15-Love   | False
15-15     | True
15-30     | False
15-40     | True
30-Love   | True
30-15     | False
30-30     | True
30-40     | False
40-Love   | False
40-15     | True
40-30     | False
Deuce     | True
Ad-40     | False
40-Ad     | False

These test cases are exhaustive, i.e. that list represents every possible input and its corresponding output. I used True for right and False for left.

\$\endgroup\$
  • 3
    \$\begingroup\$ Welcome to the site! This is a nice first question, and is very well specified. For future challenges however, it is recommended to post them in the Sandbox first to get feedback. A couple of questions: we usually allow fairly loose I/O formats, are we allowed to choose our own formats for input, or do we have to stick by the exact format in the question? Furthermore, do we have to output truthy for right and falsey for left, or may we instead choose two distinct consistent values? \$\endgroup\$ – caird coinheringaahing Oct 28 at 17:15
  • 5
    \$\begingroup\$ Thank you! I did not post in the sandbox because of the rep requirement, and I could not see a convenient way to get around it. I restricted the input form because of the variability of the type of the score, but I suppose I could allow other types of input as well. You may also choose which output corresponds to truthey and falsey. I will edit my question to reflect these changes \$\endgroup\$ – EphraimRuttenberg Oct 28 at 17:20
  • 1
    \$\begingroup\$ Didn't we drop the rep requirement for the Sandbox? \$\endgroup\$ – xnor Oct 29 at 0:30
  • 1
    \$\begingroup\$ @xnor From what I can tell, it seems to be a known issue that despite the rep requirement being dropped you need 5 rep to post \$\endgroup\$ – EphraimRuttenberg Oct 29 at 0:46
  • 2
    \$\begingroup\$ @xnor The rep issue on Meta has been raised. It doesn't look like a resolution has been found. \$\endgroup\$ – Dingus Oct 29 at 1:55

17 Answers 17

15
\$\begingroup\$

Retina 0.8.2, 7 bytes

M`4|5
1

Try it online! Link includes test cases. Outputs 0 for right and 1 for left. Explanation:

M`[45]

The number of 4s and 5s...

1

... must equal 1 if the serve is on the left.

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ Good observation! \$\endgroup\$ – Jonathan Allan Oct 28 at 20:31
5
\$\begingroup\$

Perl 5 (-p), 17, 11, 10 bytes

-6 using Neil's solution, -1 thanks to Dom Hastings re-ordering

$_=1^y;45;

Try it online!

0 for false, <>0 for true

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Nice one! You can save an extra byte using ; as the delimiter for y///, re-ordering the operation and accepting 0 for false and any non-zero integer for true: Try it online! \$\endgroup\$ – Dom Hastings Oct 29 at 8:37
4
\$\begingroup\$

Python 2, 26 bytes

Takes input in the same format as the test cases. Output is inverted, 1 for falsey cases and 0 for truthy ones. Normal output would be 1 byte longer with a - preprended.

lambda s:hash(s)*199%421%2

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Inverted output is fine \$\endgroup\$ – EphraimRuttenberg Oct 28 at 18:00
  • \$\begingroup\$ @EphraimRuttenberg thanks for the clarification \$\endgroup\$ – ovs Oct 28 at 18:10
4
\$\begingroup\$

Haskell, 29 bytes

odd.sum.map(mod 882.fromEnum)

Try it online!

An anonymous function taking a hyphenated call and returning True or False, just like in the test cases.

Note: it looks like we're taking values "mod 882", but actually in Haskell mod 882 is the function \$y \mapsto (882 \bmod y)\$. (That is: it's a partially-applied mod x y.)

| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

Haskell, 28 bytes

f s=[1|c<-s,elem c"45"]==[1]

Try it online!

Implement's Neil's method of checking that the string has exactly one 4 or 5. Test suite from Lynn.

Haskell doesn't have a nice way to count elements satisfying a property. We could do filter(`elem`"45"), but then it seems too long to check the resulting list is a single element, or check that it's one of "4" or "5".

We use a list comprehension to make a list with a 1 for each character that's in "45", and check if we end up with the list [1]. Any value could be used in place of 1 here, including s itself.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Ahhh, I totally wrote f s=sum[1|…]==1 and thought "well, there's no shortening that any further…" ^^ \$\endgroup\$ – Lynn Oct 29 at 13:22
2
\$\begingroup\$

Jelly, 6 bytes

OP%25Ḃ

A monadic Link accepting a list of characters which yields 1 if serving from the right or 0 if serving from the left.

Try it online!

How?

OP%25Ḃ - Link: list of characters  e.g. "15-30"
O      - ordinals                       [49,53,45,51,48]
 P     - product                        286085520
   25  - twenty-five                    25
  %    - modulo                         20
     Ḃ - modulo-2                       0

Also 6 bytes using Neil's observation (inverted result):

f⁾45LḂ - Link: list of characters, S
 ⁾45   - list of characters = "45"
f      - filter (S) keep (those characters)
    L  - length -> 0, 1, or 2; but only 1 when serving from the left
     Ḃ - modulo-2
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Raku, 12 bytes

{1-m:g/1|4/}

Try it online!

An anonymous function that returns zero for left and non-zero for the right. This uses Neil's observation that there must be exactly one 1 or 4 for it to be the left side's serve.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Oh, i guess i misread Neil's answer and did 1 or 4 instead lf 4 or 5. It still works, since only 15 contains those digits, though now i wonder if there's some language where one is golfier than the other \$\endgroup\$ – Jo King Oct 29 at 13:53
2
\$\begingroup\$

Pyth, 10 bytes

%%CSz287 2

Try it online!

Explanation, A little different to the others here...

%%CSz287 2
   Sz        - Sort the input string, (so that Love-15 and 15-Love provide the same string)  
  CSz        - Get the 256 base int value of the string.
 % ^ 287     - Modulo that number by 287
%  ^     2   - Return whether that number is odd or even.
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Scala, 30 29 bytes

_.matches("[^45]*[45][^45]*")

Try it online!

My solution uses false for right and true for left.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ [45] should work in place of (4|5) \$\endgroup\$ – Jo King Oct 29 at 13:50
  • \$\begingroup\$ You can save a byte by using infix notation. (btw, whatever happened to the Scala chat room?) \$\endgroup\$ – user Nov 2 at 0:34
2
\$\begingroup\$

JavaScript (ES6), 26 bytes

Using @Neil's method saves 4 more bytes.

Expects a string. Returns 0 for left or 1 for right.

s=>s.split(/4|5/).length%2

Try it online!


JavaScript (ES6), 30 bytes

Expects a pair of string scores (or a singleton for Deuce). Returns 0 for right or 1 for left.

a=>parseInt(a.join`2`,36)%31%2

Try it online!


JavaScript (ES6), 34 bytes

Expects a string. Returns 0 for left or 1 for right.

s=>parseInt(s[0]+s[3]+s[5],35)%3%2

Try it online!

How?

Looking at the 1st and the 4th characters provides enough information, except if the score is Love-X in which case we need to look at the 6th character as well. To make things easier, we just look at these 3 positions in all cases and hash them.

Below is a summary of the process for each possible input.

 input       | s[0]+s[3]+s[5] | parsed as base-35 | mod 3 | mod 2
-------------+----------------+-------------------+-------+-------
 "Love-Love" | "LeL"          |             26236 |   1   |   1
 "Love-15"   | "Le1"          |             26216 |   2   |   0
 "Love-30"   | "Le3"          |             26218 |   1   |   1
 "Love-40"   | "Le4"          |             26219 |   2   |   0
 "15-Love"   | "1Lv"          |              1991 |   2   |   0
 "15-15"     | "11undefined"  |  2906423713803553 |   1   |   1
 "15-30"     | "13undefined"  |  3064054991147303 |   2   |   0
 "15-40"     | "14undefined"  |  3142870629819178 |   1   |   1
 "30-Love"   | "3Lv"          |              4441 |   1   |   1
 "30-15"     | "31undefined"  |  8423518420834803 |   0   |   0
 "30-30"     | "33undefined"  |  8581149698178553 |   1   |   1
 "30-40"     | "34undefined"  |  8659965336850428 |   0   |   0
 "40-Love"   | "4Lv"          |              5666 |   2   |   0
 "40-15"     | "41undefined"  | 11182065774350428 |   1   |   1
 "40-30"     | "43undefined"  | 11339697051694176 |   0   |   0
 "Deuce"     | "Dcundefined"  | 36875963981381680 |   1   |   1
 "Ad-40"     | "A4undefined"  | 27969796811459804 |   2   |   0
 "40-Ad"     | "4Aundefined"  | 11891406522397304 |   2   |   0
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I've edited my question. Could you please specify which output corresponds to which side? \$\endgroup\$ – EphraimRuttenberg Oct 28 at 17:32
  • 1
    \$\begingroup\$ @EphraimRuttenberg Sure. Edited. \$\endgroup\$ – Arnauld Oct 28 at 17:39
  • \$\begingroup\$ For the 26 bytes one, why use bitwise XOR (^) instead of just mod (%)? It seems to work with mod, and it limits it to a 0 or a 1 \$\endgroup\$ – Samathingamajig Nov 1 at 1:32
  • \$\begingroup\$ @Samathingamajig Good point. (And I have no idea why I used XOR here...) \$\endgroup\$ – Arnauld Nov 1 at 9:19
1
\$\begingroup\$

Jelly, 7 bytes

Ṣ“Œọ‘ḥḂ

Try it online! (the footer tests the program on all possible inputs)

Takes input in the form of a list of strings, e.g. ["Love","15"], ["40","Ad"], or ["Deuce"]. Outputs 0 or 1, in the opposite sense from the table of test cases in the question (i.e. 0 = right, 1 = left).

This is using much the same idea as the other answers: we simply find a hash function that happens to map each possible input to the correct output. In order to make it more likely that a randomly selected hash function would produce the correct result (therefore reducing the number of bytes needed to specify which hash function we want), the input list is sorted before hashing it (thus almost halving the number of cases we need to match; a score of X-Y always produces the same answer as a score of Y-X).

There wasn't any skill that went into the selection of the hash function; there was a 2-12 probability that any given hash function would work, which is fairly high, so I simply tried Jelly's built-in hash functions in order from shortest to longest configuration, and picked the first one that worked. This is hash function [19,221]. Because configuring the hash function to output booleans would require a substantially larger configuration string, I just used the default output format (this one outputs numbers in the range 1…221) and took the least significant bit of the output as my output.

Explanation

Ṣ“Œọ‘ḥḂ
Ṣ          Sort {the input list}
     ḥ     Hash {the sorted list}, with configuration
 “Œọ‘        [19, 221] (compressed representation)
      Ḃ    Take the least significant bit of the output
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Bash, 20 bytes

[[ $[$1] = *[14]* ]]

Try it online!

In Bash, any unset variables are 0 in $[arithmetic mode]. A quick test revealed that if the input was evaluated in arithmetic mode, the following pattern emerges:

Result:  0  10 -10  15 -15  25 -25  30 -30  40 -40
  Side:  R   L   L   L   L   R   R   R   R   L   L

Thus, if the arithmetic result contains a 1 or a 4, it is a left serve, otherwise it is a right serve.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Husk, 9 bytes

%2`%25Πmc

Try it online!

Same method as Jonathan Allan's answer.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Batch, 32 bytes

@set/an=(%1)/10%%3^&1
@echo %n%

Takes input on the command line and outputs 1 for serves on the left and 0 for serves on the right. Explanation: Based on @GammaFunction's answer, by performing arithmetic evaluation on the input, and assuming that the variables Love, Ad and Deuce are unset and therefore zero, the tens digit of the result is 1 or 4 for serves on the left and 0, 2 or 3 for serves on the right. This is then further reduced via modulo with 3 and bitwise and with 1 to produce the desired result.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Charcoal, 9 bytes

⁼¹LΦθ№45ι

Try it online! Link is to verbose version of code. Output is a Charcoal boolean, where - (True) means you serve on the left and empty output (False) means you serve on the right. Explanation:

   Φθ       Filter input where
     №45ι   Literal string `45` contains character
  L         Length
⁼¹          Equals literal `1`
            Implicitly print

Longer than Retina, sigh... (I found several formulations for 9 bytes, but none shorter that weren't buggy.)

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

05AB1E, 6 bytes

45S¢O≠

Port of @Neil's Retina answer, so make sure to upvote him!!

Try it online or verify all test cases.

Explanation:

45S     # Push 45, and convert it to a list of digits: [4,5]
   ¢    # Count both of those in the (implicit) input-string
    O   # Take the sum of this pair of counts
     ≠  # And check that it's NOT equal to 1
        # (after which the result is output implicitly)
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

MathGolf, 7 bytes

▒$ε*N%¥

Port of @JonathanAllan's Jelly answer, so make sure to upvote him!!

Try it online.

Explanation:

▒        # Convert the (implicit) input-string to a list of characters
 $       # Convert each character to its codepoint integer
  ε*     # Take the product of this list (reduce by multiplication)
    N%   # Modulo-25
      ¥  # Modulo-2
         # (after which the entire stack joined together is output implicitly as result)
| improve this answer | |
\$\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.